Local project added
This commit is contained in:
179
Assets/Scripts/VoxelOctree/GPU/OctreeGpuUpload.cs
Normal file
179
Assets/Scripts/VoxelOctree/GPU/OctreeGpuUpload.cs
Normal file
@ -0,0 +1,179 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
// Layout for the GPU buffer: must match HLSL struct exactly and size-aligned.
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct LinearNode
|
||||
{
|
||||
// ---- Bloc 1 (16 bytes)
|
||||
public float penetrationFactor; // 4
|
||||
public float reflexionFactor; // 4
|
||||
public uint childMask; // 4
|
||||
public int childBase; // 4
|
||||
|
||||
// ---- Bloc 7 (16 bytes)
|
||||
public uint isLeaf; // 4
|
||||
public uint isOccupied; // 4
|
||||
public uint pad0; // 4
|
||||
public uint pad1; // 4
|
||||
}
|
||||
|
||||
public struct LinearTree
|
||||
{
|
||||
public LinearNode[] nodes;
|
||||
public int rootIndex;
|
||||
}
|
||||
|
||||
public static class OctreeGpuHelpers
|
||||
{
|
||||
// Flatten the recursive octree into a linear array.
|
||||
// Returns LinearNode[] and root index (should be 0)
|
||||
|
||||
public static LinearTree FlattenOctree(OctreeNode root)
|
||||
{
|
||||
nodeToSig = new Dictionary<OctreeNode, string>(ReferenceEqualityComparer<OctreeNode>.Default);
|
||||
sigToIndex = new Dictionary<string, int>();
|
||||
|
||||
List<LinearNode> flatNodes = new List<LinearNode>();
|
||||
|
||||
flatNodes.Add(new LinearNode());
|
||||
|
||||
int rootIndex = BuildNode(root, flatNodes, 0);
|
||||
|
||||
LinearTree linearTree;
|
||||
linearTree.rootIndex = rootIndex;
|
||||
linearTree.nodes = flatNodes.ToArray();
|
||||
|
||||
nodeToSig = null;
|
||||
|
||||
return linearTree;
|
||||
}
|
||||
|
||||
private static Dictionary<OctreeNode, string> nodeToSig;
|
||||
private static Dictionary<string, int> sigToIndex;
|
||||
|
||||
private static string ComputeChildrenSignature(OctreeNode node)
|
||||
{
|
||||
// child signatures
|
||||
string[] childSigs = new string[8];
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
var c = node.children[i];
|
||||
childSigs[i] = c != null ? ComputeChildrenSignature(c) : "<null>";
|
||||
}
|
||||
|
||||
// Build signature: combine node properties + child signatures (deterministic)
|
||||
// Use StringBuilder then a hash (optional) — here we keep reasonable precision for floats
|
||||
var sb = new StringBuilder();
|
||||
sb.Append(node.isLeaf ? 'L' : 'N');
|
||||
sb.Append(node.isOccupied ? '1' : '0');
|
||||
sb.Append('|');
|
||||
sb.Append(node.penetrationFactor.ToString("R")); // "R" for round-trip
|
||||
sb.Append(',');
|
||||
sb.Append(node.reflexionFactor.ToString("R"));
|
||||
sb.Append('|');
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
sb.Append(childSigs[i]);
|
||||
sb.Append('|');
|
||||
}
|
||||
|
||||
string sig = sb.ToString();
|
||||
return sig;
|
||||
}
|
||||
|
||||
// PASS 1: compute a stable signature for each node (string here for simplicity)
|
||||
private static string ComputeSignature(OctreeNode node)
|
||||
{
|
||||
// child signatures
|
||||
string[] childSigs = new string[8];
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
var c = node.children[i];
|
||||
childSigs[i] = c != null ? ComputeChildrenSignature(c) : "<null>";
|
||||
}
|
||||
|
||||
// Build signature: combine node properties + child signatures (deterministic)
|
||||
// Use StringBuilder then a hash (optional) — here we keep reasonable precision for floats
|
||||
var sb = new StringBuilder();
|
||||
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
sb.Append(childSigs[i]);
|
||||
sb.Append('|');
|
||||
}
|
||||
|
||||
string sig = sb.ToString();
|
||||
return sig;
|
||||
}
|
||||
|
||||
// PASS 2: build nodes list — parent reserved first so unique children end up contiguous after parent.
|
||||
private static int BuildNode(OctreeNode node, List<LinearNode> nodes, int nodeIndex)
|
||||
{
|
||||
LinearNode ln = nodes[nodeIndex];
|
||||
|
||||
// Now fill the LinearNode with correct fields.
|
||||
ln.penetrationFactor = node.penetrationFactor;
|
||||
ln.reflexionFactor = node.reflexionFactor;
|
||||
ln.isLeaf = node.isLeaf ? 1u : 0u;
|
||||
ln.isOccupied = node.isOccupied ? 1u : 0u;
|
||||
ln.pad0 = 0;
|
||||
ln.pad1 = 0;
|
||||
ln.childMask = 0;
|
||||
|
||||
string sig = ComputeSignature(node);
|
||||
|
||||
if(sigToIndex.TryGetValue(sig, out int existingIndex))
|
||||
{
|
||||
ln.childBase = nodes[existingIndex].childBase;
|
||||
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
if (node.children[i] != null)
|
||||
ln.childMask |= (1u << i);
|
||||
}
|
||||
|
||||
nodes[nodeIndex] = ln;
|
||||
return nodeIndex;
|
||||
}
|
||||
|
||||
ln.childBase = nodes.Count;
|
||||
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
OctreeNode childNode = node.children[i];
|
||||
if (childNode != null)
|
||||
{
|
||||
nodes.Add(new LinearNode());
|
||||
}
|
||||
}
|
||||
|
||||
int realIndex = 0;
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
OctreeNode childNode = node.children[i];
|
||||
if (childNode != null)
|
||||
{
|
||||
int index = BuildNode(childNode, nodes, ln.childBase + realIndex);
|
||||
string childSig = ComputeSignature(childNode);
|
||||
sigToIndex[childSig] = index;
|
||||
|
||||
ln.childMask |= 1u << i;
|
||||
realIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
nodes[nodeIndex] = ln;
|
||||
|
||||
return nodeIndex;
|
||||
}
|
||||
|
||||
// Simple reference-equality comparer for OctreeNode (so Dictionary uses node identity)
|
||||
private class ReferenceEqualityComparer<T> : IEqualityComparer<T> where T : class
|
||||
{
|
||||
public static readonly ReferenceEqualityComparer<T> Default = new ReferenceEqualityComparer<T>();
|
||||
public bool Equals(T x, T y) => ReferenceEquals(x, y);
|
||||
public int GetHashCode(T obj) => System.Runtime.CompilerServices.RuntimeHelpers.GetHashCode(obj);
|
||||
}
|
||||
}
|
||||
2
Assets/Scripts/VoxelOctree/GPU/OctreeGpuUpload.cs.meta
Normal file
2
Assets/Scripts/VoxelOctree/GPU/OctreeGpuUpload.cs.meta
Normal file
@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d8d6d3db0d1de6a488ad343cfc26da8d
|
||||
60
Assets/Scripts/VoxelOctree/GPU/VoxelRaycastGPU.cs
Normal file
60
Assets/Scripts/VoxelOctree/GPU/VoxelRaycastGPU.cs
Normal file
@ -0,0 +1,60 @@
|
||||
using UnityEngine;
|
||||
|
||||
public class VoxelRaycastGPU : MonoBehaviour
|
||||
{
|
||||
public VoxelRaycastGPU(ComputeShader computeShader)
|
||||
{
|
||||
raycastShader = computeShader;
|
||||
}
|
||||
|
||||
public ComputeShader raycastShader;
|
||||
|
||||
public struct Ray
|
||||
{
|
||||
public float pad;
|
||||
public Vector3 direction;
|
||||
}
|
||||
|
||||
public struct BatchData
|
||||
{
|
||||
public Vector3 origin;
|
||||
public float maxDistance;
|
||||
};
|
||||
|
||||
public struct Hit
|
||||
{
|
||||
public float penetrationFactor;
|
||||
public float reflexionFactor;
|
||||
public float lastDistance;
|
||||
private float _pad1;
|
||||
public Vector3 origin;
|
||||
private float _pad2;
|
||||
public Vector3 position;
|
||||
private float _pad3;
|
||||
public float distance;
|
||||
public uint hit;
|
||||
private float _pad4;
|
||||
private float _pad5;
|
||||
}
|
||||
|
||||
public void Init( ComputeShader computeShader )
|
||||
{
|
||||
raycastShader = computeShader;
|
||||
}
|
||||
|
||||
public void Raycast( in Ray[] rays, float maxDistance, ref ComputeBuffer rayBuffer, ref ComputeBuffer hitBuffer )
|
||||
{
|
||||
int kernel = raycastShader.FindKernel("CSMain");
|
||||
|
||||
rayBuffer.SetData(rays);
|
||||
raycastShader.SetBuffer(kernel, "rays", rayBuffer);
|
||||
raycastShader.SetBuffer(kernel, "hits", hitBuffer);
|
||||
raycastShader.SetFloat("maxDistance", maxDistance);
|
||||
|
||||
int threadGroups = Mathf.CeilToInt(rays.Length / 64f);
|
||||
raycastShader.Dispatch(kernel, threadGroups, 1, 1);
|
||||
|
||||
Hit[] hits = new Hit[rays.Length];
|
||||
hitBuffer.GetData(hits);
|
||||
}
|
||||
}
|
||||
2
Assets/Scripts/VoxelOctree/GPU/VoxelRaycastGPU.cs.meta
Normal file
2
Assets/Scripts/VoxelOctree/GPU/VoxelRaycastGPU.cs.meta
Normal file
@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 46218eff2d8f9a6489444052fb6a7ef6
|
||||
142
Assets/Scripts/VoxelOctree/GPU/VoxelRaycastGpuManager.cs
Normal file
142
Assets/Scripts/VoxelOctree/GPU/VoxelRaycastGpuManager.cs
Normal file
@ -0,0 +1,142 @@
|
||||
using UnityEngine;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
public class VoxelRaycastGpuManager
|
||||
{
|
||||
ComputeShader raycastShader;
|
||||
OctreeNode root; // assign your built octree root
|
||||
public VoxelRaycastGpuManager(ComputeShader computeShader, OctreeNode octreeRoot)
|
||||
{
|
||||
raycastShader = computeShader;
|
||||
root = octreeRoot;
|
||||
}
|
||||
|
||||
ComputeBuffer nodeBuffer;
|
||||
|
||||
public LinearTree linearTree;
|
||||
|
||||
int kernel;
|
||||
|
||||
ComputeBuffer hitCounterBuffer = null;
|
||||
ComputeBuffer rayBuffer = null;
|
||||
|
||||
int raysPerBatch;
|
||||
|
||||
int batchDataClassSize = Marshal.SizeOf(typeof(VoxelRaycastGPU.BatchData));
|
||||
|
||||
int groupsX;
|
||||
|
||||
int maxRaycastPerIteration;
|
||||
|
||||
|
||||
public VoxelRaycastGPU.BatchData[] Raycast(in VoxelRaycastGPU.BatchData[] batchData, int datasLenght)
|
||||
{
|
||||
ComputeBuffer hitBuffer = new ComputeBuffer(5000, batchDataClassSize, ComputeBufferType.Append);
|
||||
ComputeBuffer datasBuffer = new ComputeBuffer(datasLenght, batchDataClassSize, ComputeBufferType.Default);
|
||||
ComputeBuffer countBuffer = new ComputeBuffer(1, sizeof(int), ComputeBufferType.Raw);
|
||||
|
||||
int iteration = 0;
|
||||
int currentCount = datasLenght;
|
||||
int previousCount = datasLenght;
|
||||
|
||||
datasBuffer.SetData(batchData, 0, 0, currentCount);
|
||||
|
||||
while (iteration < 4 && currentCount > 0)
|
||||
{
|
||||
previousCount = currentCount;
|
||||
|
||||
raycastShader.SetBuffer(kernel, "batchDatas", datasBuffer);
|
||||
raycastShader.SetBuffer(kernel, "hits", hitBuffer);
|
||||
|
||||
int threadsY = 8;
|
||||
|
||||
int groupsY = Mathf.CeilToInt((float)currentCount / threadsY);
|
||||
|
||||
raycastShader.Dispatch(kernel, groupsX, groupsY, 1);
|
||||
|
||||
ComputeBuffer.CopyCount(hitBuffer, countBuffer, 0);
|
||||
int[] countArr = new int[1];
|
||||
countBuffer.GetData(countArr);
|
||||
currentCount = countArr[0];
|
||||
|
||||
/*VoxelRaycastGPU.BatchData[] hits = new VoxelRaycastGPU.BatchData[currentCount];
|
||||
hitBuffer.GetData(hits, 0, 0, currentCount);
|
||||
|
||||
for (int i = 0; i < currentCount; i++)
|
||||
{
|
||||
GameObject sphere = GameObject.CreatePrimitive(PrimitiveType.Sphere);
|
||||
sphere.transform.position = hits[i].origin;
|
||||
sphere.transform.localScale = Vector3.one * 0.5f;
|
||||
}*/
|
||||
|
||||
if (currentCount > 0)
|
||||
{
|
||||
(datasBuffer, hitBuffer) = (hitBuffer, datasBuffer);
|
||||
|
||||
hitBuffer.Release();
|
||||
hitBuffer = new ComputeBuffer(5000, batchDataClassSize, ComputeBufferType.Append);
|
||||
}
|
||||
|
||||
iteration++;
|
||||
}
|
||||
|
||||
VoxelRaycastGPU.BatchData[] result = new VoxelRaycastGPU.BatchData[previousCount];
|
||||
if (currentCount == 0)
|
||||
datasBuffer.GetData(result, 0, 0, previousCount);
|
||||
else
|
||||
hitBuffer.GetData(result, 0, 0, previousCount);
|
||||
|
||||
hitBuffer.Release();
|
||||
datasBuffer.Release();
|
||||
countBuffer.Release();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public void Init( int nbRaysPerBatch, in VoxelRaycastGPU.Ray[] rays )
|
||||
{
|
||||
// Flatten octree
|
||||
linearTree = OctreeGpuHelpers.FlattenOctree(root);
|
||||
int nodeStride = Marshal.SizeOf(typeof(LinearNode)); // should be 64
|
||||
|
||||
rayBuffer = new ComputeBuffer(rays.Length, Marshal.SizeOf(typeof(VoxelRaycastGPU.Ray)), ComputeBufferType.Default);
|
||||
rayBuffer.SetData(rays, 0, 0, rays.Length);
|
||||
|
||||
// Create GPU buffer for nodes
|
||||
nodeBuffer = new ComputeBuffer(linearTree.nodes.Length, nodeStride, ComputeBufferType.Default);
|
||||
nodeBuffer.SetData(linearTree.nodes);
|
||||
|
||||
hitCounterBuffer = new ComputeBuffer(1, sizeof(int), ComputeBufferType.Raw);
|
||||
uint[] counterInit = { 0 };
|
||||
counterInit[0] = 0;
|
||||
hitCounterBuffer.SetData(counterInit);
|
||||
|
||||
kernel = raycastShader.FindKernel("CSMain");
|
||||
|
||||
raycastShader.SetBuffer(kernel, "nodes", nodeBuffer);
|
||||
raycastShader.SetBuffer(kernel, "hitCount", hitCounterBuffer);
|
||||
raycastShader.SetBuffer(kernel, "rays", rayBuffer);
|
||||
|
||||
raycastShader.SetInt("raysPerBatch", nbRaysPerBatch);
|
||||
raycastShader.SetInt("rootIndex", linearTree.rootIndex);
|
||||
raycastShader.SetInt("nodeCount", linearTree.nodes.Length);
|
||||
|
||||
raycastShader.SetFloat("rootHalfSize", root.bounds.size.x / 2f);
|
||||
raycastShader.SetFloats("rootCenter", new float[3] { root.bounds.center.x, root.bounds.center.y, root.bounds.center.z });
|
||||
|
||||
raysPerBatch = nbRaysPerBatch;
|
||||
|
||||
groupsX = Mathf.CeilToInt((float)raysPerBatch / 8);
|
||||
|
||||
maxRaycastPerIteration = 5000 / raysPerBatch;
|
||||
}
|
||||
|
||||
~VoxelRaycastGpuManager()
|
||||
{
|
||||
if (hitCounterBuffer != null)
|
||||
hitCounterBuffer.Release();
|
||||
|
||||
if (rayBuffer != null)
|
||||
rayBuffer.Release();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f13f71d059d1b854bb51ac64bede9396
|
||||
Reference in New Issue
Block a user