Implement data structures and initial parser

This commit is contained in:
2026-03-04 20:21:19 +08:00
parent f30d41eaf1
commit 8d6428660e
4 changed files with 131 additions and 78 deletions
+26 -9
View File
@@ -10,16 +10,22 @@ public sealed class SplatData : ScriptableObject
public int SplatCount => PositionArray.Length;
public GraphicsBuffer PositionBuffer => GetCachedBuffers().position;
public GraphicsBuffer AxisBuffer => GetCachedBuffers().axis;
public GraphicsBuffer ScaleBuffer => GetCachedBuffers().scale;
public GraphicsBuffer RotationBuffer => GetCachedBuffers().rotation;
public GraphicsBuffer ColorBuffer => GetCachedBuffers().color;
public GraphicsBuffer OpacityBuffer => GetCachedBuffers().opacity;
public GraphicsBuffer SHBuffer => GetCachedBuffers().sh;
#endregion
#region Serialized data
[field:SerializeField] public Vector3[] PositionArray { get; set; }
[field:SerializeField] public Vector3[] AxisArray { get; set; }
[field:SerializeField] public Vector3[] ScaleArray { get; set; }
[field:SerializeField] public Quaternion[] RotationArray { get; set; }
[field:SerializeField] public Color[] ColorArray { get; set; }
[field:SerializeField] public float[] OpacityArray { get; set; }
[field:SerializeField] public float[][] SHArray { get; set; }
#endregion
@@ -28,32 +34,43 @@ public sealed class SplatData : ScriptableObject
public void ReleaseGpuResources()
{
_cachedBuffers.position?.Release();
_cachedBuffers.axis?.Release();
_cachedBuffers.scale?.Release();
_cachedBuffers.rotation?.Release();
_cachedBuffers.color?.Release();
_cachedBuffers = (null, null, null);
_cachedBuffers.opacity?.Release();
_cachedBuffers = (null, null, null, null, null, null);
}
#endregion
#region GPU resource management
(GraphicsBuffer position, GraphicsBuffer axis, GraphicsBuffer color)
(GraphicsBuffer position, GraphicsBuffer scale, GraphicsBuffer rotation,
GraphicsBuffer color, GraphicsBuffer opacity, GraphicsBuffer sh)
_cachedBuffers;
static unsafe GraphicsBuffer NewBuffer<T>(int count) where T : unmanaged
=> new GraphicsBuffer(GraphicsBuffer.Target.Structured, count, sizeof(T));
(GraphicsBuffer position, GraphicsBuffer axis, GraphicsBuffer color)
(GraphicsBuffer position, GraphicsBuffer scale, GraphicsBuffer rotation,
GraphicsBuffer color, GraphicsBuffer opacity, GraphicsBuffer sh)
GetCachedBuffers()
{
if (_cachedBuffers.position == null)
{
_cachedBuffers.position = NewBuffer<Vector3>(SplatCount);
_cachedBuffers.axis = NewBuffer<Vector3>(SplatCount * 3);
_cachedBuffers.scale = NewBuffer<Vector3>(SplatCount);
_cachedBuffers.rotation = NewBuffer<Quaternion>(SplatCount);
_cachedBuffers.color = NewBuffer<Color>(SplatCount);
_cachedBuffers.opacity = NewBuffer<float>(SplatCount);
_cachedBuffers.sh = NewBuffer<float>(SplatCount * 45);
_cachedBuffers.position.SetData(PositionArray);
_cachedBuffers.axis.SetData(AxisArray);
_cachedBuffers.scale.SetData(ScaleArray);
_cachedBuffers.rotation.SetData(RotationArray);
_cachedBuffers.color.SetData(ColorArray);
_cachedBuffers.opacity.SetData(OpacityArray);
_cachedBuffers.sh.SetData(SHArray);
}
return _cachedBuffers;
}
@@ -67,4 +84,4 @@ public sealed class SplatData : ScriptableObject
#endregion
}
} // namespace SplatVfx
}
+105 -69
View File
@@ -9,112 +9,148 @@ using UnityEditor.Experimental;
using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using UnityEditor.Rendering.LookDev;
using System.Linq;
namespace SplatVfx.Editor {
[ScriptedImporter(1, "splat"), BurstCompile]
[ScriptedImporter(1, "ply"), BurstCompile]
public sealed class SplatImporter : ScriptedImporter
{
#region ScriptedImporter implementation
public override void OnImportAsset(AssetImportContext context)
{
var data = ImportAsSplatData(context.assetPath);
var prefab = CreatePrefab(data);
context.AddObjectToAsset("prefab", prefab);
context.AddObjectToAsset("data", data);
context.SetMainObject(prefab);
_ = ParsePLYFile(context.assetPath);
// var data = ImportAsSplatData(context.assetPath);
// var prefab = CreatePrefab(data);
// context.AddObjectToAsset("prefab", prefab);
// context.AddObjectToAsset("data", data);
// context.SetMainObject(prefab);
}
#endregion
#region Prefab construction
const string DefaultVfxPath = "Packages/jp.keijiro.splat-vfx/VFX/Splat.vfx";
// const string DefaultVfxPath = "Packages/jp.keijiro.splat-vfx/VFX/Splat.vfx";
GameObject CreatePrefab(SplatData data)
{
var go = new GameObject();
// GameObject CreatePrefab(SplatData data)
// {
// var go = new GameObject();
var vfx = go.AddComponent<VisualEffect>();
vfx.visualEffectAsset = EditorResources.Load<VisualEffectAsset>(DefaultVfxPath);
// var vfx = go.AddComponent<VisualEffect>();
// vfx.visualEffectAsset = EditorResources.Load<VisualEffectAsset>(DefaultVfxPath);
var binderBase = go.AddComponent<VFXPropertyBinder>();
var binder = binderBase.AddPropertyBinder<VFXSplatDataBinder>();
binder.SplatData = data;
// var binderBase = go.AddComponent<VFXPropertyBinder>();
// var binder = binderBase.AddPropertyBinder<VFXSplatDataBinder>();
// binder.SplatData = data;
return go;
}
// return go;
// }
#endregion
#region Reader implementation
SplatData ImportAsSplatData(string path)
(string header, byte[] binaryData) ParsePLYFile(string filePath)
{
var data = ScriptableObject.CreateInstance<SplatData>();
data.name = Path.GetFileNameWithoutExtension(path);
using (var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read))
using (var reader = new StreamReader(fileStream))
{
StringBuilder headerBuilder = new StringBuilder();
string line;
var arrays = LoadDataArrays(path);
data.PositionArray = arrays.position;
data.AxisArray = arrays.axis;
data.ColorArray = arrays.color;
data.ReleaseGpuResources();
while ((line = reader.ReadLine()) != null)
{
headerBuilder.AppendLine(line);
if (line.Trim() == "end_header")
{
break;
}
}
return data;
long endHeaderPosition = headerBuilder.Length - headerBuilder.ToString().Split('\n').Length + 1;
fileStream.Seek(endHeaderPosition, SeekOrigin.Begin);
byte[] binaryData = new byte[fileStream.Length - endHeaderPosition];
return (headerBuilder.ToString(), binaryData);
}
}
#pragma warning disable CS0649
/*
get number of elements
struct ReadData
{
public float px, py, pz;
public float sx, sy, sz;
public byte r, g, b, a;
public byte rw, rx, ry, rz;
}
run down header, building list of data mappings
for element
for data mapping
write data
*/
#pragma warning restore CS0649
// SplatData ImportAsSplatData(string path)
// {
// var data = ScriptableObject.CreateInstance<SplatData>();
// data.name = Path.GetFileNameWithoutExtension(path);
(Vector3[] position, Vector3[] axis, Color[] color)
LoadDataArrays(string path)
{
var bytes = (Span<byte>)File.ReadAllBytes(path);
var count = bytes.Length / 32;
// var arrays = LoadDataArrays(path);
// data.PositionArray = arrays.position;
// data.AxisArray = arrays.axis;
// data.ColorArray = arrays.color;
// data.ReleaseGpuResources();
var source = MemoryMarshal.Cast<byte, ReadData>(bytes);
// return data;
// }
var position = new Vector3[count];
var axis = new Vector3[count * 3];
var color = new Color[count];
// struct ReadData
// {
// public float px, py, pz;
// public float sx, sy, sz;
// public byte r, g, b, a;
// public byte rw, rx, ry, rz;
// }
for (var i = 0; i < count; i++)
ParseReadData(source[i],
out position[i],
out axis[i * 3],
out axis[i * 3 + 1],
out axis[i * 3 + 2],
out color[i]);
// (Vector3[] position, Vector3[] axis, Color[] color)
// LoadDataArrays(string path)
// {
// var bytes = (Span<byte>)File.ReadAllBytes(path);
// var count = bytes.Length / 32;
return (position, axis, color);
}
// var source = MemoryMarshal.Cast<byte, ReadData>(bytes);
[BurstCompile]
void ParseReadData(in ReadData src,
out Vector3 position,
out Vector3 axis1,
out Vector3 axis2,
out Vector3 axis3,
out Color color)
{
var rv = (math.float4(src.rx, src.ry, src.rz, src.rw) - 128) / 128;
var q = math.quaternion(-rv.x, -rv.y, rv.z, rv.w);
position = math.float3(src.px, src.py, -src.pz);
axis1 = math.mul(q, math.float3(src.sx, 0, 0));
axis2 = math.mul(q, math.float3(0, src.sy, 0));
axis3 = math.mul(q, math.float3(0, 0, src.sz));
color = (Vector4)math.float4(src.r, src.g, src.b, src.a) / 255;
}
// var position = new Vector3[count];
// var axis = new Vector3[count * 3];
// var color = new Color[count];
// for (var i = 0; i < count; i++)
// ParseReadData(source[i],
// out position[i],
// out axis[i * 3],
// out axis[i * 3 + 1],
// out axis[i * 3 + 2],
// out color[i]);
// return (position, axis, color);
// }
// [BurstCompile]
// void ParseReadData(in ReadData src,
// out Vector3 position,
// out Vector3 axis1,
// out Vector3 axis2,
// out Vector3 axis3,
// out Color color)
// {
// var rv = (math.float4(src.rx, src.ry, src.rz, src.rw) - 128) / 128;
// var q = math.quaternion(-rv.x, -rv.y, rv.z, rv.w);
// position = math.float3(src.px, src.py, -src.pz);
// axis1 = math.mul(q, math.float3(src.sx, 0, 0));
// axis2 = math.mul(q, math.float3(0, src.sy, 0));
// axis3 = math.mul(q, math.float3(0, 0, src.sz));
// color = (Vector4)math.float4(src.r, src.g, src.b, src.a) / 255;
// }
#endregion
}