using Cryville.Common.Buffers; using Cryville.Common.Pdt; using System; using System.Collections.Generic; using UnityEngine; namespace Cryville.Crtr.Skin.Components { public class PolygonSGO : SectionalGameObject { static readonly SimpleObjectPool> _ptPool = new(1024); static readonly SimpleObjectPool> _lPool = new(1024); static readonly ListPool _indexPool = new(); static readonly ListPool _vertPool = new(); static readonly ListPool _uvPool = new(); static readonly ArrayPool _shapePool = new(0x100, 0x10000); public PolygonSGO() { SubmitProperty("head", new PropOp.String(v => head.FrameName = v)); SubmitProperty("body", new PropOp.String(v => body.FrameName = v)); SubmitProperty("tail", new PropOp.String(v => tail.FrameName = v)); SubmitProperty("shape", new op_set_shape(this), 2); } #pragma warning disable IDE1006 public class op_set_shape : PdtOperator { readonly PolygonSGO _self; public op_set_shape(PolygonSGO self) : base(1) { _self = self; } protected override unsafe void Execute() { var o = GetOperand(0); if (o.Type != PdtInternalType.Vector) throw new ArgumentException("Not a vector"); _self._shapeLength = (o.Length - sizeof(int)) / sizeof(Vector2); if (_self._shape != null) _shapePool.Return(_self._shape); _self._shape = _shapePool.Rent(_self._shapeLength); for (int i = 0; i < _self._shapeLength; i++) { _self._shape[i] = o.As(i * sizeof(Vector2)); } } } #pragma warning restore IDE1006 int _shapeLength = 0; Vector2[] _shape = null; float GetWidth() { float r = 0; for (int i = 1; i < _shapeLength; i++) { r += (_shape[i] - _shape[i-1]).magnitude; } return r; } public SpriteInfo head = new(); public SpriteInfo body = new(); public SpriteInfo tail = new(); List vertices; List lengths; float sumLength = 0; public override void Init() { mesh.Init(transform); mesh.Mesh = new Mesh(); mesh.Renderer.sharedMaterials = materials = new Material[] { MeshWrapper.NewMaterial(), MeshWrapper.NewMaterial(), MeshWrapper.NewMaterial(), }; head.Bind(materials[0]); body.Bind(materials[1]); tail.Bind(materials[2]); base.Init(); } protected override void OnDestroy() { if (_shape != null) _shapePool.Return(_shape); if (vertices != null) { _ptPool.Return(vertices); _lPool.Return(lengths); } base.OnDestroy(); } protected override void AppendPointInternal(Vector3 p, Quaternion r) { if (vertices == null) { vertices = _ptPool.Rent(); lengths = _lPool.Rent(); } for (int i = 0; i < _shapeLength; i++) { Vector2 sp = r * _shape[i]; vertices.Add(p + (Vector3)sp); } if (prevpt != null) { float len = (p - prevpt.Value).magnitude; lengths.Add(len); sumLength += len; } } List verts; List uvs; List trih = null, trib = null, trit = null; static readonly List _emptyTris = new(); public override void Seal() { if (vertCount <= 1 || sumLength == 0) return; int vcpsec = _shapeLength; // Vertex Count Per Section float width = GetWidth(); float headLength = 0; if (head.Frame != null) headLength = width / head.Ratio; float tailLength = 0; if (tail.Frame != null) tailLength = width / tail.Ratio; float endLength = headLength + tailLength; if (sumLength <= endLength) { // The total length of the two ends is longer than the whole mesh, squeeze the two ends float ratio = sumLength / endLength; headLength *= ratio; tailLength *= ratio; } // Find the vertex counts needed for the three parts from the head float l0 = 0; int hvc = 0; for (; l0 < headLength && hvc < lengths.Count; hvc++) l0 += lengths[hvc]; int bvc = 0; for (; l0 < sumLength - tailLength && hvc + bvc < lengths.Count; bvc++) l0 += lengths[hvc + bvc]; int tvc = lengths.Count - hvc - bvc + 1; if (hvc > 0) { hvc++; bvc++; } bvc++; int vc = hvc + bvc + tvc; verts = _vertPool.Rent(vc * vcpsec); uvs = _uvPool.Rent(vc * vcpsec); int i = 0; int t = 0; float l = 0; if (head.Frame != null) { GenerateMeshTo(verts, uvs, out trih, head, ref i, ref t, ref l, 0, headLength, vcpsec, hvc); } GenerateMeshTo(verts, uvs, out trib, body, ref i, ref t, ref l, headLength, sumLength - tailLength, vcpsec, hvc + bvc); if (tail.Frame != null) { GenerateMeshTo(verts, uvs, out trit, tail, ref i, ref t, ref l, sumLength - tailLength, sumLength, vcpsec, vc); } mesh.Mesh.subMeshCount = 3; int m = 0; mesh.Mesh.SetVertices(verts); mesh.Mesh.SetUVs(0, uvs); mesh.Mesh.SetTriangles(head.Frame == null ? _emptyTris : trih, m++); mesh.Mesh.SetTriangles(trib, m++); mesh.Mesh.SetTriangles(tail.Frame == null ? _emptyTris : trit, m++); mesh.Mesh.RecalculateNormals(); _vertPool.Return(verts); verts = null; _uvPool.Return(uvs); uvs = null; if (trih != null) { _indexPool.Return(trih); trih = null; } if (trib != null) { _indexPool.Return(trib); trib = null; } if (trit != null) { _indexPool.Return(trit); trit = null; } } void GenerateMeshTo(List verts, List uvs, out List tris, SpriteInfo info, ref int i, ref int t, ref float l, float startl, float endl, int vcpsec, int vend) { if (i > lengths.Count) { tris = _indexPool.Rent(0); return; } int t0 = t; var frame = info.Frame; if (startl != 0) { float pl2 = l - lengths[i - 1]; float fr2 = (startl - pl2) / (l - pl2); for (int j = 0; j < vcpsec; j++, t++) { verts[t] = vertices[vcpsec*(i-1)+j] * (1-fr2) + vertices[vcpsec*i+j] * fr2; var u = j / (vcpsec - 1); if (frame != null) uvs[t] = frame.GetUV(u, 0); } } for (; t / vcpsec < vend - 1; i++) { var v = (l - startl) / (endl - startl); for (int j = 0; j < vcpsec; j++, t++) { verts[t] = vertices[vcpsec*i+j]; var u = j / (vcpsec - 1); if (frame != null) uvs[t] = frame.GetUV(u, v); } l += lengths[i]; } float pl = l - lengths[i - 1]; float fr = (endl - pl) / (l - pl); for (int j = 0; j < vcpsec; j++, t++) { verts[t] = vertices[vcpsec*(i-1)+j] * (1-fr) + vertices[vcpsec*i+j] * fr; var u = j / (vcpsec - 1); if (frame != null) uvs[t] = frame.GetUV(u, 1); } tris = _indexPool.Rent((vend - t0 / vcpsec - 1) * (vcpsec - 1) * 6); int i3 = 0; for (int i1 = t0 + vcpsec + 1; i1 < t; i1++) { if (i1 % vcpsec == 0) continue; int i2 = i1 - vcpsec; tris[i3++] = i1 - 1; tris[i3++] = i2; tris[i3++] = i2 - 1; tris[i3++] = i1 - 1; tris[i3++] = i1; tris[i3++] = i2; } } public override void Reset() { base.Reset(); if (mesh.Initialized) mesh.Mesh.Clear(); if (vertices != null) { vertices.Clear(); lengths.Clear(); } sumLength = 0; } } }