228 lines
6.8 KiB
C#
228 lines
6.8 KiB
C#
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<List<Vector3>> _ptPool = new(1024);
|
|
static readonly SimpleObjectPool<List<float>> _lPool = new(1024);
|
|
|
|
static readonly ListPool<int> _indexPool = new();
|
|
static readonly ListPool<Vector3> _vertPool = new();
|
|
static readonly ListPool<Vector2> _uvPool = new();
|
|
static readonly ArrayPool<Vector2> _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<Vector2>(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<Vector3> vertices;
|
|
List<float> 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<Vector3> verts;
|
|
List<Vector2> uvs;
|
|
List<int> trih = null, trib = null, trit = null;
|
|
static readonly List<int> _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<Vector3> verts, List<Vector2> uvs, out List<int> 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;
|
|
}
|
|
}
|
|
}
|