Files
project-rr/tools/spineconv/Program.cs
T
Leonid PershinandClaude Opus 4.8 04cf5f90b0 ProjectRR — нативный прототип на ассетах ProjectR
- Все 189 скелетов Spine сконвертированы в нативные Godot Skeleton2D (.tscn), без Spine в рантайме
- Меню + сцены: подземелье (карта комнат), просмотр спрайтов, нативные анимации
- Кастомизация цвета (кожа/волосы/глаза) через шейдер
- Вытащенные текстуры/UI/шрифты + нарезка спрайтов из атласа
- Датасет комнат для LoRA, офлайн-инструменты конвертации в tools/

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-03 16:29:21 +03:00

133 lines
4.9 KiB
C#

using System;
using System.Collections.Generic;
using System.IO;
using System.Text.Json;
using Spine;
// Офлайн-экспортёр: читает Spine .skel.bytes+.atlas через spine-csharp и
// выгружает геометрию (кости setup + привязки с мировыми вершинами/UV/треуг/весами) в свой JSON.
// Использование: spineconv <base_path_без_расширения> <out.json>
class NullTex : TextureLoader
{
public void Load(AtlasPage page, string path) { }
public void Unload(object texture) { }
}
class Program
{
static void Main(string[] args)
{
string baseP = args[0];
string outPath = args[1];
var atlas = new Atlas(baseP + ".atlas.txt", new NullTex());
var loader = new AtlasAttachmentLoader(atlas);
var binary = new SkeletonBinary(loader) { Scale = 1f };
SkeletonData data = binary.ReadSkeletonData(baseP + ".skel.bytes");
var skeleton = new Skeleton(data);
skeleton.SetToSetupPose();
skeleton.UpdateWorldTransform(Skeleton.Physics.Update);
var boneIndex = new Dictionary<BoneData, int>();
var bonesOut = new List<object>();
var bones = data.Bones;
for (int i = 0; i < bones.Count; i++)
{
var b = bones.Items[i];
boneIndex[b] = i;
bonesOut.Add(new
{
name = b.Name,
parent = b.Parent != null ? b.Parent.Name : null,
x = b.X, y = b.Y, rotation = b.Rotation,
scaleX = b.ScaleX, scaleY = b.ScaleY,
shearX = b.ShearX, shearY = b.ShearY, length = b.Length
});
}
var attsOut = new List<object>();
var pages = new HashSet<string>();
var drawOrder = skeleton.DrawOrder;
for (int d = 0; d < drawOrder.Count; d++)
{
Slot slot = drawOrder.Items[d];
Attachment att = slot.Attachment;
if (att == null) continue;
int sBone = boneIndex[slot.Data.BoneData];
var sd = slot.Data;
float[] color = { sd.R, sd.G, sd.B, sd.A };
if (att is RegionAttachment ra)
{
float[] wv = new float[8];
ra.ComputeWorldVertices(slot, wv, 0, 2);
var reg = (AtlasRegion)ra.Region;
pages.Add(reg.page.name);
var weights = new List<object>();
for (int k = 0; k < 4; k++)
weights.Add(new object[] { new object[] { sBone, 1f } });
attsOut.Add(new
{
slot = sd.Name, name = ra.Name, type = "region", page = reg.page.name,
world = wv, uv = ra.UVs, triangles = new int[] { 0, 1, 2, 2, 3, 0 },
weights, color
});
}
else if (att is MeshAttachment ma)
{
int n = ma.WorldVerticesLength;
float[] wv = new float[n];
ma.ComputeWorldVertices(slot, 0, n, wv, 0, 2);
var reg = (AtlasRegion)ma.Region;
pages.Add(reg.page.name);
attsOut.Add(new
{
slot = sd.Name, name = ma.Name, type = "mesh", page = reg.page.name,
world = wv, uv = ma.UVs, triangles = ma.Triangles,
weights = ParseWeights(ma, sBone), color
});
}
}
var outObj = new { bones = bonesOut, pages = new List<string>(pages), attachments = attsOut };
File.WriteAllText(outPath, JsonSerializer.Serialize(outObj));
int meshes = 0, regions = 0, weighted = 0;
foreach (var s in drawOrder)
{
if (s.Attachment is MeshAttachment m) { meshes++; if (m.Bones != null) weighted++; }
else if (s.Attachment is RegionAttachment) regions++;
}
Console.WriteLine($"OK bones={bonesOut.Count} regions={regions} meshes={meshes} (weighted={weighted}) pages={pages.Count}");
}
static List<object> ParseWeights(MeshAttachment ma, int fallbackBone)
{
var res = new List<object>();
int verts = ma.WorldVerticesLength / 2;
if (ma.Bones == null)
{
for (int i = 0; i < verts; i++)
res.Add(new object[] { new object[] { fallbackBone, 1f } });
return res;
}
int[] bs = ma.Bones;
float[] vs = ma.Vertices;
int vb = 0, vv = 0;
for (int i = 0; i < verts; i++)
{
int cnt = bs[vb++];
var list = new List<object>();
for (int k = 0; k < cnt; k++)
{
int bi = bs[vb++];
float x = vs[vv++], y = vs[vv++], w = vs[vv++];
list.Add(new object[] { bi, w });
}
res.Add(list);
}
return res;
}
}