Files
crtr/Assets/Cryville/Crtr/Skin/Components/PolygonSGO.cs

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;
}
}
}