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