2024-10-23 17:55:55 +08:00
/ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Spine Runtimes License Agreement
2024-11-21 09:35:48 +08:00
* Last updated July 28 , 2023. Replaces all prior versions .
2024-10-23 17:55:55 +08:00
*
2024-11-21 09:35:48 +08:00
* Copyright ( c ) 2013 - 2023 , Esoteric Software LLC
2024-10-23 17:55:55 +08:00
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement :
* http : //esotericsoftware.com/spine-editor-license
*
2024-11-21 09:35:48 +08:00
* Otherwise , it is permitted to integrate the Spine Runtimes into software or
* otherwise create derivative works of the Spine Runtimes ( collectively ,
2024-10-23 17:55:55 +08:00
* "Products" ) , provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice .
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES , INCLUDING , BUT NOT LIMITED TO , THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED . IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT , INDIRECT , INCIDENTAL , SPECIAL , EXEMPLARY , OR CONSEQUENTIAL DAMAGES
* ( INCLUDING , BUT NOT LIMITED TO , PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES ,
* BUSINESS INTERRUPTION , OR LOSS OF USE , DATA , OR PROFITS ) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY , WHETHER IN CONTRACT , STRICT LIABILITY , OR TORT
2024-11-21 09:35:48 +08:00
* ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF THE
* SPINE RUNTIMES , EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE .
2024-10-23 17:55:55 +08:00
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /
2024-11-21 09:35:48 +08:00
#if UNITY_2019_3_OR_NEWER
#define MESH_SET_TRIANGLES_PROVIDES_LENGTH_PARAM
#endif
#if ! UNITY_2020_1_OR_NEWER
// Note: on Unity 2019.4 or older, e.g. operator* was not inlined via AggressiveInlining and at least with some
// configurations will lead to unnecessary overhead.
#define MANUALLY_INLINE_VECTOR_OPERATORS
#endif
// Optimization option: Allows faster BuildMeshWithArrays call and avoids calling SetTriangles at the cost of
// checking for mesh differences (vertex counts, member-wise attachment list compare) every frame.
#define SPINE_TRIANGLECHECK
2024-10-23 17:55:55 +08:00
//#define SPINE_DEBUG
2024-11-21 09:35:48 +08:00
// New optimization option to avoid rendering fully transparent attachments at slot alpha 0.
// Comment out this line to revert to previous behaviour.
// You may only need this option disabled when utilizing a custom shader which
// uses vertex color alpha for purposes other than transparency.
//
// Important Note: When disabling this define, also disable the one in SkeletonRenderInstruction.cs
#define SLOT_ALPHA_DISABLES_ATTACHMENT
// Note: This define below enables a bugfix where when Linear color space is used and `PMA vertex colors` enabled,
// additive slots add a too dark (too transparent) color value.
//
// If you want the old incorrect behaviour (darker additive slots) or are not using Linear but Gamma color space,
// you can comment-out the define below to deactivate the fix or just to skip unnecessary instructions.
//
// Details:
// Alpha-premultiplication of vertex colors happens in gamma-space, and vertexColor.a is set to 0 at additive slots.
// In the shader, gamma space vertex color has to be transformed from gamma space to linear space.
// Unfortunately vertexColorGamma.rgb=(rgb*a) while the desired color in linear space would be
// vertexColorLinear.rgb = GammaToLinear(rgb)*a = GammaToLinear(vertexColorGamma.rgb/a),
// but unfortunately 'a' is unknown as vertexColorGamma.a = 0 at additive slots.
// Thus the define below enables a fix where 'a' is transformed via
// a=LinearToGamma(a), so that the subsequent GammaToLinear() operation is canceled out on 'a'.
#define LINEAR_COLOR_SPACE_FIX_ADDITIVE_ALPHA
2024-10-23 17:55:55 +08:00
using System ;
using System.Collections.Generic ;
2024-11-21 09:35:48 +08:00
using UnityEngine ;
2024-10-23 17:55:55 +08:00
namespace Spine.Unity {
public delegate void MeshGeneratorDelegate ( MeshGeneratorBuffers buffers ) ;
public struct MeshGeneratorBuffers {
/// <summary>The vertex count that will actually be used for the mesh. The Lengths of the buffer arrays may be larger than this number.</summary>
public int vertexCount ;
/// <summary> Vertex positions. To be used for UnityEngine.Mesh.vertices.</summary>
public Vector3 [ ] vertexBuffer ;
2024-11-21 09:35:48 +08:00
/// <summary> Vertex texture coordinates (UVs). To be used for UnityEngine.Mesh.uv.</summary>
2024-10-23 17:55:55 +08:00
public Vector2 [ ] uvBuffer ;
/// <summary> Vertex colors. To be used for UnityEngine.Mesh.colors32.</summary>
public Color32 [ ] colorBuffer ;
2024-11-21 09:35:48 +08:00
/// <summary> Optional vertex texture coordinates (UVs), second channel. To be used for UnityEngine.Mesh.uv2.
/// Using this accessor automatically allocates and resizes the buffer accordingly.</summary>
public Vector2 [ ] uv2Buffer { get { return meshGenerator . UV2 ; } }
/// <summary> Optional vertex texture coordinates (UVs), third channel. To be used for UnityEngine.Mesh.uv3.
/// Using this accessor automatically allocates and resizes the buffer accordingly.</summary>
public Vector2 [ ] uv3Buffer { get { return meshGenerator . UV3 ; } }
2024-10-23 17:55:55 +08:00
/// <summary> The Spine rendering component's MeshGenerator. </summary>
public MeshGenerator meshGenerator ;
}
/// <summary>Holds several methods to prepare and generate a UnityEngine mesh based on a skeleton. Contains buffers needed to perform the operation, and serializes settings for mesh generation.</summary>
[System.Serializable]
public class MeshGenerator {
public Settings settings = Settings . Default ;
[System.Serializable]
public struct Settings {
public bool useClipping ;
[Range(-0.1f, 0f)] public float zSpacing ;
public bool tintBlack ;
2024-11-21 09:35:48 +08:00
[UnityEngine.Serialization.FormerlySerializedAs("canvasGroupTintBlack")]
[ Tooltip ( "Enable when using SkeletonGraphic under a CanvasGroup. " +
"When enabled, PMA Vertex Color alpha value is stored at uv2.g instead of color.a to capture " +
"CanvasGroup modifying color.a. Also helps to detect correct parameter setting combinations." ) ]
public bool canvasGroupCompatible ;
public bool pmaVertexColors ;
2024-10-23 17:55:55 +08:00
public bool addNormals ;
2024-11-21 09:35:48 +08:00
public bool calculateTangents ;
2024-10-23 17:55:55 +08:00
public bool immutableTriangles ;
static public Settings Default {
get {
return new Settings {
pmaVertexColors = true ,
zSpacing = 0f ,
useClipping = true ,
tintBlack = false ,
calculateTangents = false ,
//renderMeshes = true,
addNormals = false ,
immutableTriangles = false
} ;
}
}
}
const float BoundsMinDefault = float . PositiveInfinity ;
const float BoundsMaxDefault = float . NegativeInfinity ;
2024-11-21 09:35:48 +08:00
[NonSerialized] protected readonly ExposedList < Vector3 > vertexBuffer = new ExposedList < Vector3 > ( 4 ) ;
[NonSerialized] protected readonly ExposedList < Vector2 > uvBuffer = new ExposedList < Vector2 > ( 4 ) ;
[NonSerialized] protected readonly ExposedList < Color32 > colorBuffer = new ExposedList < Color32 > ( 4 ) ;
[NonSerialized] protected readonly ExposedList < ExposedList < int > > submeshes = new ExposedList < ExposedList < int > > { new ExposedList < int > ( 6 ) } ; // start with 1 submesh.
2024-10-23 17:55:55 +08:00
[NonSerialized] Vector2 meshBoundsMin , meshBoundsMax ;
[NonSerialized] float meshBoundsThickness ;
[NonSerialized] int submeshIndex = 0 ;
[NonSerialized] SkeletonClipping clipper = new SkeletonClipping ( ) ;
[NonSerialized] float [ ] tempVerts = new float [ 8 ] ;
[NonSerialized] int [ ] regionTriangles = { 0 , 1 , 2 , 2 , 3 , 0 } ;
#region Optional Buffers
// These optional buffers are lazy-instantiated when the feature is used.
[NonSerialized] Vector3 [ ] normals ;
[NonSerialized] Vector4 [ ] tangents ;
[NonSerialized] Vector2 [ ] tempTanBuffer ;
[NonSerialized] ExposedList < Vector2 > uv2 ;
[NonSerialized] ExposedList < Vector2 > uv3 ;
2024-11-21 09:35:48 +08:00
/// <summary> Optional vertex texture coordinates (UVs), second channel. To be used for UnityEngine.Mesh.uv2.
/// Using this accessor automatically allocates and resizes the buffer accordingly.</summary>
public Vector2 [ ] UV2 { get { PrepareOptionalUVBuffer ( ref uv2 , vertexBuffer . Count ) ; return uv2 . Items ; } }
/// <summary> Optional vertex texture coordinates (UVs), third channel. To be used for UnityEngine.Mesh.uv3.
/// Using this accessor automatically allocates and resizes the buffer accordingly.</summary>
public Vector2 [ ] UV3 { get { PrepareOptionalUVBuffer ( ref uv3 , vertexBuffer . Count ) ; return uv3 . Items ; } }
2024-10-23 17:55:55 +08:00
#endregion
public int VertexCount { get { return vertexBuffer . Count ; } }
2024-11-21 09:35:48 +08:00
public int SubmeshIndexCount ( int submeshIndex ) { return submeshes . Items [ submeshIndex ] . Count ; }
2024-10-23 17:55:55 +08:00
/// <summary>A set of mesh arrays whose values are modifiable by the user. Modify these values before they are passed to the UnityEngine mesh object in order to see the effect.</summary>
public MeshGeneratorBuffers Buffers {
get {
return new MeshGeneratorBuffers {
vertexCount = this . VertexCount ,
vertexBuffer = this . vertexBuffer . Items ,
uvBuffer = this . uvBuffer . Items ,
colorBuffer = this . colorBuffer . Items ,
meshGenerator = this
} ;
}
}
2024-11-21 09:35:48 +08:00
/// <summary>Returns the <see cref="SkeletonClipping"/> used by this mesh generator for use with e.g.
/// <see cref="Skeleton.GetBounds(out float, out float, out float, out float, ref float[], SkeletonClipping)"/>
/// </summary>
public SkeletonClipping SkeletonClipping { get { return clipper ; } }
2024-10-23 17:55:55 +08:00
public MeshGenerator ( ) {
submeshes . TrimExcess ( ) ;
}
#region Step 1 : Generate Instructions
/// <summary>
/// A specialized variant of <see cref="GenerateSkeletonRendererInstruction"/>.
/// Generates renderer instructions using a single submesh, using only a single material and texture.
/// </summary>
/// <param name="instructionOutput">The resulting instructions.</param>
/// <param name="skeleton">The skeleton to generate renderer instructions for.</param>
/// <param name="material">Material to be set at the renderer instruction. When null, the last attachment
/// in the draw order list is assigned as the instruction's material.</param>
public static void GenerateSingleSubmeshInstruction ( SkeletonRendererInstruction instructionOutput , Skeleton skeleton , Material material ) {
2024-11-21 09:35:48 +08:00
ExposedList < Slot > drawOrder = skeleton . DrawOrder ;
2024-10-23 17:55:55 +08:00
int drawOrderCount = drawOrder . Count ;
// Clear last state of attachments and submeshes
instructionOutput . Clear ( ) ; // submeshInstructions.Clear(); attachments.Clear();
2024-11-21 09:35:48 +08:00
ExposedList < SubmeshInstruction > workingSubmeshInstructions = instructionOutput . submeshInstructions ;
2024-10-23 17:55:55 +08:00
2024-11-21 09:35:48 +08:00
#if SPINE_TRIANGLECHECK
2024-10-23 17:55:55 +08:00
instructionOutput . attachments . Resize ( drawOrderCount ) ;
2024-11-21 09:35:48 +08:00
Attachment [ ] workingAttachmentsItems = instructionOutput . attachments . Items ;
2024-10-23 17:55:55 +08:00
int totalRawVertexCount = 0 ;
2024-11-21 09:35:48 +08:00
#endif
2024-10-23 17:55:55 +08:00
2024-11-21 09:35:48 +08:00
SubmeshInstruction current = new SubmeshInstruction {
2024-10-23 17:55:55 +08:00
skeleton = skeleton ,
preActiveClippingSlotSource = - 1 ,
startSlot = 0 ,
2024-11-21 09:35:48 +08:00
#if SPINE_TRIANGLECHECK
2024-10-23 17:55:55 +08:00
rawFirstVertexIndex = 0 ,
2024-11-21 09:35:48 +08:00
#endif
2024-10-23 17:55:55 +08:00
material = material ,
forceSeparate = false ,
endSlot = drawOrderCount
} ;
2024-11-21 09:35:48 +08:00
#if SPINE_TRIANGLECHECK
2024-10-23 17:55:55 +08:00
object rendererObject = null ;
bool skeletonHasClipping = false ;
2024-11-21 09:35:48 +08:00
Slot [ ] drawOrderItems = drawOrder . Items ;
2024-10-23 17:55:55 +08:00
for ( int i = 0 ; i < drawOrderCount ; i + + ) {
Slot slot = drawOrderItems [ i ] ;
2024-11-21 09:35:48 +08:00
if ( ! slot . Bone . Active
#if SLOT_ALPHA_DISABLES_ATTACHMENT
| | slot . A = = 0f
#endif
) {
workingAttachmentsItems [ i ] = null ;
continue ;
}
if ( slot . Data . BlendMode = = BlendMode . Additive ) current . hasPMAAdditiveSlot = true ;
Attachment attachment = slot . Attachment ;
2024-10-23 17:55:55 +08:00
workingAttachmentsItems [ i ] = attachment ;
int attachmentTriangleCount ;
int attachmentVertexCount ;
2024-11-21 09:35:48 +08:00
RegionAttachment regionAttachment = attachment as RegionAttachment ;
2024-10-23 17:55:55 +08:00
if ( regionAttachment ! = null ) {
2024-11-21 09:35:48 +08:00
if ( regionAttachment . Sequence ! = null ) regionAttachment . Sequence . Apply ( slot , regionAttachment ) ;
rendererObject = regionAttachment . Region ;
2024-10-23 17:55:55 +08:00
attachmentVertexCount = 4 ;
attachmentTriangleCount = 6 ;
} else {
2024-11-21 09:35:48 +08:00
MeshAttachment meshAttachment = attachment as MeshAttachment ;
2024-10-23 17:55:55 +08:00
if ( meshAttachment ! = null ) {
2024-11-21 09:35:48 +08:00
if ( meshAttachment . Sequence ! = null ) meshAttachment . Sequence . Apply ( slot , meshAttachment ) ;
rendererObject = meshAttachment . Region ;
attachmentVertexCount = meshAttachment . WorldVerticesLength > > 1 ;
attachmentTriangleCount = meshAttachment . Triangles . Length ;
2024-10-23 17:55:55 +08:00
} else {
2024-11-21 09:35:48 +08:00
ClippingAttachment clippingAttachment = attachment as ClippingAttachment ;
2024-10-23 17:55:55 +08:00
if ( clippingAttachment ! = null ) {
current . hasClipping = true ;
skeletonHasClipping = true ;
}
attachmentVertexCount = 0 ;
attachmentTriangleCount = 0 ;
}
}
current . rawTriangleCount + = attachmentTriangleCount ;
current . rawVertexCount + = attachmentVertexCount ;
totalRawVertexCount + = attachmentVertexCount ;
}
2024-11-21 09:35:48 +08:00
#if ! SPINE_TK2D
2024-10-23 17:55:55 +08:00
if ( material = = null & & rendererObject ! = null )
current . material = ( Material ) ( ( AtlasRegion ) rendererObject ) . page . rendererObject ;
2024-11-21 09:35:48 +08:00
#else
2024-10-23 17:55:55 +08:00
if ( material = = null & & rendererObject ! = null )
current . material = ( rendererObject is Material ) ? ( Material ) rendererObject : ( Material ) ( ( AtlasRegion ) rendererObject ) . page . rendererObject ;
2024-11-21 09:35:48 +08:00
#endif
2024-10-23 17:55:55 +08:00
instructionOutput . hasActiveClipping = skeletonHasClipping ;
instructionOutput . rawVertexCount = totalRawVertexCount ;
2024-11-21 09:35:48 +08:00
#endif
#if SPINE_TRIANGLECHECK
bool hasAnyVertices = totalRawVertexCount > 0 ;
#else
bool hasAnyVertices = true ;
#endif
if ( hasAnyVertices ) {
2024-10-23 17:55:55 +08:00
workingSubmeshInstructions . Resize ( 1 ) ;
workingSubmeshInstructions . Items [ 0 ] = current ;
2024-11-21 09:35:48 +08:00
} else {
2024-10-23 17:55:55 +08:00
workingSubmeshInstructions . Resize ( 0 ) ;
}
}
public static bool RequiresMultipleSubmeshesByDrawOrder ( Skeleton skeleton ) {
2024-11-21 09:35:48 +08:00
#if SPINE_TK2D
2024-10-23 17:55:55 +08:00
return false ;
2024-11-21 09:35:48 +08:00
#endif
ExposedList < Slot > drawOrder = skeleton . DrawOrder ;
2024-10-23 17:55:55 +08:00
int drawOrderCount = drawOrder . Count ;
2024-11-21 09:35:48 +08:00
Slot [ ] drawOrderItems = drawOrder . Items ;
2024-10-23 17:55:55 +08:00
Material lastRendererMaterial = null ;
for ( int i = 0 ; i < drawOrderCount ; i + + ) {
Slot slot = drawOrderItems [ i ] ;
2024-11-21 09:35:48 +08:00
if ( ! slot . Bone . Active
#if SLOT_ALPHA_DISABLES_ATTACHMENT
| | slot . A = = 0f
#endif
) continue ;
Attachment attachment = slot . Attachment ;
IHasTextureRegion rendererAttachment = attachment as IHasTextureRegion ;
2024-10-23 17:55:55 +08:00
if ( rendererAttachment ! = null ) {
2024-11-21 09:35:48 +08:00
if ( rendererAttachment . Sequence ! = null ) rendererAttachment . Sequence . Apply ( slot , rendererAttachment ) ;
AtlasRegion atlasRegion = ( AtlasRegion ) rendererAttachment . Region ;
2024-10-23 17:55:55 +08:00
Material material = ( Material ) atlasRegion . page . rendererObject ;
if ( lastRendererMaterial ! = material ) {
if ( lastRendererMaterial ! = null )
return true ;
2024-11-21 09:35:48 +08:00
lastRendererMaterial = material ;
2024-10-23 17:55:55 +08:00
}
}
}
return false ;
}
public static void GenerateSkeletonRendererInstruction ( SkeletonRendererInstruction instructionOutput , Skeleton skeleton , Dictionary < Slot , Material > customSlotMaterials , List < Slot > separatorSlots , bool generateMeshOverride , bool immutableTriangles = false ) {
// if (skeleton == null) throw new ArgumentNullException("skeleton");
// if (instructionOutput == null) throw new ArgumentNullException("instructionOutput");
2024-11-21 09:35:48 +08:00
ExposedList < Slot > drawOrder = skeleton . DrawOrder ;
2024-10-23 17:55:55 +08:00
int drawOrderCount = drawOrder . Count ;
// Clear last state of attachments and submeshes
instructionOutput . Clear ( ) ; // submeshInstructions.Clear(); attachments.Clear();
2024-11-21 09:35:48 +08:00
ExposedList < SubmeshInstruction > workingSubmeshInstructions = instructionOutput . submeshInstructions ;
#if SPINE_TRIANGLECHECK
2024-10-23 17:55:55 +08:00
instructionOutput . attachments . Resize ( drawOrderCount ) ;
2024-11-21 09:35:48 +08:00
Attachment [ ] workingAttachmentsItems = instructionOutput . attachments . Items ;
2024-10-23 17:55:55 +08:00
int totalRawVertexCount = 0 ;
bool skeletonHasClipping = false ;
2024-11-21 09:35:48 +08:00
#endif
2024-10-23 17:55:55 +08:00
2024-11-21 09:35:48 +08:00
SubmeshInstruction current = new SubmeshInstruction {
2024-10-23 17:55:55 +08:00
skeleton = skeleton ,
preActiveClippingSlotSource = - 1
} ;
2024-11-21 09:35:48 +08:00
#if ! SPINE_TK2D
2024-10-23 17:55:55 +08:00
bool isCustomSlotMaterialsPopulated = customSlotMaterials ! = null & & customSlotMaterials . Count > 0 ;
2024-11-21 09:35:48 +08:00
#endif
2024-10-23 17:55:55 +08:00
int separatorCount = separatorSlots = = null ? 0 : separatorSlots . Count ;
bool hasSeparators = separatorCount > 0 ;
int clippingAttachmentSource = - 1 ;
int lastPreActiveClipping = - 1 ; // The index of the last slot that had an active ClippingAttachment.
SlotData clippingEndSlot = null ;
int submeshIndex = 0 ;
2024-11-21 09:35:48 +08:00
Slot [ ] drawOrderItems = drawOrder . Items ;
2024-10-23 17:55:55 +08:00
for ( int i = 0 ; i < drawOrderCount ; i + + ) {
Slot slot = drawOrderItems [ i ] ;
2024-11-21 09:35:48 +08:00
if ( ! slot . Bone . Active
#if SLOT_ALPHA_DISABLES_ATTACHMENT
| | ( slot . A = = 0f & & slot . Data ! = clippingEndSlot )
#endif
) {
#if SPINE_TRIANGLECHECK
workingAttachmentsItems [ i ] = null ;
#endif
continue ;
}
if ( slot . Data . BlendMode = = BlendMode . Additive ) current . hasPMAAdditiveSlot = true ;
Attachment attachment = slot . Attachment ;
#if SPINE_TRIANGLECHECK
2024-10-23 17:55:55 +08:00
workingAttachmentsItems [ i ] = attachment ;
int attachmentVertexCount = 0 , attachmentTriangleCount = 0 ;
2024-11-21 09:35:48 +08:00
#endif
2024-10-23 17:55:55 +08:00
2024-11-21 09:35:48 +08:00
object region = null ;
2024-10-23 17:55:55 +08:00
bool noRender = false ; // Using this allows empty slots as separators, and keeps separated parts more stable despite slots being reordered
2024-11-21 09:35:48 +08:00
RegionAttachment regionAttachment = attachment as RegionAttachment ;
2024-10-23 17:55:55 +08:00
if ( regionAttachment ! = null ) {
2024-11-21 09:35:48 +08:00
if ( regionAttachment . Sequence ! = null ) regionAttachment . Sequence . Apply ( slot , regionAttachment ) ;
region = regionAttachment . Region ;
#if SPINE_TRIANGLECHECK
2024-10-23 17:55:55 +08:00
attachmentVertexCount = 4 ;
attachmentTriangleCount = 6 ;
2024-11-21 09:35:48 +08:00
#endif
2024-10-23 17:55:55 +08:00
} else {
2024-11-21 09:35:48 +08:00
MeshAttachment meshAttachment = attachment as MeshAttachment ;
2024-10-23 17:55:55 +08:00
if ( meshAttachment ! = null ) {
2024-11-21 09:35:48 +08:00
if ( meshAttachment . Sequence ! = null ) meshAttachment . Sequence . Apply ( slot , meshAttachment ) ;
region = meshAttachment . Region ;
#if SPINE_TRIANGLECHECK
attachmentVertexCount = meshAttachment . WorldVerticesLength > > 1 ;
attachmentTriangleCount = meshAttachment . Triangles . Length ;
#endif
2024-10-23 17:55:55 +08:00
} else {
2024-11-21 09:35:48 +08:00
#if SPINE_TRIANGLECHECK
ClippingAttachment clippingAttachment = attachment as ClippingAttachment ;
2024-10-23 17:55:55 +08:00
if ( clippingAttachment ! = null ) {
2024-11-21 09:35:48 +08:00
clippingEndSlot = clippingAttachment . EndSlot ;
2024-10-23 17:55:55 +08:00
clippingAttachmentSource = i ;
current . hasClipping = true ;
skeletonHasClipping = true ;
}
2024-11-21 09:35:48 +08:00
#endif
2024-10-23 17:55:55 +08:00
noRender = true ;
}
}
// Create a new SubmeshInstruction when material changes. (or when forced to separate by a submeshSeparator)
// Slot with a separator/new material will become the starting slot of the next new instruction.
if ( hasSeparators ) { //current.forceSeparate = hasSeparators && separatorSlots.Contains(slot);
current . forceSeparate = false ;
for ( int s = 0 ; s < separatorCount ; s + + ) {
if ( Slot . ReferenceEquals ( slot , separatorSlots [ s ] ) ) {
current . forceSeparate = true ;
break ;
}
}
}
if ( noRender ) {
if ( current . forceSeparate & & generateMeshOverride ) { // && current.rawVertexCount > 0) {
{ // Add
current . endSlot = i ;
current . preActiveClippingSlotSource = lastPreActiveClipping ;
workingSubmeshInstructions . Resize ( submeshIndex + 1 ) ;
workingSubmeshInstructions . Items [ submeshIndex ] = current ;
submeshIndex + + ;
}
current . startSlot = i ;
lastPreActiveClipping = clippingAttachmentSource ;
2024-11-21 09:35:48 +08:00
#if SPINE_TRIANGLECHECK
2024-10-23 17:55:55 +08:00
current . rawTriangleCount = 0 ;
current . rawVertexCount = 0 ;
current . rawFirstVertexIndex = totalRawVertexCount ;
current . hasClipping = clippingAttachmentSource > = 0 ;
2024-11-21 09:35:48 +08:00
#endif
2024-10-23 17:55:55 +08:00
}
} else {
2024-11-21 09:35:48 +08:00
#if ! SPINE_TK2D
2024-10-23 17:55:55 +08:00
Material material ;
if ( isCustomSlotMaterialsPopulated ) {
if ( ! customSlotMaterials . TryGetValue ( slot , out material ) )
2024-11-21 09:35:48 +08:00
material = ( Material ) ( ( AtlasRegion ) region ) . page . rendererObject ;
2024-10-23 17:55:55 +08:00
} else {
2024-11-21 09:35:48 +08:00
material = ( Material ) ( ( AtlasRegion ) region ) . page . rendererObject ;
2024-10-23 17:55:55 +08:00
}
2024-11-21 09:35:48 +08:00
#else
// An AtlasRegion in plain spine-unity, spine-TK2D hooks into TK2D's system. eventual source of Material object.
Material material = ( region is Material ) ? ( Material ) region : ( Material ) ( ( AtlasRegion ) region ) . page . rendererObject ;
#endif
#if ! SPINE_TRIANGLECHECK
if ( current . forceSeparate | | ! System . Object . ReferenceEquals ( current . material , material ) ) { // Material changed. Add the previous submesh.
#else
2024-10-23 17:55:55 +08:00
if ( current . forceSeparate | | ( current . rawVertexCount > 0 & & ! System . Object . ReferenceEquals ( current . material , material ) ) ) { // Material changed. Add the previous submesh.
2024-11-21 09:35:48 +08:00
#endif
2024-10-23 17:55:55 +08:00
{ // Add
current . endSlot = i ;
current . preActiveClippingSlotSource = lastPreActiveClipping ;
workingSubmeshInstructions . Resize ( submeshIndex + 1 ) ;
workingSubmeshInstructions . Items [ submeshIndex ] = current ;
submeshIndex + + ;
}
current . startSlot = i ;
lastPreActiveClipping = clippingAttachmentSource ;
2024-11-21 09:35:48 +08:00
#if SPINE_TRIANGLECHECK
2024-10-23 17:55:55 +08:00
current . rawTriangleCount = 0 ;
current . rawVertexCount = 0 ;
current . rawFirstVertexIndex = totalRawVertexCount ;
current . hasClipping = clippingAttachmentSource > = 0 ;
2024-11-21 09:35:48 +08:00
#endif
2024-10-23 17:55:55 +08:00
}
// Update state for the next Attachment.
current . material = material ;
2024-11-21 09:35:48 +08:00
#if SPINE_TRIANGLECHECK
2024-10-23 17:55:55 +08:00
current . rawTriangleCount + = attachmentTriangleCount ;
current . rawVertexCount + = attachmentVertexCount ;
current . rawFirstVertexIndex = totalRawVertexCount ;
totalRawVertexCount + = attachmentVertexCount ;
2024-11-21 09:35:48 +08:00
#endif
2024-10-23 17:55:55 +08:00
}
2024-11-21 09:35:48 +08:00
if ( clippingEndSlot ! = null & & slot . Data = = clippingEndSlot & & i ! = clippingAttachmentSource ) {
2024-10-23 17:55:55 +08:00
clippingEndSlot = null ;
clippingAttachmentSource = - 1 ;
}
}
if ( current . rawVertexCount > 0 ) {
{ // Add last or only submesh.
current . endSlot = drawOrderCount ;
current . preActiveClippingSlotSource = lastPreActiveClipping ;
current . forceSeparate = false ;
workingSubmeshInstructions . Resize ( submeshIndex + 1 ) ;
workingSubmeshInstructions . Items [ submeshIndex ] = current ;
//submeshIndex++;
}
}
2024-11-21 09:35:48 +08:00
#if SPINE_TRIANGLECHECK
2024-10-23 17:55:55 +08:00
instructionOutput . hasActiveClipping = skeletonHasClipping ;
instructionOutput . rawVertexCount = totalRawVertexCount ;
2024-11-21 09:35:48 +08:00
#endif
2024-10-23 17:55:55 +08:00
instructionOutput . immutableTriangles = immutableTriangles ;
}
public static void TryReplaceMaterials ( ExposedList < SubmeshInstruction > workingSubmeshInstructions , Dictionary < Material , Material > customMaterialOverride ) {
// Material overrides are done here so they can be applied per submesh instead of per slot
// but they will still be passed through the GenerateMeshOverride delegate,
// and will still go through the normal material match check step in STEP 3.
2024-11-21 09:35:48 +08:00
SubmeshInstruction [ ] wsii = workingSubmeshInstructions . Items ;
2024-10-23 17:55:55 +08:00
for ( int i = 0 ; i < workingSubmeshInstructions . Count ; i + + ) {
2024-11-21 09:35:48 +08:00
Material material = wsii [ i ] . material ;
if ( material = = null ) continue ;
Material overrideMaterial ;
if ( customMaterialOverride . TryGetValue ( material , out overrideMaterial ) )
wsii [ i ] . material = overrideMaterial ;
2024-10-23 17:55:55 +08:00
}
}
#endregion
#region Step 2 : Populate vertex data and triangle index buffers .
public void Begin ( ) {
vertexBuffer . Clear ( false ) ;
colorBuffer . Clear ( false ) ;
uvBuffer . Clear ( false ) ;
clipper . ClipEnd ( ) ;
{
meshBoundsMin . x = BoundsMinDefault ;
meshBoundsMin . y = BoundsMinDefault ;
meshBoundsMax . x = BoundsMaxDefault ;
meshBoundsMax . y = BoundsMaxDefault ;
meshBoundsThickness = 0f ;
}
submeshIndex = 0 ;
submeshes . Count = 1 ;
//submeshes.Items[0].Clear(false);
}
public void AddSubmesh ( SubmeshInstruction instruction , bool updateTriangles = true ) {
2024-11-21 09:35:48 +08:00
Settings settings = this . settings ;
2024-10-23 17:55:55 +08:00
int newSubmeshCount = submeshIndex + 1 ;
if ( submeshes . Items . Length < newSubmeshCount )
submeshes . Resize ( newSubmeshCount ) ;
submeshes . Count = newSubmeshCount ;
2024-11-21 09:35:48 +08:00
ExposedList < int > submesh = submeshes . Items [ submeshIndex ] ;
2024-10-23 17:55:55 +08:00
if ( submesh = = null )
submeshes . Items [ submeshIndex ] = submesh = new ExposedList < int > ( ) ;
submesh . Clear ( false ) ;
2024-11-21 09:35:48 +08:00
Skeleton skeleton = instruction . skeleton ;
Slot [ ] drawOrderItems = skeleton . DrawOrder . Items ;
2024-10-23 17:55:55 +08:00
Color32 color = default ( Color32 ) ;
2024-11-21 09:35:48 +08:00
float skeletonA = skeleton . A , skeletonR = skeleton . R , skeletonG = skeleton . G , skeletonB = skeleton . B ;
2024-10-23 17:55:55 +08:00
Vector2 meshBoundsMin = this . meshBoundsMin , meshBoundsMax = this . meshBoundsMax ;
// Settings
float zSpacing = settings . zSpacing ;
bool pmaVertexColors = settings . pmaVertexColors ;
bool tintBlack = settings . tintBlack ;
2024-11-21 09:35:48 +08:00
#if LINEAR_COLOR_SPACE_FIX_ADDITIVE_ALPHA
bool linearColorSpace = QualitySettings . activeColorSpace = = ColorSpace . Linear ;
#endif
#if SPINE_TRIANGLECHECK
2024-10-23 17:55:55 +08:00
bool useClipping = settings . useClipping & & instruction . hasClipping ;
2024-11-21 09:35:48 +08:00
#else
2024-10-23 17:55:55 +08:00
bool useClipping = settings . useClipping ;
2024-11-21 09:35:48 +08:00
#endif
bool canvasGroupTintBlack = settings . tintBlack & & settings . canvasGroupCompatible ;
2024-10-23 17:55:55 +08:00
if ( useClipping ) {
if ( instruction . preActiveClippingSlotSource > = 0 ) {
2024-11-21 09:35:48 +08:00
Slot slot = drawOrderItems [ instruction . preActiveClippingSlotSource ] ;
clipper . ClipStart ( slot , slot . Attachment as ClippingAttachment ) ;
2024-10-23 17:55:55 +08:00
}
}
for ( int slotIndex = instruction . startSlot ; slotIndex < instruction . endSlot ; slotIndex + + ) {
2024-11-21 09:35:48 +08:00
Slot slot = drawOrderItems [ slotIndex ] ;
if ( ! slot . Bone . Active ) {
2024-10-23 17:55:55 +08:00
clipper . ClipEnd ( slot ) ;
continue ;
}
2024-11-21 09:35:48 +08:00
Attachment attachment = slot . Attachment ;
2024-10-23 17:55:55 +08:00
float z = zSpacing * slotIndex ;
2024-11-21 09:35:48 +08:00
float [ ] workingVerts = this . tempVerts ;
2024-10-23 17:55:55 +08:00
float [ ] uvs ;
int [ ] attachmentTriangleIndices ;
int attachmentVertexCount ;
int attachmentIndexCount ;
Color c = default ( Color ) ;
// Identify and prepare values.
2024-11-21 09:35:48 +08:00
RegionAttachment region = attachment as RegionAttachment ;
2024-10-23 17:55:55 +08:00
if ( region ! = null ) {
2024-11-21 09:35:48 +08:00
region . ComputeWorldVertices ( slot , workingVerts , 0 ) ;
uvs = region . UVs ;
2024-10-23 17:55:55 +08:00
attachmentTriangleIndices = regionTriangles ;
2024-11-21 09:35:48 +08:00
c . r = region . R ; c . g = region . G ; c . b = region . B ; c . a = region . A ;
2024-10-23 17:55:55 +08:00
attachmentVertexCount = 4 ;
attachmentIndexCount = 6 ;
} else {
2024-11-21 09:35:48 +08:00
MeshAttachment mesh = attachment as MeshAttachment ;
2024-10-23 17:55:55 +08:00
if ( mesh ! = null ) {
2024-11-21 09:35:48 +08:00
int meshVerticesLength = mesh . WorldVerticesLength ;
2024-10-23 17:55:55 +08:00
if ( workingVerts . Length < meshVerticesLength ) {
workingVerts = new float [ meshVerticesLength ] ;
this . tempVerts = workingVerts ;
}
mesh . ComputeWorldVertices ( slot , 0 , meshVerticesLength , workingVerts , 0 ) ; //meshAttachment.ComputeWorldVertices(slot, tempVerts);
2024-11-21 09:35:48 +08:00
uvs = mesh . UVs ;
attachmentTriangleIndices = mesh . Triangles ;
c . r = mesh . R ; c . g = mesh . G ; c . b = mesh . B ; c . a = mesh . A ;
2024-10-23 17:55:55 +08:00
attachmentVertexCount = meshVerticesLength > > 1 ; // meshVertexCount / 2;
2024-11-21 09:35:48 +08:00
attachmentIndexCount = mesh . Triangles . Length ;
2024-10-23 17:55:55 +08:00
} else {
if ( useClipping ) {
2024-11-21 09:35:48 +08:00
ClippingAttachment clippingAttachment = attachment as ClippingAttachment ;
2024-10-23 17:55:55 +08:00
if ( clippingAttachment ! = null ) {
clipper . ClipStart ( slot , clippingAttachment ) ;
continue ;
}
}
// If not any renderable attachment.
clipper . ClipEnd ( slot ) ;
continue ;
}
}
float tintBlackAlpha = 1.0f ;
if ( pmaVertexColors ) {
2024-11-21 09:35:48 +08:00
float alpha = skeletonA * slot . A * c . a ;
bool isAdditiveSlot = slot . Data . BlendMode = = BlendMode . Additive ;
#if LINEAR_COLOR_SPACE_FIX_ADDITIVE_ALPHA
if ( linearColorSpace & & isAdditiveSlot )
alpha = Mathf . LinearToGammaSpace ( alpha ) ; // compensate GammaToLinear performed in shader
#endif
color . a = ( byte ) ( alpha * 255 ) ;
color . r = ( byte ) ( skeletonR * slot . R * c . r * color . a ) ;
color . g = ( byte ) ( skeletonG * slot . G * c . g * color . a ) ;
color . b = ( byte ) ( skeletonB * slot . B * c . b * color . a ) ;
if ( canvasGroupTintBlack ) {
tintBlackAlpha = isAdditiveSlot ? 0 : alpha ;
color . a = 255 ;
} else {
if ( isAdditiveSlot )
2024-10-23 17:55:55 +08:00
color . a = 0 ;
}
} else {
2024-11-21 09:35:48 +08:00
color . a = ( byte ) ( skeletonA * slot . A * c . a * 255 ) ;
color . r = ( byte ) ( skeletonR * slot . R * c . r * 255 ) ;
color . g = ( byte ) ( skeletonG * slot . G * c . g * 255 ) ;
color . b = ( byte ) ( skeletonB * slot . B * c . b * 255 ) ;
2024-10-23 17:55:55 +08:00
}
if ( useClipping & & clipper . IsClipping ) {
2024-11-21 09:35:48 +08:00
clipper . ClipTriangles ( workingVerts , attachmentTriangleIndices , attachmentIndexCount , uvs ) ;
workingVerts = clipper . ClippedVertices . Items ;
attachmentVertexCount = clipper . ClippedVertices . Count > > 1 ;
attachmentTriangleIndices = clipper . ClippedTriangles . Items ;
attachmentIndexCount = clipper . ClippedTriangles . Count ;
uvs = clipper . ClippedUVs . Items ;
2024-10-23 17:55:55 +08:00
}
// Actually add slot/attachment data into buffers.
if ( attachmentVertexCount ! = 0 & & attachmentIndexCount ! = 0 ) {
if ( tintBlack ) {
2024-11-21 09:35:48 +08:00
float r2 = slot . R2 ;
float g2 = slot . G2 ;
float b2 = slot . B2 ;
2024-10-23 17:55:55 +08:00
if ( pmaVertexColors ) {
2024-11-21 09:35:48 +08:00
float alpha = skeletonA * slot . A * c . a ;
#if LINEAR_COLOR_SPACE_FIX_ADDITIVE_ALPHA
bool isAdditiveSlot = slot . Data . BlendMode = = BlendMode . Additive ;
if ( linearColorSpace & & isAdditiveSlot )
alpha = Mathf . LinearToGammaSpace ( alpha ) ; // compensate GammaToLinear performed in shader
#endif
2024-10-23 17:55:55 +08:00
r2 * = alpha ;
g2 * = alpha ;
b2 * = alpha ;
}
AddAttachmentTintBlack ( r2 , g2 , b2 , tintBlackAlpha , attachmentVertexCount ) ;
}
//AddAttachment(workingVerts, uvs, color, attachmentTriangleIndices, attachmentVertexCount, attachmentIndexCount, ref meshBoundsMin, ref meshBoundsMax, z);
int ovc = vertexBuffer . Count ;
// Add data to vertex buffers
{
int newVertexCount = ovc + attachmentVertexCount ;
int oldArraySize = vertexBuffer . Items . Length ;
if ( newVertexCount > oldArraySize ) {
int newArraySize = ( int ) ( oldArraySize * 1.3f ) ;
if ( newArraySize < newVertexCount ) newArraySize = newVertexCount ;
Array . Resize ( ref vertexBuffer . Items , newArraySize ) ;
Array . Resize ( ref uvBuffer . Items , newArraySize ) ;
Array . Resize ( ref colorBuffer . Items , newArraySize ) ;
}
vertexBuffer . Count = uvBuffer . Count = colorBuffer . Count = newVertexCount ;
}
2024-11-21 09:35:48 +08:00
Vector3 [ ] vbi = vertexBuffer . Items ;
Vector2 [ ] ubi = uvBuffer . Items ;
Color32 [ ] cbi = colorBuffer . Items ;
2024-10-23 17:55:55 +08:00
if ( ovc = = 0 ) {
for ( int i = 0 ; i < attachmentVertexCount ; i + + ) {
int vi = ovc + i ;
int i2 = i < < 1 ; // i * 2
float x = workingVerts [ i2 ] ;
float y = workingVerts [ i2 + 1 ] ;
vbi [ vi ] . x = x ;
vbi [ vi ] . y = y ;
vbi [ vi ] . z = z ;
ubi [ vi ] . x = uvs [ i2 ] ;
ubi [ vi ] . y = uvs [ i2 + 1 ] ;
cbi [ vi ] = color ;
// Calculate bounds.
if ( x < meshBoundsMin . x ) meshBoundsMin . x = x ;
if ( x > meshBoundsMax . x ) meshBoundsMax . x = x ;
if ( y < meshBoundsMin . y ) meshBoundsMin . y = y ;
if ( y > meshBoundsMax . y ) meshBoundsMax . y = y ;
}
} else {
for ( int i = 0 ; i < attachmentVertexCount ; i + + ) {
int vi = ovc + i ;
int i2 = i < < 1 ; // i * 2
float x = workingVerts [ i2 ] ;
float y = workingVerts [ i2 + 1 ] ;
vbi [ vi ] . x = x ;
vbi [ vi ] . y = y ;
vbi [ vi ] . z = z ;
ubi [ vi ] . x = uvs [ i2 ] ;
ubi [ vi ] . y = uvs [ i2 + 1 ] ;
cbi [ vi ] = color ;
// Calculate bounds.
if ( x < meshBoundsMin . x ) meshBoundsMin . x = x ;
else if ( x > meshBoundsMax . x ) meshBoundsMax . x = x ;
if ( y < meshBoundsMin . y ) meshBoundsMin . y = y ;
else if ( y > meshBoundsMax . y ) meshBoundsMax . y = y ;
}
}
// Add data to triangle buffer
if ( updateTriangles ) {
int oldTriangleCount = submesh . Count ;
{ //submesh.Resize(oldTriangleCount + attachmentIndexCount);
int newTriangleCount = oldTriangleCount + attachmentIndexCount ;
if ( newTriangleCount > submesh . Items . Length ) Array . Resize ( ref submesh . Items , newTriangleCount ) ;
submesh . Count = newTriangleCount ;
}
2024-11-21 09:35:48 +08:00
int [ ] submeshItems = submesh . Items ;
2024-10-23 17:55:55 +08:00
for ( int i = 0 ; i < attachmentIndexCount ; i + + )
submeshItems [ oldTriangleCount + i ] = attachmentTriangleIndices [ i ] + ovc ;
}
}
clipper . ClipEnd ( slot ) ;
}
clipper . ClipEnd ( ) ;
this . meshBoundsMin = meshBoundsMin ;
this . meshBoundsMax = meshBoundsMax ;
meshBoundsThickness = instruction . endSlot * zSpacing ;
// Trim or zero submesh triangles.
2024-11-21 09:35:48 +08:00
int [ ] currentSubmeshItems = submesh . Items ;
2024-10-23 17:55:55 +08:00
for ( int i = submesh . Count , n = currentSubmeshItems . Length ; i < n ; i + + )
currentSubmeshItems [ i ] = 0 ;
submeshIndex + + ; // Next AddSubmesh will use a new submeshIndex value.
}
public void BuildMesh ( SkeletonRendererInstruction instruction , bool updateTriangles ) {
2024-11-21 09:35:48 +08:00
SubmeshInstruction [ ] wsii = instruction . submeshInstructions . Items ;
2024-10-23 17:55:55 +08:00
for ( int i = 0 , n = instruction . submeshInstructions . Count ; i < n ; i + + )
this . AddSubmesh ( wsii [ i ] , updateTriangles ) ;
}
// Use this faster method when no clipping is involved.
public void BuildMeshWithArrays ( SkeletonRendererInstruction instruction , bool updateTriangles ) {
2024-11-21 09:35:48 +08:00
#if ! SPINE_TRIANGLECHECK
return ;
#else
Settings settings = this . settings ;
bool canvasGroupTintBlack = settings . tintBlack & & settings . canvasGroupCompatible ;
2024-10-23 17:55:55 +08:00
int totalVertexCount = instruction . rawVertexCount ;
2024-11-21 09:35:48 +08:00
#if LINEAR_COLOR_SPACE_FIX_ADDITIVE_ALPHA
bool linearColorSpace = QualitySettings . activeColorSpace = = ColorSpace . Linear ;
#endif
2024-10-23 17:55:55 +08:00
// Add data to vertex buffers
{
if ( totalVertexCount > vertexBuffer . Items . Length ) { // Manual ExposedList.Resize()
Array . Resize ( ref vertexBuffer . Items , totalVertexCount ) ;
Array . Resize ( ref uvBuffer . Items , totalVertexCount ) ;
Array . Resize ( ref colorBuffer . Items , totalVertexCount ) ;
}
vertexBuffer . Count = uvBuffer . Count = colorBuffer . Count = totalVertexCount ;
}
// Populate Verts
Color32 color = default ( Color32 ) ;
int vertexIndex = 0 ;
2024-11-21 09:35:48 +08:00
float [ ] tempVerts = this . tempVerts ;
2024-10-23 17:55:55 +08:00
Vector2 bmin = this . meshBoundsMin ;
Vector2 bmax = this . meshBoundsMax ;
2024-11-21 09:35:48 +08:00
Vector3 [ ] vbi = vertexBuffer . Items ;
Vector2 [ ] ubi = uvBuffer . Items ;
Color32 [ ] cbi = colorBuffer . Items ;
2024-10-23 17:55:55 +08:00
int lastSlotIndex = 0 ;
// drawOrder[endSlot] is excluded
for ( int si = 0 , n = instruction . submeshInstructions . Count ; si < n ; si + + ) {
2024-11-21 09:35:48 +08:00
SubmeshInstruction submesh = instruction . submeshInstructions . Items [ si ] ;
Skeleton skeleton = submesh . skeleton ;
Slot [ ] drawOrderItems = skeleton . DrawOrder . Items ;
float a = skeleton . A , r = skeleton . R , g = skeleton . G , b = skeleton . B ;
2024-10-23 17:55:55 +08:00
int endSlot = submesh . endSlot ;
int startSlot = submesh . startSlot ;
lastSlotIndex = endSlot ;
if ( settings . tintBlack ) {
Vector2 rg , b2 ;
int vi = vertexIndex ;
b2 . y = 1f ;
2024-11-21 09:35:48 +08:00
PrepareOptionalUVBuffer ( ref uv2 , totalVertexCount ) ;
PrepareOptionalUVBuffer ( ref uv3 , totalVertexCount ) ;
2024-10-23 17:55:55 +08:00
2024-11-21 09:35:48 +08:00
Vector2 [ ] uv2i = uv2 . Items ;
Vector2 [ ] uv3i = uv3 . Items ;
2024-10-23 17:55:55 +08:00
for ( int slotIndex = startSlot ; slotIndex < endSlot ; slotIndex + + ) {
2024-11-21 09:35:48 +08:00
Slot slot = drawOrderItems [ slotIndex ] ;
if ( ! slot . Bone . Active
#if SLOT_ALPHA_DISABLES_ATTACHMENT
| | slot . A = = 0f
#endif
) continue ;
Attachment attachment = slot . Attachment ;
rg . x = slot . R2 ; //r
rg . y = slot . G2 ; //g
b2 . x = slot . B2 ; //b
2024-10-23 17:55:55 +08:00
b2 . y = 1.0f ;
2024-11-21 09:35:48 +08:00
RegionAttachment regionAttachment = attachment as RegionAttachment ;
2024-10-23 17:55:55 +08:00
if ( regionAttachment ! = null ) {
if ( settings . pmaVertexColors ) {
2024-11-21 09:35:48 +08:00
float alpha = a * slot . A * regionAttachment . A ;
bool isAdditiveSlot = slot . Data . BlendMode = = BlendMode . Additive ;
#if LINEAR_COLOR_SPACE_FIX_ADDITIVE_ALPHA
if ( linearColorSpace & & isAdditiveSlot )
alpha = Mathf . LinearToGammaSpace ( alpha ) ; // compensate GammaToLinear performed in shader
#endif
2024-10-23 17:55:55 +08:00
rg . x * = alpha ;
rg . y * = alpha ;
b2 . x * = alpha ;
2024-11-21 09:35:48 +08:00
b2 . y = isAdditiveSlot ? 0 : alpha ;
2024-10-23 17:55:55 +08:00
}
uv2i [ vi ] = rg ; uv2i [ vi + 1 ] = rg ; uv2i [ vi + 2 ] = rg ; uv2i [ vi + 3 ] = rg ;
uv3i [ vi ] = b2 ; uv3i [ vi + 1 ] = b2 ; uv3i [ vi + 2 ] = b2 ; uv3i [ vi + 3 ] = b2 ;
vi + = 4 ;
} else { //} if (settings.renderMeshes) {
2024-11-21 09:35:48 +08:00
MeshAttachment meshAttachment = attachment as MeshAttachment ;
2024-10-23 17:55:55 +08:00
if ( meshAttachment ! = null ) {
if ( settings . pmaVertexColors ) {
2024-11-21 09:35:48 +08:00
float alpha = a * slot . A * meshAttachment . A ;
bool isAdditiveSlot = slot . Data . BlendMode = = BlendMode . Additive ;
#if LINEAR_COLOR_SPACE_FIX_ADDITIVE_ALPHA
if ( linearColorSpace & & isAdditiveSlot )
alpha = Mathf . LinearToGammaSpace ( alpha ) ; // compensate GammaToLinear performed in shader
#endif
2024-10-23 17:55:55 +08:00
rg . x * = alpha ;
rg . y * = alpha ;
b2 . x * = alpha ;
2024-11-21 09:35:48 +08:00
b2 . y = isAdditiveSlot ? 0 : alpha ;
2024-10-23 17:55:55 +08:00
}
2024-11-21 09:35:48 +08:00
int verticesArrayLength = meshAttachment . WorldVerticesLength ;
for ( int iii = 0 ; iii < verticesArrayLength ; iii + = 2 ) {
2024-10-23 17:55:55 +08:00
uv2i [ vi ] = rg ;
uv3i [ vi ] = b2 ;
vi + + ;
}
}
}
}
}
for ( int slotIndex = startSlot ; slotIndex < endSlot ; slotIndex + + ) {
2024-11-21 09:35:48 +08:00
Slot slot = drawOrderItems [ slotIndex ] ;
if ( ! slot . Bone . Active
#if SLOT_ALPHA_DISABLES_ATTACHMENT
| | slot . A = = 0f
#endif
) continue ;
Attachment attachment = slot . Attachment ;
2024-10-23 17:55:55 +08:00
float z = slotIndex * settings . zSpacing ;
2024-11-21 09:35:48 +08:00
RegionAttachment regionAttachment = attachment as RegionAttachment ;
2024-10-23 17:55:55 +08:00
if ( regionAttachment ! = null ) {
2024-11-21 09:35:48 +08:00
regionAttachment . ComputeWorldVertices ( slot , tempVerts , 0 ) ;
2024-10-23 17:55:55 +08:00
float x1 = tempVerts [ RegionAttachment . BLX ] , y1 = tempVerts [ RegionAttachment . BLY ] ;
float x2 = tempVerts [ RegionAttachment . ULX ] , y2 = tempVerts [ RegionAttachment . ULY ] ;
float x3 = tempVerts [ RegionAttachment . URX ] , y3 = tempVerts [ RegionAttachment . URY ] ;
float x4 = tempVerts [ RegionAttachment . BRX ] , y4 = tempVerts [ RegionAttachment . BRY ] ;
vbi [ vertexIndex ] . x = x1 ; vbi [ vertexIndex ] . y = y1 ; vbi [ vertexIndex ] . z = z ;
vbi [ vertexIndex + 1 ] . x = x4 ; vbi [ vertexIndex + 1 ] . y = y4 ; vbi [ vertexIndex + 1 ] . z = z ;
vbi [ vertexIndex + 2 ] . x = x2 ; vbi [ vertexIndex + 2 ] . y = y2 ; vbi [ vertexIndex + 2 ] . z = z ;
2024-11-21 09:35:48 +08:00
vbi [ vertexIndex + 3 ] . x = x3 ; vbi [ vertexIndex + 3 ] . y = y3 ; vbi [ vertexIndex + 3 ] . z = z ;
2024-10-23 17:55:55 +08:00
if ( settings . pmaVertexColors ) {
2024-11-21 09:35:48 +08:00
float alpha = a * slot . A * regionAttachment . A ;
bool isAdditiveSlot = slot . Data . BlendMode = = BlendMode . Additive ;
#if LINEAR_COLOR_SPACE_FIX_ADDITIVE_ALPHA
if ( linearColorSpace & & isAdditiveSlot )
alpha = Mathf . LinearToGammaSpace ( alpha ) ; // compensate GammaToLinear performed in shader
#endif
color . a = ( byte ) ( alpha * 255 ) ;
color . r = ( byte ) ( r * slot . R * regionAttachment . R * color . a ) ;
color . g = ( byte ) ( g * slot . G * regionAttachment . G * color . a ) ;
color . b = ( byte ) ( b * slot . B * regionAttachment . B * color . a ) ;
if ( canvasGroupTintBlack ) color . a = 255 ;
else if ( isAdditiveSlot ) color . a = 0 ;
2024-10-23 17:55:55 +08:00
} else {
2024-11-21 09:35:48 +08:00
color . a = ( byte ) ( a * slot . A * regionAttachment . A * 255 ) ;
color . r = ( byte ) ( r * slot . R * regionAttachment . R * 255 ) ;
color . g = ( byte ) ( g * slot . G * regionAttachment . G * 255 ) ;
color . b = ( byte ) ( b * slot . B * regionAttachment . B * 255 ) ;
2024-10-23 17:55:55 +08:00
}
cbi [ vertexIndex ] = color ; cbi [ vertexIndex + 1 ] = color ; cbi [ vertexIndex + 2 ] = color ; cbi [ vertexIndex + 3 ] = color ;
2024-11-21 09:35:48 +08:00
float [ ] regionUVs = regionAttachment . UVs ;
2024-10-23 17:55:55 +08:00
ubi [ vertexIndex ] . x = regionUVs [ RegionAttachment . BLX ] ; ubi [ vertexIndex ] . y = regionUVs [ RegionAttachment . BLY ] ;
ubi [ vertexIndex + 1 ] . x = regionUVs [ RegionAttachment . BRX ] ; ubi [ vertexIndex + 1 ] . y = regionUVs [ RegionAttachment . BRY ] ;
ubi [ vertexIndex + 2 ] . x = regionUVs [ RegionAttachment . ULX ] ; ubi [ vertexIndex + 2 ] . y = regionUVs [ RegionAttachment . ULY ] ;
ubi [ vertexIndex + 3 ] . x = regionUVs [ RegionAttachment . URX ] ; ubi [ vertexIndex + 3 ] . y = regionUVs [ RegionAttachment . URY ] ;
if ( x1 < bmin . x ) bmin . x = x1 ; // Potential first attachment bounds initialization. Initial min should not block initial max. Same for Y below.
if ( x1 > bmax . x ) bmax . x = x1 ;
if ( x2 < bmin . x ) bmin . x = x2 ;
else if ( x2 > bmax . x ) bmax . x = x2 ;
if ( x3 < bmin . x ) bmin . x = x3 ;
else if ( x3 > bmax . x ) bmax . x = x3 ;
if ( x4 < bmin . x ) bmin . x = x4 ;
else if ( x4 > bmax . x ) bmax . x = x4 ;
if ( y1 < bmin . y ) bmin . y = y1 ;
if ( y1 > bmax . y ) bmax . y = y1 ;
if ( y2 < bmin . y ) bmin . y = y2 ;
else if ( y2 > bmax . y ) bmax . y = y2 ;
if ( y3 < bmin . y ) bmin . y = y3 ;
else if ( y3 > bmax . y ) bmax . y = y3 ;
if ( y4 < bmin . y ) bmin . y = y4 ;
else if ( y4 > bmax . y ) bmax . y = y4 ;
vertexIndex + = 4 ;
} else { //if (settings.renderMeshes) {
2024-11-21 09:35:48 +08:00
MeshAttachment meshAttachment = attachment as MeshAttachment ;
2024-10-23 17:55:55 +08:00
if ( meshAttachment ! = null ) {
2024-11-21 09:35:48 +08:00
int verticesArrayLength = meshAttachment . WorldVerticesLength ;
if ( tempVerts . Length < verticesArrayLength ) this . tempVerts = tempVerts = new float [ verticesArrayLength ] ;
2024-10-23 17:55:55 +08:00
meshAttachment . ComputeWorldVertices ( slot , tempVerts ) ;
if ( settings . pmaVertexColors ) {
2024-11-21 09:35:48 +08:00
float alpha = a * slot . A * meshAttachment . A ;
bool isAdditiveSlot = slot . Data . BlendMode = = BlendMode . Additive ;
#if LINEAR_COLOR_SPACE_FIX_ADDITIVE_ALPHA
if ( linearColorSpace & & isAdditiveSlot )
alpha = Mathf . LinearToGammaSpace ( alpha ) ; // compensate GammaToLinear performed in shader
#endif
color . a = ( byte ) ( alpha * 255 ) ;
color . r = ( byte ) ( r * slot . R * meshAttachment . R * color . a ) ;
color . g = ( byte ) ( g * slot . G * meshAttachment . G * color . a ) ;
color . b = ( byte ) ( b * slot . B * meshAttachment . B * color . a ) ;
if ( canvasGroupTintBlack ) color . a = 255 ;
else if ( isAdditiveSlot ) color . a = 0 ;
2024-10-23 17:55:55 +08:00
} else {
2024-11-21 09:35:48 +08:00
color . a = ( byte ) ( a * slot . A * meshAttachment . A * 255 ) ;
color . r = ( byte ) ( r * slot . R * meshAttachment . R * 255 ) ;
color . g = ( byte ) ( g * slot . G * meshAttachment . G * 255 ) ;
color . b = ( byte ) ( b * slot . B * meshAttachment . B * 255 ) ;
2024-10-23 17:55:55 +08:00
}
2024-11-21 09:35:48 +08:00
float [ ] attachmentUVs = meshAttachment . UVs ;
2024-10-23 17:55:55 +08:00
// Potential first attachment bounds initialization. See conditions in RegionAttachment logic.
if ( vertexIndex = = 0 ) {
// Initial min should not block initial max.
// vi == vertexIndex does not always mean the bounds are fresh. It could be a submesh. Do not nuke old values by omitting the check.
// Should know that this is the first attachment in the submesh. slotIndex == startSlot could be an empty slot.
float fx = tempVerts [ 0 ] , fy = tempVerts [ 1 ] ;
if ( fx < bmin . x ) bmin . x = fx ;
if ( fx > bmax . x ) bmax . x = fx ;
if ( fy < bmin . y ) bmin . y = fy ;
if ( fy > bmax . y ) bmax . y = fy ;
}
2024-11-21 09:35:48 +08:00
for ( int iii = 0 ; iii < verticesArrayLength ; iii + = 2 ) {
2024-10-23 17:55:55 +08:00
float x = tempVerts [ iii ] , y = tempVerts [ iii + 1 ] ;
vbi [ vertexIndex ] . x = x ; vbi [ vertexIndex ] . y = y ; vbi [ vertexIndex ] . z = z ;
cbi [ vertexIndex ] = color ; ubi [ vertexIndex ] . x = attachmentUVs [ iii ] ; ubi [ vertexIndex ] . y = attachmentUVs [ iii + 1 ] ;
if ( x < bmin . x ) bmin . x = x ;
else if ( x > bmax . x ) bmax . x = x ;
if ( y < bmin . y ) bmin . y = y ;
else if ( y > bmax . y ) bmax . y = y ;
vertexIndex + + ;
}
}
}
}
}
this . meshBoundsMin = bmin ;
this . meshBoundsMax = bmax ;
this . meshBoundsThickness = lastSlotIndex * settings . zSpacing ;
int submeshInstructionCount = instruction . submeshInstructions . Count ;
submeshes . Count = submeshInstructionCount ;
// Add triangles
if ( updateTriangles ) {
// Match submesh buffers count with submeshInstruction count.
if ( this . submeshes . Items . Length < submeshInstructionCount ) {
this . submeshes . Resize ( submeshInstructionCount ) ;
for ( int i = 0 , n = submeshInstructionCount ; i < n ; i + + ) {
2024-11-21 09:35:48 +08:00
ExposedList < int > submeshBuffer = this . submeshes . Items [ i ] ;
2024-10-23 17:55:55 +08:00
if ( submeshBuffer = = null )
this . submeshes . Items [ i ] = new ExposedList < int > ( ) ;
else
submeshBuffer . Clear ( false ) ;
}
}
2024-11-21 09:35:48 +08:00
SubmeshInstruction [ ] submeshInstructionsItems = instruction . submeshInstructions . Items ; // This relies on the resize above.
2024-10-23 17:55:55 +08:00
// Fill the buffers.
int attachmentFirstVertex = 0 ;
for ( int smbi = 0 ; smbi < submeshInstructionCount ; smbi + + ) {
2024-11-21 09:35:48 +08:00
SubmeshInstruction submeshInstruction = submeshInstructionsItems [ smbi ] ;
ExposedList < int > currentSubmeshBuffer = this . submeshes . Items [ smbi ] ;
2024-10-23 17:55:55 +08:00
{ //submesh.Resize(submesh.rawTriangleCount);
int newTriangleCount = submeshInstruction . rawTriangleCount ;
if ( newTriangleCount > currentSubmeshBuffer . Items . Length )
Array . Resize ( ref currentSubmeshBuffer . Items , newTriangleCount ) ;
else if ( newTriangleCount < currentSubmeshBuffer . Items . Length ) {
// Zero the extra.
2024-11-21 09:35:48 +08:00
int [ ] sbi = currentSubmeshBuffer . Items ;
2024-10-23 17:55:55 +08:00
for ( int ei = newTriangleCount , nn = sbi . Length ; ei < nn ; ei + + )
sbi [ ei ] = 0 ;
}
currentSubmeshBuffer . Count = newTriangleCount ;
}
2024-11-21 09:35:48 +08:00
int [ ] tris = currentSubmeshBuffer . Items ;
2024-10-23 17:55:55 +08:00
int triangleIndex = 0 ;
2024-11-21 09:35:48 +08:00
Skeleton skeleton = submeshInstruction . skeleton ;
Slot [ ] drawOrderItems = skeleton . DrawOrder . Items ;
2024-10-23 17:55:55 +08:00
for ( int slotIndex = submeshInstruction . startSlot , endSlot = submeshInstruction . endSlot ; slotIndex < endSlot ; slotIndex + + ) {
2024-11-21 09:35:48 +08:00
Slot slot = drawOrderItems [ slotIndex ] ;
if ( ! slot . Bone . Active
#if SLOT_ALPHA_DISABLES_ATTACHMENT
| | slot . A = = 0f
#endif
) continue ;
Attachment attachment = drawOrderItems [ slotIndex ] . Attachment ;
2024-10-23 17:55:55 +08:00
if ( attachment is RegionAttachment ) {
tris [ triangleIndex ] = attachmentFirstVertex ;
tris [ triangleIndex + 1 ] = attachmentFirstVertex + 2 ;
tris [ triangleIndex + 2 ] = attachmentFirstVertex + 1 ;
tris [ triangleIndex + 3 ] = attachmentFirstVertex + 2 ;
tris [ triangleIndex + 4 ] = attachmentFirstVertex + 3 ;
tris [ triangleIndex + 5 ] = attachmentFirstVertex + 1 ;
triangleIndex + = 6 ;
attachmentFirstVertex + = 4 ;
continue ;
}
2024-11-21 09:35:48 +08:00
MeshAttachment meshAttachment = attachment as MeshAttachment ;
2024-10-23 17:55:55 +08:00
if ( meshAttachment ! = null ) {
2024-11-21 09:35:48 +08:00
int [ ] attachmentTriangles = meshAttachment . Triangles ;
2024-10-23 17:55:55 +08:00
for ( int ii = 0 , nn = attachmentTriangles . Length ; ii < nn ; ii + + , triangleIndex + + )
tris [ triangleIndex ] = attachmentFirstVertex + attachmentTriangles [ ii ] ;
2024-11-21 09:35:48 +08:00
attachmentFirstVertex + = meshAttachment . WorldVerticesLength > > 1 ; // length/2;
2024-10-23 17:55:55 +08:00
}
}
}
}
2024-11-21 09:35:48 +08:00
#endif // SPINE_TRIANGLECHECK
2024-10-23 17:55:55 +08:00
}
public void ScaleVertexData ( float scale ) {
2024-11-21 09:35:48 +08:00
Vector3 [ ] vbi = vertexBuffer . Items ;
2024-10-23 17:55:55 +08:00
for ( int i = 0 , n = vertexBuffer . Count ; i < n ; i + + ) {
2024-11-21 09:35:48 +08:00
#if MANUALLY_INLINE_VECTOR_OPERATORS
vbi [ i ] . x * = scale ;
vbi [ i ] . y * = scale ;
vbi [ i ] . z * = scale ;
#else
vbi [ i ] * = scale ;
#endif
2024-10-23 17:55:55 +08:00
}
meshBoundsMin * = scale ;
meshBoundsMax * = scale ;
meshBoundsThickness * = scale ;
}
2024-11-21 09:35:48 +08:00
public void ScaleAndOffsetVertexData ( float scale , Vector2 offset2D ) {
Vector3 offset = new Vector3 ( offset2D . x , offset2D . y ) ;
Vector3 [ ] vbi = vertexBuffer . Items ;
for ( int i = 0 , n = vertexBuffer . Count ; i < n ; i + + ) {
#if MANUALLY_INLINE_VECTOR_OPERATORS
vbi [ i ] . x = vbi [ i ] . x * scale + offset . x ;
vbi [ i ] . y = vbi [ i ] . y * scale + offset . y ;
vbi [ i ] . z = vbi [ i ] . z * scale + offset . z ;
#else
vbi [ i ] = vbi [ i ] * scale + offset ;
#endif
}
meshBoundsMin * = scale ;
meshBoundsMax * = scale ;
meshBoundsMin + = offset2D ;
meshBoundsMax + = offset2D ;
meshBoundsThickness * = scale ;
}
public Bounds GetMeshBounds ( ) {
if ( float . IsInfinity ( meshBoundsMin . x ) ) { // meshBoundsMin.x == BoundsMinDefault // == doesn't work on float Infinity constants.
return new Bounds ( ) ;
} else {
//mesh.bounds = ArraysMeshGenerator.ToBounds(meshBoundsMin, meshBoundsMax);
float halfWidth = ( meshBoundsMax . x - meshBoundsMin . x ) * 0.5f ;
float halfHeight = ( meshBoundsMax . y - meshBoundsMin . y ) * 0.5f ;
return new Bounds {
center = new Vector3 ( meshBoundsMin . x + halfWidth , meshBoundsMin . y + halfHeight ) ,
extents = new Vector3 ( halfWidth , halfHeight , meshBoundsThickness * 0.5f )
} ;
}
}
2024-10-23 17:55:55 +08:00
void AddAttachmentTintBlack ( float r2 , float g2 , float b2 , float a , int vertexCount ) {
2024-11-21 09:35:48 +08:00
Vector2 rg = new Vector2 ( r2 , g2 ) ;
Vector2 bo = new Vector2 ( b2 , a ) ;
2024-10-23 17:55:55 +08:00
int ovc = vertexBuffer . Count ;
int newVertexCount = ovc + vertexCount ;
2024-11-21 09:35:48 +08:00
PrepareOptionalUVBuffer ( ref uv2 , newVertexCount ) ;
PrepareOptionalUVBuffer ( ref uv3 , newVertexCount ) ;
Vector2 [ ] uv2i = uv2 . Items ;
Vector2 [ ] uv3i = uv3 . Items ;
2024-10-23 17:55:55 +08:00
for ( int i = 0 ; i < vertexCount ; i + + ) {
uv2i [ ovc + i ] = rg ;
uv3i [ ovc + i ] = bo ;
}
}
2024-11-21 09:35:48 +08:00
void PrepareOptionalUVBuffer ( ref ExposedList < Vector2 > uvBuffer , int vertexCount ) {
if ( uvBuffer = = null ) {
uvBuffer = new ExposedList < Vector2 > ( ) ;
}
if ( vertexCount > uvBuffer . Items . Length ) { // Manual ExposedList.Resize()
Array . Resize ( ref uvBuffer . Items , vertexCount ) ;
}
uvBuffer . Count = vertexCount ;
}
void ResizeOptionalUVBuffer ( ref ExposedList < Vector2 > uvBuffer , int vertexCount ) {
if ( uvBuffer ! = null ) {
if ( vertexCount ! = uvBuffer . Items . Length ) {
Array . Resize ( ref uvBuffer . Items , vertexCount ) ;
uvBuffer . Count = vertexCount ;
}
}
}
2024-10-23 17:55:55 +08:00
#endregion
#region Step 3 : Transfer vertex and triangle data to UnityEngine . Mesh
public void FillVertexData ( Mesh mesh ) {
2024-11-21 09:35:48 +08:00
Vector3 [ ] vbi = vertexBuffer . Items ;
Vector2 [ ] ubi = uvBuffer . Items ;
Color32 [ ] cbi = colorBuffer . Items ;
2024-10-23 17:55:55 +08:00
int vbiLength = vbi . Length ;
// Zero the extra.
{
int listCount = vertexBuffer . Count ;
2024-11-21 09:35:48 +08:00
Vector3 vector3zero = Vector3 . zero ;
2024-10-23 17:55:55 +08:00
for ( int i = listCount ; i < vbiLength ; i + + )
vbi [ i ] = vector3zero ;
}
// Set the vertex buffer.
{
mesh . vertices = vbi ;
mesh . uv = ubi ;
mesh . colors32 = cbi ;
2024-11-21 09:35:48 +08:00
mesh . bounds = GetMeshBounds ( ) ;
2024-10-23 17:55:55 +08:00
}
{
if ( settings . addNormals ) {
int oldLength = 0 ;
if ( normals = = null )
normals = new Vector3 [ vbiLength ] ;
else
oldLength = normals . Length ;
if ( oldLength ! = vbiLength ) {
Array . Resize ( ref this . normals , vbiLength ) ;
2024-11-21 09:35:48 +08:00
Vector3 [ ] localNormals = this . normals ;
2024-10-23 17:55:55 +08:00
for ( int i = oldLength ; i < vbiLength ; i + + ) localNormals [ i ] = Vector3 . back ;
}
mesh . normals = this . normals ;
}
2024-11-21 09:35:48 +08:00
// Sometimes, the vertex buffer becomes smaller. We need to trim the size of
// the uv2 and uv3 buffers (used for tint black) to match.
ResizeOptionalUVBuffer ( ref uv2 , vbiLength ) ;
ResizeOptionalUVBuffer ( ref uv3 , vbiLength ) ;
mesh . uv2 = this . uv2 = = null ? null : this . uv2 . Items ;
mesh . uv3 = this . uv3 = = null ? null : this . uv3 . Items ;
2024-10-23 17:55:55 +08:00
}
}
public void FillLateVertexData ( Mesh mesh ) {
if ( settings . calculateTangents ) {
int vertexCount = this . vertexBuffer . Count ;
2024-11-21 09:35:48 +08:00
ExposedList < int > [ ] sbi = submeshes . Items ;
2024-10-23 17:55:55 +08:00
int submeshCount = submeshes . Count ;
2024-11-21 09:35:48 +08:00
Vector3 [ ] vbi = vertexBuffer . Items ;
Vector2 [ ] ubi = uvBuffer . Items ;
2024-10-23 17:55:55 +08:00
MeshGenerator . SolveTangents2DEnsureSize ( ref this . tangents , ref this . tempTanBuffer , vertexCount , vbi . Length ) ;
for ( int i = 0 ; i < submeshCount ; i + + ) {
2024-11-21 09:35:48 +08:00
int [ ] submesh = sbi [ i ] . Items ;
2024-10-23 17:55:55 +08:00
int triangleCount = sbi [ i ] . Count ;
MeshGenerator . SolveTangents2DTriangles ( this . tempTanBuffer , submesh , triangleCount , vbi , ubi , vertexCount ) ;
}
MeshGenerator . SolveTangents2DBuffer ( this . tangents , this . tempTanBuffer , vertexCount ) ;
mesh . tangents = this . tangents ;
}
}
public void FillTriangles ( Mesh mesh ) {
int submeshCount = submeshes . Count ;
2024-11-21 09:35:48 +08:00
ExposedList < int > [ ] submeshesItems = submeshes . Items ;
2024-10-23 17:55:55 +08:00
mesh . subMeshCount = submeshCount ;
for ( int i = 0 ; i < submeshCount ; i + + )
2024-11-21 09:35:48 +08:00
#if MESH_SET_TRIANGLES_PROVIDES_LENGTH_PARAM
mesh . SetTriangles ( submeshesItems [ i ] . Items , 0 , submeshesItems [ i ] . Count , i , false ) ;
#else
2024-10-23 17:55:55 +08:00
mesh . SetTriangles ( submeshesItems [ i ] . Items , i , false ) ;
2024-11-21 09:35:48 +08:00
#endif
2024-10-23 17:55:55 +08:00
}
#endregion
public void EnsureVertexCapacity ( int minimumVertexCount , bool inlcudeTintBlack = false , bool includeTangents = false , bool includeNormals = false ) {
if ( minimumVertexCount > vertexBuffer . Items . Length ) {
Array . Resize ( ref vertexBuffer . Items , minimumVertexCount ) ;
Array . Resize ( ref uvBuffer . Items , minimumVertexCount ) ;
Array . Resize ( ref colorBuffer . Items , minimumVertexCount ) ;
if ( inlcudeTintBlack ) {
if ( uv2 = = null ) {
uv2 = new ExposedList < Vector2 > ( minimumVertexCount ) ;
uv3 = new ExposedList < Vector2 > ( minimumVertexCount ) ;
}
uv2 . Resize ( minimumVertexCount ) ;
uv3 . Resize ( minimumVertexCount ) ;
}
if ( includeNormals ) {
if ( normals = = null )
normals = new Vector3 [ minimumVertexCount ] ;
else
Array . Resize ( ref normals , minimumVertexCount ) ;
}
if ( includeTangents ) {
if ( tangents = = null )
tangents = new Vector4 [ minimumVertexCount ] ;
else
Array . Resize ( ref tangents , minimumVertexCount ) ;
}
}
}
/// <summary>Trims internal buffers to reduce the resulting mesh data stream size.</summary>
public void TrimExcess ( ) {
vertexBuffer . TrimExcess ( ) ;
uvBuffer . TrimExcess ( ) ;
colorBuffer . TrimExcess ( ) ;
if ( uv2 ! = null ) uv2 . TrimExcess ( ) ;
if ( uv3 ! = null ) uv3 . TrimExcess ( ) ;
int vbiLength = vertexBuffer . Items . Length ;
if ( normals ! = null ) Array . Resize ( ref normals , vbiLength ) ;
if ( tangents ! = null ) Array . Resize ( ref tangents , vbiLength ) ;
}
#region TangentSolver2D
// Thanks to contributions from forum user ToddRivers
/// <summary>Step 1 of solving tangents. Ensure you have buffers of the correct size.</summary>
/// <param name="tangentBuffer">Eventual Vector4[] tangent buffer to assign to Mesh.tangents.</param>
/// <param name="tempTanBuffer">Temporary Vector2 buffer for calculating directions.</param>
/// <param name="vertexCount">Number of vertices that require tangents (or the size of the vertex array)</param>
internal static void SolveTangents2DEnsureSize ( ref Vector4 [ ] tangentBuffer , ref Vector2 [ ] tempTanBuffer , int vertexCount , int vertexBufferLength ) {
if ( tangentBuffer = = null | | tangentBuffer . Length ! = vertexBufferLength )
tangentBuffer = new Vector4 [ vertexBufferLength ] ;
if ( tempTanBuffer = = null | | tempTanBuffer . Length < vertexCount * 2 )
tempTanBuffer = new Vector2 [ vertexCount * 2 ] ; // two arrays in one.
}
/// <summary>Step 2 of solving tangents. Fills (part of) a temporary tangent-solution buffer based on the vertices and uvs defined by a submesh's triangle buffer. Only needs to be called once for single-submesh meshes.</summary>
/// <param name="tempTanBuffer">A temporary Vector3[] for calculating tangents.</param>
/// <param name="vertices">The mesh's current vertex position buffer.</param>
/// <param name="triangles">The mesh's current triangles buffer.</param>
/// <param name="uvs">The mesh's current uvs buffer.</param>
/// <param name="vertexCount">Number of vertices that require tangents (or the size of the vertex array)</param>
/// <param name = "triangleCount">The number of triangle indexes in the triangle array to be used.</param>
internal static void SolveTangents2DTriangles ( Vector2 [ ] tempTanBuffer , int [ ] triangles , int triangleCount , Vector3 [ ] vertices , Vector2 [ ] uvs , int vertexCount ) {
Vector2 sdir ;
Vector2 tdir ;
for ( int t = 0 ; t < triangleCount ; t + = 3 ) {
int i1 = triangles [ t + 0 ] ;
int i2 = triangles [ t + 1 ] ;
int i3 = triangles [ t + 2 ] ;
Vector3 v1 = vertices [ i1 ] ;
Vector3 v2 = vertices [ i2 ] ;
Vector3 v3 = vertices [ i3 ] ;
Vector2 w1 = uvs [ i1 ] ;
Vector2 w2 = uvs [ i2 ] ;
Vector2 w3 = uvs [ i3 ] ;
float x1 = v2 . x - v1 . x ;
float x2 = v3 . x - v1 . x ;
float y1 = v2 . y - v1 . y ;
float y2 = v3 . y - v1 . y ;
float s1 = w2 . x - w1 . x ;
float s2 = w3 . x - w1 . x ;
float t1 = w2 . y - w1 . y ;
float t2 = w3 . y - w1 . y ;
float div = s1 * t2 - s2 * t1 ;
float r = ( div = = 0f ) ? 0f : 1f / div ;
sdir . x = ( t2 * x1 - t1 * x2 ) * r ;
sdir . y = ( t2 * y1 - t1 * y2 ) * r ;
tempTanBuffer [ i1 ] = tempTanBuffer [ i2 ] = tempTanBuffer [ i3 ] = sdir ;
tdir . x = ( s1 * x2 - s2 * x1 ) * r ;
tdir . y = ( s1 * y2 - s2 * y1 ) * r ;
tempTanBuffer [ vertexCount + i1 ] = tempTanBuffer [ vertexCount + i2 ] = tempTanBuffer [ vertexCount + i3 ] = tdir ;
}
}
/// <summary>Step 3 of solving tangents. Fills a Vector4[] tangents array according to values calculated in step 2.</summary>
/// <param name="tangents">A Vector4[] that will eventually be used to set Mesh.tangents</param>
/// <param name="tempTanBuffer">A temporary Vector3[] for calculating tangents.</param>
/// <param name="vertexCount">Number of vertices that require tangents (or the size of the vertex array)</param>
internal static void SolveTangents2DBuffer ( Vector4 [ ] tangents , Vector2 [ ] tempTanBuffer , int vertexCount ) {
Vector4 tangent ;
tangent . z = 0 ;
for ( int i = 0 ; i < vertexCount ; + + i ) {
Vector2 t = tempTanBuffer [ i ] ;
// t.Normalize() (aggressively inlined). Even better if offloaded to GPU via vertex shader.
float magnitude = Mathf . Sqrt ( t . x * t . x + t . y * t . y ) ;
if ( magnitude > 1E-05 ) {
2024-11-21 09:35:48 +08:00
float reciprocalMagnitude = 1f / magnitude ;
2024-10-23 17:55:55 +08:00
t . x * = reciprocalMagnitude ;
t . y * = reciprocalMagnitude ;
}
Vector2 t2 = tempTanBuffer [ vertexCount + i ] ;
tangent . x = t . x ;
tangent . y = t . y ;
//tangent.z = 0;
tangent . w = ( t . y * t2 . x > t . x * t2 . y ) ? 1 : - 1 ; // 2D direction calculation. Used for binormals.
tangents [ i ] = tangent ;
}
}
#endregion
#region AttachmentRendering
static List < Vector3 > AttachmentVerts = new List < Vector3 > ( ) ;
static List < Vector2 > AttachmentUVs = new List < Vector2 > ( ) ;
static List < Color32 > AttachmentColors32 = new List < Color32 > ( ) ;
static List < int > AttachmentIndices = new List < int > ( ) ;
/// <summary>Fills mesh vertex data to render a RegionAttachment.</summary>
public static void FillMeshLocal ( Mesh mesh , RegionAttachment regionAttachment ) {
if ( mesh = = null ) return ;
if ( regionAttachment = = null ) return ;
AttachmentVerts . Clear ( ) ;
2024-11-21 09:35:48 +08:00
float [ ] offsets = regionAttachment . Offset ;
2024-10-23 17:55:55 +08:00
AttachmentVerts . Add ( new Vector3 ( offsets [ RegionAttachment . BLX ] , offsets [ RegionAttachment . BLY ] ) ) ;
AttachmentVerts . Add ( new Vector3 ( offsets [ RegionAttachment . ULX ] , offsets [ RegionAttachment . ULY ] ) ) ;
AttachmentVerts . Add ( new Vector3 ( offsets [ RegionAttachment . URX ] , offsets [ RegionAttachment . URY ] ) ) ;
AttachmentVerts . Add ( new Vector3 ( offsets [ RegionAttachment . BRX ] , offsets [ RegionAttachment . BRY ] ) ) ;
AttachmentUVs . Clear ( ) ;
2024-11-21 09:35:48 +08:00
float [ ] uvs = regionAttachment . UVs ;
2024-10-23 17:55:55 +08:00
AttachmentUVs . Add ( new Vector2 ( uvs [ RegionAttachment . ULX ] , uvs [ RegionAttachment . ULY ] ) ) ;
AttachmentUVs . Add ( new Vector2 ( uvs [ RegionAttachment . URX ] , uvs [ RegionAttachment . URY ] ) ) ;
AttachmentUVs . Add ( new Vector2 ( uvs [ RegionAttachment . BRX ] , uvs [ RegionAttachment . BRY ] ) ) ;
AttachmentUVs . Add ( new Vector2 ( uvs [ RegionAttachment . BLX ] , uvs [ RegionAttachment . BLY ] ) ) ;
AttachmentColors32 . Clear ( ) ;
2024-11-21 09:35:48 +08:00
Color32 c = ( Color32 ) ( new Color ( regionAttachment . R , regionAttachment . G , regionAttachment . B , regionAttachment . A ) ) ;
2024-10-23 17:55:55 +08:00
for ( int i = 0 ; i < 4 ; i + + )
AttachmentColors32 . Add ( c ) ;
AttachmentIndices . Clear ( ) ;
AttachmentIndices . AddRange ( new [ ] { 0 , 2 , 1 , 0 , 3 , 2 } ) ;
mesh . Clear ( ) ;
mesh . name = regionAttachment . Name ;
mesh . SetVertices ( AttachmentVerts ) ;
mesh . SetUVs ( 0 , AttachmentUVs ) ;
mesh . SetColors ( AttachmentColors32 ) ;
mesh . SetTriangles ( AttachmentIndices , 0 ) ;
mesh . RecalculateBounds ( ) ;
AttachmentVerts . Clear ( ) ;
AttachmentUVs . Clear ( ) ;
AttachmentColors32 . Clear ( ) ;
AttachmentIndices . Clear ( ) ;
}
public static void FillMeshLocal ( Mesh mesh , MeshAttachment meshAttachment , SkeletonData skeletonData ) {
if ( mesh = = null ) return ;
if ( meshAttachment = = null ) return ;
int vertexCount = meshAttachment . WorldVerticesLength / 2 ;
AttachmentVerts . Clear ( ) ;
if ( meshAttachment . IsWeighted ( ) ) {
int count = meshAttachment . WorldVerticesLength ;
2024-11-21 09:35:48 +08:00
int [ ] meshAttachmentBones = meshAttachment . Bones ;
2024-10-23 17:55:55 +08:00
int v = 0 ;
2024-11-21 09:35:48 +08:00
float [ ] vertices = meshAttachment . Vertices ;
2024-10-23 17:55:55 +08:00
for ( int w = 0 , b = 0 ; w < count ; w + = 2 ) {
float wx = 0 , wy = 0 ;
int n = meshAttachmentBones [ v + + ] ;
n + = v ;
for ( ; v < n ; v + + , b + = 3 ) {
2024-11-21 09:35:48 +08:00
BoneMatrix bm = BoneMatrix . CalculateSetupWorld ( skeletonData . Bones . Items [ meshAttachmentBones [ v ] ] ) ;
2024-10-23 17:55:55 +08:00
float vx = vertices [ b ] , vy = vertices [ b + 1 ] , weight = vertices [ b + 2 ] ;
wx + = ( vx * bm . a + vy * bm . b + bm . x ) * weight ;
wy + = ( vx * bm . c + vy * bm . d + bm . y ) * weight ;
}
AttachmentVerts . Add ( new Vector3 ( wx , wy ) ) ;
}
} else {
2024-11-21 09:35:48 +08:00
float [ ] localVerts = meshAttachment . Vertices ;
2024-10-23 17:55:55 +08:00
Vector3 pos = default ( Vector3 ) ;
for ( int i = 0 ; i < vertexCount ; i + + ) {
int ii = i * 2 ;
pos . x = localVerts [ ii ] ;
pos . y = localVerts [ ii + 1 ] ;
AttachmentVerts . Add ( pos ) ;
}
}
2024-11-21 09:35:48 +08:00
float [ ] uvs = meshAttachment . UVs ;
2024-10-23 17:55:55 +08:00
Vector2 uv = default ( Vector2 ) ;
2024-11-21 09:35:48 +08:00
Color32 c = ( Color32 ) ( new Color ( meshAttachment . R , meshAttachment . G , meshAttachment . B , meshAttachment . A ) ) ;
2024-10-23 17:55:55 +08:00
AttachmentUVs . Clear ( ) ;
AttachmentColors32 . Clear ( ) ;
for ( int i = 0 ; i < vertexCount ; i + + ) {
int ii = i * 2 ;
uv . x = uvs [ ii ] ;
uv . y = uvs [ ii + 1 ] ;
AttachmentUVs . Add ( uv ) ;
AttachmentColors32 . Add ( c ) ;
}
AttachmentIndices . Clear ( ) ;
2024-11-21 09:35:48 +08:00
AttachmentIndices . AddRange ( meshAttachment . Triangles ) ;
2024-10-23 17:55:55 +08:00
mesh . Clear ( ) ;
mesh . name = meshAttachment . Name ;
mesh . SetVertices ( AttachmentVerts ) ;
mesh . SetUVs ( 0 , AttachmentUVs ) ;
mesh . SetColors ( AttachmentColors32 ) ;
mesh . SetTriangles ( AttachmentIndices , 0 ) ;
mesh . RecalculateBounds ( ) ;
AttachmentVerts . Clear ( ) ;
AttachmentUVs . Clear ( ) ;
AttachmentColors32 . Clear ( ) ;
AttachmentIndices . Clear ( ) ;
}
#endregion
}
}