Local project added
This commit is contained in:
6
Assets/Scripts/AudioObject.cs
Normal file
6
Assets/Scripts/AudioObject.cs
Normal file
@ -0,0 +1,6 @@
|
||||
using UnityEngine;
|
||||
|
||||
public class AudioObject : MonoBehaviour
|
||||
{
|
||||
public AudioMaterial audioMaterialSettings;
|
||||
}
|
||||
2
Assets/Scripts/AudioObject.cs.meta
Normal file
2
Assets/Scripts/AudioObject.cs.meta
Normal file
@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c0d5c94b23fe7cf40a75a538e5abe377
|
||||
8
Assets/Scripts/Debug.meta
Normal file
8
Assets/Scripts/Debug.meta
Normal file
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4c021777962f9e642b6e423c5ebe2331
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
102
Assets/Scripts/Debug/FlattenVoxelTreeDebugger.cs
Normal file
102
Assets/Scripts/Debug/FlattenVoxelTreeDebugger.cs
Normal file
@ -0,0 +1,102 @@
|
||||
using UnityEngine;
|
||||
|
||||
public class FlattenVoxelTreeDebugger : MonoBehaviour
|
||||
{
|
||||
public bool enableDraw = false;
|
||||
|
||||
[Header("Voxel Tree Data")]
|
||||
public ComputeBuffer nodeBuffer; // buffer GPU (optionnel)
|
||||
public LinearNode[] nodes; // ou data CPU (si tu l’as)
|
||||
public Vector3 rootCenter = Vector3.zero;
|
||||
public float rootHalfSize = 100f;
|
||||
public bool showOnlyLeaves = false;
|
||||
public bool useGPUBuffer = false;
|
||||
|
||||
[Header("Display Settings")]
|
||||
public Color nodeColor = new Color(0f, 1f, 0f, 0.05f);
|
||||
public Color leafColor = new Color(1f, 0f, 0f, 0.15f);
|
||||
public Color borderColor = Color.yellow;
|
||||
public int maxDepthToDraw = 6;
|
||||
public VoxelTreeManager voxelTreeManager;
|
||||
|
||||
|
||||
private void OnDrawGizmos()
|
||||
{
|
||||
if (enableDraw == false)
|
||||
return;
|
||||
|
||||
if (nodes == null && voxelTreeManager.gpuRayCaster != null )
|
||||
{
|
||||
nodes = voxelTreeManager.gpuRayCaster.linearTree.nodes;
|
||||
}
|
||||
|
||||
if (nodes == null || nodes.Length == 0)
|
||||
return;
|
||||
|
||||
Gizmos.matrix = Matrix4x4.identity;
|
||||
DrawNodeRecursive(0, rootCenter, rootHalfSize, 0);
|
||||
}
|
||||
|
||||
private void DrawNodeRecursive(int nodeIndex, Vector3 center, float halfSize, int depth)
|
||||
{
|
||||
if (nodeIndex < 0 || nodeIndex >= nodes.Length) return;
|
||||
if (depth > maxDepthToDraw) return;
|
||||
|
||||
var node = nodes[nodeIndex];
|
||||
|
||||
bool isLeaf = node.isLeaf == 1;
|
||||
bool isOccupied = node.isOccupied == 1;
|
||||
|
||||
// Color based on node type
|
||||
if (isOccupied)
|
||||
Gizmos.color = leafColor;
|
||||
else
|
||||
Gizmos.color = nodeColor;
|
||||
|
||||
if (!showOnlyLeaves || isLeaf)
|
||||
{
|
||||
Gizmos.DrawCube(center, Vector3.one * (halfSize * 2f));
|
||||
Gizmos.color = borderColor;
|
||||
Gizmos.DrawWireCube(center, Vector3.one * (halfSize * 2f));
|
||||
}
|
||||
|
||||
if (node.childMask != 0)
|
||||
{
|
||||
float childHalf = halfSize * 0.5f;
|
||||
|
||||
int realIndex = 0;
|
||||
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
bool childExists = (node.childMask & (1u << i)) != 0 ;
|
||||
if (!childExists) continue;
|
||||
|
||||
Vector3 offset = childHalf * new Vector3(
|
||||
(i & 4) != 0 ? 1f : -1f, // X
|
||||
(i & 2) != 0 ? 1f : -1f, // Y
|
||||
(i & 1) != 0 ? 1f : -1f // Z
|
||||
);
|
||||
|
||||
Vector3 childCenter = center + offset;
|
||||
|
||||
int childIndex = node.childBase + realIndex;
|
||||
|
||||
DrawNodeRecursive(childIndex, childCenter, childHalf, depth + 1);
|
||||
|
||||
realIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
// Optionnel : utilitaire pour extraire depuis un ComputeBuffer (GPU -> CPU)
|
||||
public void SyncFromGPU()
|
||||
{
|
||||
if (nodeBuffer == null || !useGPUBuffer) return;
|
||||
if (nodes == null || nodes.Length == 0)
|
||||
nodes = new LinearNode[nodeBuffer.count];
|
||||
|
||||
nodeBuffer.GetData(nodes);
|
||||
}
|
||||
}
|
||||
2
Assets/Scripts/Debug/FlattenVoxelTreeDebugger.cs.meta
Normal file
2
Assets/Scripts/Debug/FlattenVoxelTreeDebugger.cs.meta
Normal file
@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7af577c689fa40b4a9291ff83dbde469
|
||||
154
Assets/Scripts/Debug/Player.cs
Normal file
154
Assets/Scripts/Debug/Player.cs
Normal file
@ -0,0 +1,154 @@
|
||||
using UnityEngine;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Collections.Generic;
|
||||
|
||||
|
||||
public class Player : MonoBehaviour
|
||||
{
|
||||
public VoxelTreeManager voxelManager;
|
||||
|
||||
public bool EnableDebug = false;
|
||||
|
||||
int totalCastDone = 0;
|
||||
|
||||
public float longitudeStep = 45f;
|
||||
public float lateralStep = 45f;
|
||||
|
||||
int rayCount;
|
||||
|
||||
// Start is called once before the first execution of Update after the MonoBehaviour is created
|
||||
void Start()
|
||||
{
|
||||
InitPrebuildArray();
|
||||
|
||||
VoxelRaycastGPU.Ray[] rays = new VoxelRaycastGPU.Ray[rayCount];
|
||||
FillRaysArray(rays);
|
||||
voxelManager.gpuRayCaster.Init(rayCount, rays);
|
||||
}
|
||||
|
||||
void Cast( ref VoxelRaycastGPU.BatchData[] batchData, int batchCount, int iIteration )
|
||||
{
|
||||
ComputeBuffer hitBuffer = new ComputeBuffer(rayCount * batchCount, Marshal.SizeOf(typeof(VoxelRaycastGPU.Hit)), ComputeBufferType.Append);
|
||||
|
||||
Stopwatch sw = Stopwatch.StartNew();
|
||||
totalCastDone += batchCount * rayCount;
|
||||
VoxelRaycastGPU.BatchData[] hits = voxelManager.CastGpuRay(in batchData, batchCount);
|
||||
|
||||
/*for( int i = 0; i < hits.Length; i++ )
|
||||
{
|
||||
GameObject sphere = GameObject.CreatePrimitive(PrimitiveType.Sphere);
|
||||
sphere.transform.position = hits[i].origin;
|
||||
sphere.transform.localScale = Vector3.one * 0.5f;
|
||||
}*/
|
||||
|
||||
sw.Stop();
|
||||
}
|
||||
|
||||
Vector3[] prebuildArrayDirection;
|
||||
|
||||
void InitPrebuildArray()
|
||||
{
|
||||
var dirs = new List<Vector3>();
|
||||
|
||||
const float EPS = 1e-6f;
|
||||
|
||||
// Elevation (longitude in your naming): from -90 to +90 inclusive
|
||||
for (float lonDeg = -90f; lonDeg <= 90f + 1e-6f; lonDeg += longitudeStep)
|
||||
{
|
||||
float lonRad = lonDeg * Mathf.Deg2Rad;
|
||||
float cosLon = Mathf.Cos(lonRad);
|
||||
float sinLon = Mathf.Sin(lonRad);
|
||||
|
||||
// If cosLon is ~0, we are at a pole: all azimuths collapse to the same vector.
|
||||
if (Mathf.Abs(cosLon) < EPS)
|
||||
{
|
||||
// Add a single pole direction (north or south)
|
||||
dirs.Add(new Vector3(0f, sinLon, 0f));
|
||||
continue; // skip azimuth loop for this elevation
|
||||
}
|
||||
|
||||
// Otherwise loop over azimuth (lateral)
|
||||
for (float latDeg = 0f; latDeg < 360f - 1e-6f; latDeg += lateralStep)
|
||||
{
|
||||
float latRad = latDeg * Mathf.Deg2Rad;
|
||||
float cosLat = Mathf.Cos(latRad);
|
||||
float sinLat = Mathf.Sin(latRad);
|
||||
|
||||
Vector3 dir = new Vector3(
|
||||
cosLon * cosLat,
|
||||
sinLon,
|
||||
cosLon * sinLat
|
||||
);
|
||||
|
||||
dirs.Add(dir.normalized);
|
||||
}
|
||||
}
|
||||
|
||||
prebuildArrayDirection = dirs.ToArray();
|
||||
rayCount = dirs.Count;
|
||||
}
|
||||
|
||||
void FillRaysArray( VoxelRaycastGPU.Ray[] rays )
|
||||
{
|
||||
for (int i = 0; i < prebuildArrayDirection.Length; i++)
|
||||
{
|
||||
rays[i].direction = prebuildArrayDirection[i];
|
||||
}
|
||||
}
|
||||
|
||||
// Update is called once per frame
|
||||
void Update()
|
||||
{
|
||||
if (Input.GetKeyDown(KeyCode.R))
|
||||
{
|
||||
//VoxelTreeRaycaster.HitInfo outHit;
|
||||
|
||||
Vector3 origin = GetComponent<Transform>().position;
|
||||
|
||||
UnityEngine.Debug.Log(origin);
|
||||
|
||||
//int iNbCast = 0;
|
||||
|
||||
/*Stopwatch sw = Stopwatch.StartNew();
|
||||
|
||||
for (float el = -90f; el <= 90f; el += elStep)
|
||||
{
|
||||
for (float az = 0f; az < 360f; az += azStep)
|
||||
{
|
||||
float elRad = el * Mathf.Deg2Rad;
|
||||
float azRad = az * Mathf.Deg2Rad;
|
||||
|
||||
Vector3 dir = new Vector3(
|
||||
Mathf.Cos(elRad) * Mathf.Cos(azRad),
|
||||
Mathf.Sin(elRad),
|
||||
Mathf.Cos(elRad) * Mathf.Sin(azRad)
|
||||
).normalized;
|
||||
|
||||
iNbCast++;
|
||||
bool hit = voxelManager.CastRay(origin, dir, 100f, out outHit);
|
||||
}
|
||||
}
|
||||
|
||||
sw.Stop();
|
||||
|
||||
UnityEngine.Debug.Log($"RayCast done in {sw.Elapsed.TotalMilliseconds}ms for {iNbCast} casts");*/
|
||||
|
||||
Stopwatch sw = Stopwatch.StartNew();
|
||||
|
||||
totalCastDone = 0;
|
||||
|
||||
VoxelRaycastGPU.Ray[] rays = new VoxelRaycastGPU.Ray[rayCount];
|
||||
VoxelRaycastGPU.BatchData[] batchDatas = new VoxelRaycastGPU.BatchData[1];
|
||||
|
||||
batchDatas[0].origin = origin;
|
||||
batchDatas[0].maxDistance = 100;
|
||||
|
||||
Cast(ref batchDatas, 1, 0);
|
||||
sw.Stop();
|
||||
|
||||
UnityEngine.Debug.Log($"Gpu RayCast done in {sw.Elapsed.TotalMilliseconds}ms for {totalCastDone} casts");
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
2
Assets/Scripts/Debug/Player.cs.meta
Normal file
2
Assets/Scripts/Debug/Player.cs.meta
Normal file
@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 98b63d0a77a360e4d94b27528bc3000d
|
||||
52
Assets/Scripts/Debug/VoxelTreeDebugger.cs
Normal file
52
Assets/Scripts/Debug/VoxelTreeDebugger.cs
Normal file
@ -0,0 +1,52 @@
|
||||
using UnityEngine;
|
||||
|
||||
[ExecuteAlways]
|
||||
public class VoxelTreeDebugger : MonoBehaviour
|
||||
{
|
||||
public bool enableDraw = false;
|
||||
|
||||
[Header("Tree reference")]
|
||||
public OctreeNode root; // Reference to your voxel tree root
|
||||
|
||||
[Header("Debug settings")]
|
||||
public bool drawLeavesOnly = true; // Draw only leaf nodes
|
||||
public int maxDepthToDraw = 6; // Limit drawing depth
|
||||
public Color emptyColor = new Color(0f, 0.5f, 1f, 0.2f);
|
||||
public Color solidColor = new Color(1f, 0f, 0f, 0.5f);
|
||||
public Color mixedColor = new Color(1f, 1f, 0f, 0.4f);
|
||||
|
||||
void OnDrawGizmos()
|
||||
{
|
||||
if (root != null && enableDraw)
|
||||
{
|
||||
DrawNode(root, 0);
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawNode(OctreeNode node, int depth)
|
||||
{
|
||||
if (node == null || depth > maxDepthToDraw)
|
||||
return;
|
||||
|
||||
// Set color based on node type
|
||||
if (node.isLeaf)
|
||||
{
|
||||
Gizmos.color = node.isOccupied ? solidColor : emptyColor;
|
||||
Gizmos.DrawWireCube(node.bounds.center, node.bounds.size);
|
||||
if (node.isOccupied)
|
||||
Gizmos.DrawCube(node.bounds.center, node.bounds.size * 0.95f);
|
||||
}
|
||||
else
|
||||
{
|
||||
Gizmos.color = mixedColor;
|
||||
if (!drawLeavesOnly)
|
||||
Gizmos.DrawWireCube(node.bounds.center, node.bounds.size);
|
||||
|
||||
// Recursively draw children
|
||||
foreach (var child in node.children)
|
||||
{
|
||||
DrawNode(child, depth + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
2
Assets/Scripts/Debug/VoxelTreeDebugger.cs.meta
Normal file
2
Assets/Scripts/Debug/VoxelTreeDebugger.cs.meta
Normal file
@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 31dcad4d65772e349b756df7096c55d0
|
||||
8
Assets/Scripts/VoxelOctree.meta
Normal file
8
Assets/Scripts/VoxelOctree.meta
Normal file
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 54c344627e0b6a1488c3c7d6c6d49bb5
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/Scripts/VoxelOctree/CPU.meta
Normal file
8
Assets/Scripts/VoxelOctree/CPU.meta
Normal file
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3ac02403714fd184095cb0c32517d4b0
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
38
Assets/Scripts/VoxelOctree/CPU/OctreeNode.cs
Normal file
38
Assets/Scripts/VoxelOctree/CPU/OctreeNode.cs
Normal file
@ -0,0 +1,38 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
|
||||
public class OctreeNode
|
||||
{
|
||||
public Bounds bounds; // The 3D space covered by this node
|
||||
public bool isLeaf; // True if this node is not subdivided
|
||||
public bool isOccupied; // True if this node contains solid voxels
|
||||
public bool hasChildrenOccupied; // True if this node contains solid voxels
|
||||
public OctreeNode[] children; // 8 sub-nodes (for each octant)
|
||||
|
||||
public float penetrationFactor;
|
||||
public float reflexionFactor;
|
||||
|
||||
public OctreeNode(Bounds bounds)
|
||||
{
|
||||
penetrationFactor = 1;
|
||||
reflexionFactor = 1;
|
||||
this.bounds = bounds;
|
||||
isLeaf = true;
|
||||
isOccupied = false;
|
||||
hasChildrenOccupied = false;
|
||||
|
||||
children = new OctreeNode[8];
|
||||
|
||||
children[0] = null;
|
||||
children[1] = null;
|
||||
children[2] = null;
|
||||
children[3] = null;
|
||||
children[4] = null;
|
||||
children[5] = null;
|
||||
children[6] = null;
|
||||
children[7] = null;
|
||||
|
||||
}
|
||||
}
|
||||
2
Assets/Scripts/VoxelOctree/CPU/OctreeNode.cs.meta
Normal file
2
Assets/Scripts/VoxelOctree/CPU/OctreeNode.cs.meta
Normal file
@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 13827c41731283f4bb6202cac32e51ab
|
||||
101
Assets/Scripts/VoxelOctree/CPU/VoxelTreeRaycaster.cs
Normal file
101
Assets/Scripts/VoxelOctree/CPU/VoxelTreeRaycaster.cs
Normal file
@ -0,0 +1,101 @@
|
||||
using UnityEngine;
|
||||
|
||||
public class VoxelTreeRaycaster
|
||||
{
|
||||
public struct HitInfo
|
||||
{
|
||||
public Vector3 point;
|
||||
public Vector3 normal;
|
||||
public float distance;
|
||||
public OctreeNode node;
|
||||
}
|
||||
|
||||
// Public API: perform a raycast through the voxel tree
|
||||
public bool Raycast(OctreeNode root, Vector3 origin, Vector3 direction, float maxDistance, out HitInfo hit)
|
||||
{
|
||||
hit = new HitInfo();
|
||||
if (root == null || direction == Vector3.zero)
|
||||
return false;
|
||||
|
||||
direction.Normalize();
|
||||
Bounds treeBounds = root.bounds;
|
||||
|
||||
// First, check if ray starts inside or intersects the tree bounds
|
||||
if (!treeBounds.IntersectRay(new Ray(origin, direction), out float entryDist))
|
||||
{
|
||||
// If outside, skip rays that miss the whole tree
|
||||
return false;
|
||||
}
|
||||
|
||||
// Clamp to max distance
|
||||
float dist = Mathf.Max(0f, entryDist);
|
||||
float endDist = Mathf.Min(maxDistance, entryDist + maxDistance);
|
||||
|
||||
return Traverse(root, origin, direction, ref dist, endDist, out hit);
|
||||
}
|
||||
|
||||
// Recursive traversal
|
||||
private bool Traverse(OctreeNode node, Vector3 origin, Vector3 dir, ref float dist, float maxDist, out HitInfo hit)
|
||||
{
|
||||
hit = new HitInfo();
|
||||
|
||||
// Stop if beyond range or node is outside of ray segment
|
||||
if (dist > maxDist) return false;
|
||||
if (!node.bounds.IntersectRay(new Ray(origin, dir), out float entry))
|
||||
return false;
|
||||
|
||||
// Adjust distance to this node's entry point
|
||||
dist = Mathf.Max(dist, entry);
|
||||
|
||||
// Leaf node
|
||||
if (node.isLeaf)
|
||||
{
|
||||
if (node.isOccupied)
|
||||
{
|
||||
// Approximate hit point (entry point)
|
||||
hit.point = origin + dir * dist;
|
||||
hit.distance = dist;
|
||||
hit.normal = GetApproximateNormal(node, origin, dir);
|
||||
hit.node = node;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Otherwise, traverse children
|
||||
if (node.children != null)
|
||||
{
|
||||
// Check all child nodes that intersect the ray
|
||||
foreach (var child in node.children)
|
||||
{
|
||||
if (child == null) continue;
|
||||
if (Traverse(child, origin, dir, ref dist, maxDist, out hit))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Approximate normal from cube face hit
|
||||
private Vector3 GetApproximateNormal(OctreeNode node, Vector3 origin, Vector3 dir)
|
||||
{
|
||||
Vector3 p = origin;
|
||||
Bounds b = node.bounds;
|
||||
Vector3 center = b.center;
|
||||
Vector3 extents = b.extents;
|
||||
|
||||
Vector3 local = p - center;
|
||||
Vector3 absDir = new Vector3(Mathf.Abs(dir.x), Mathf.Abs(dir.y), Mathf.Abs(dir.z));
|
||||
Vector3 normal = Vector3.zero;
|
||||
|
||||
if (absDir.x > absDir.y && absDir.x > absDir.z)
|
||||
normal = new Vector3(Mathf.Sign(-dir.x), 0, 0);
|
||||
else if (absDir.y > absDir.z)
|
||||
normal = new Vector3(0, Mathf.Sign(-dir.y), 0);
|
||||
else
|
||||
normal = new Vector3(0, 0, Mathf.Sign(-dir.z));
|
||||
|
||||
return normal;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 968c451685e18e54a9cb1d419f303c54
|
||||
8
Assets/Scripts/VoxelOctree/GPU.meta
Normal file
8
Assets/Scripts/VoxelOctree/GPU.meta
Normal file
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 11755919bdf10884492adbb91aa6837b
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
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
|
||||
128
Assets/Scripts/VoxelOctree/VoxelTreeBuilder.cs
Normal file
128
Assets/Scripts/VoxelOctree/VoxelTreeBuilder.cs
Normal file
@ -0,0 +1,128 @@
|
||||
using UnityEngine;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
public class VoxelTreeBuilder
|
||||
{
|
||||
private int maxDepth;
|
||||
private float minNodeSize;
|
||||
|
||||
public VoxelTreeBuilder(int maxDepth, float minNodeSize)
|
||||
{
|
||||
this.maxDepth = maxDepth;
|
||||
this.minNodeSize = minNodeSize;
|
||||
}
|
||||
|
||||
public OctreeNode Build(Bounds bounds, Func<Vector3, float, IsSolidNodeAnswere> isSolid)
|
||||
{
|
||||
return BuildNode(bounds, isSolid, 0);
|
||||
}
|
||||
|
||||
private OctreeNode BuildNode(Bounds bounds, Func<Vector3, float, IsSolidNodeAnswere> isSolid, int depth)
|
||||
{
|
||||
OctreeNode node = new OctreeNode(bounds);
|
||||
IsSolidNodeAnswere answere;
|
||||
|
||||
// Stop subdivision if we reached the maximum depth or smallest size
|
||||
if (depth >= maxDepth)
|
||||
{
|
||||
// Check the voxel state at the center
|
||||
answere = isSolid(bounds.center, bounds.size.x);
|
||||
node.isOccupied = answere.isSolid;
|
||||
node.penetrationFactor = answere.penetrationFactor;
|
||||
node.reflexionFactor = answere.reflexionFactor;
|
||||
return node;
|
||||
}
|
||||
|
||||
if( bounds.size.x <= minNodeSize )
|
||||
{
|
||||
bool allSolid = true;
|
||||
bool allEmpty = true;
|
||||
|
||||
foreach (Vector3 corner in GetCorners(bounds))
|
||||
{
|
||||
answere = isSolid(corner, bounds.size.x);
|
||||
bool solid = answere.isSolid;
|
||||
|
||||
node.penetrationFactor = answere.penetrationFactor;
|
||||
node.reflexionFactor = answere.reflexionFactor;
|
||||
|
||||
allSolid &= solid;
|
||||
allEmpty &= !solid;
|
||||
}
|
||||
|
||||
// Check if the entire cube is homogeneous (either fully solid or empty)
|
||||
if (depth >= maxDepth || allSolid)
|
||||
{
|
||||
// If homogeneous, stop subdivision
|
||||
if (allSolid || allEmpty)
|
||||
{
|
||||
if (allSolid)
|
||||
{
|
||||
answere = isSolid(bounds.center, bounds.size.x);
|
||||
node.isOccupied = answere.isSolid;
|
||||
node.penetrationFactor = answere.penetrationFactor;
|
||||
node.reflexionFactor = answere.reflexionFactor;
|
||||
node.isOccupied = allSolid;
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise, subdivide into 8 child nodes
|
||||
node.isLeaf = false;
|
||||
int arrayIndex = 0;
|
||||
|
||||
foreach (var (i, subBounds) in GetSubBounds(bounds).Select((b, i) => (i, b)))
|
||||
{
|
||||
OctreeNode childrenNode = BuildNode(subBounds, isSolid, depth + 1);
|
||||
|
||||
if (childrenNode.isOccupied == true || childrenNode.hasChildrenOccupied == true)
|
||||
{
|
||||
node.hasChildrenOccupied = true;
|
||||
node.children[arrayIndex] = childrenNode;
|
||||
}
|
||||
else
|
||||
{
|
||||
node.children[arrayIndex] = null;
|
||||
}
|
||||
arrayIndex++;
|
||||
}
|
||||
|
||||
if (arrayIndex == 0)
|
||||
node.isLeaf = true;
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
private IEnumerable<Bounds> GetSubBounds(Bounds parent)
|
||||
{
|
||||
// Generate 8 sub-bounds for the octree subdivision
|
||||
Vector3 size = parent.size / 2f;
|
||||
Vector3 min = parent.min;
|
||||
for (int x = 0; x < 2; x++)
|
||||
for (int y = 0; y < 2; y++)
|
||||
for (int z = 0; z < 2; z++)
|
||||
{
|
||||
Vector3 center = min + Vector3.Scale(new Vector3(x + 0.5f, y + 0.5f, z + 0.5f), size);
|
||||
yield return new Bounds(center, size);
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerable<Vector3> GetCorners(Bounds b)
|
||||
{
|
||||
// Return all 8 corners of the bounding box
|
||||
Vector3 min = b.min;
|
||||
Vector3 max = b.max;
|
||||
for (int x = 0; x <= 1; x++)
|
||||
for (int y = 0; y <= 1; y++)
|
||||
for (int z = 0; z <= 1; z++)
|
||||
yield return new Vector3(
|
||||
x == 0 ? min.x : max.x,
|
||||
y == 0 ? min.y : max.y,
|
||||
z == 0 ? min.z : max.z);
|
||||
}
|
||||
}
|
||||
2
Assets/Scripts/VoxelOctree/VoxelTreeBuilder.cs.meta
Normal file
2
Assets/Scripts/VoxelOctree/VoxelTreeBuilder.cs.meta
Normal file
@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 45419c0456ed1a74f97cc990244648a5
|
||||
105
Assets/Scripts/VoxelOctree/VoxelTreeManager.cs
Normal file
105
Assets/Scripts/VoxelOctree/VoxelTreeManager.cs
Normal file
@ -0,0 +1,105 @@
|
||||
using UnityEngine;
|
||||
|
||||
public struct IsSolidNodeAnswere
|
||||
{
|
||||
public float penetrationFactor;
|
||||
public float reflexionFactor;
|
||||
public bool isSolid;
|
||||
public GameObject Go;
|
||||
}
|
||||
|
||||
public class VoxelTreeManager : MonoBehaviour
|
||||
{
|
||||
public LayerMask solidLayerMask;
|
||||
public float voxelSize = 2f; // Minimum voxel size
|
||||
public int maxDepth = 6; // Tree subdivision limit
|
||||
public float boundsSize = 300f; // World size covered by the voxel tree
|
||||
public ComputeShader computeShader;
|
||||
private OctreeNode root;
|
||||
private VoxelTreeRaycaster raycaster = new VoxelTreeRaycaster();
|
||||
public VoxelRaycastGpuManager gpuRayCaster;
|
||||
|
||||
void Start()
|
||||
{
|
||||
Debug.Log($"Building voxel tree with LayerMask: {LayerMaskToString(solidLayerMask)}");
|
||||
|
||||
var builder = new VoxelTreeBuilder(maxDepth, voxelSize);
|
||||
root = builder.Build(new Bounds(Vector3.zero, Vector3.one * boundsSize), IsSolid);
|
||||
|
||||
var dbg = FindObjectOfType<VoxelTreeDebugger>();
|
||||
if (dbg) dbg.root = root;
|
||||
|
||||
gpuRayCaster = new VoxelRaycastGpuManager(computeShader, root);
|
||||
}
|
||||
|
||||
// This function replaces pos => pos.magnitude < 100f
|
||||
private IsSolidNodeAnswere IsSolid(Vector3 pos, float size)
|
||||
{
|
||||
IsSolidNodeAnswere answere;
|
||||
|
||||
answere.Go = null;
|
||||
|
||||
answere.penetrationFactor = 1;
|
||||
answere.reflexionFactor = 1;
|
||||
|
||||
float overlapMargin = size / 3f;
|
||||
answere.isSolid = false;
|
||||
|
||||
Collider[] hits = Physics.OverlapSphere(pos, overlapMargin, solidLayerMask, QueryTriggerInteraction.Ignore);
|
||||
|
||||
if (hits.Length > 0)
|
||||
{
|
||||
answere.isSolid = true;
|
||||
for (int i = 0; i < hits.Length; i++)
|
||||
{
|
||||
AudioObject audioObj = hits[i].GetComponent<AudioObject>();
|
||||
|
||||
if (audioObj)
|
||||
{
|
||||
answere.penetrationFactor = audioObj.audioMaterialSettings.penetrationFactor;
|
||||
answere.reflexionFactor = audioObj.audioMaterialSettings.reflexionFactor;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return answere;
|
||||
}
|
||||
|
||||
return answere;
|
||||
}
|
||||
|
||||
private static string LayerMaskToString(LayerMask mask)
|
||||
{
|
||||
string names = "";
|
||||
for (int i = 0; i < 32; i++)
|
||||
{
|
||||
if (((1 << i) & mask.value) != 0)
|
||||
{
|
||||
names += LayerMask.LayerToName(i) + " ";
|
||||
}
|
||||
}
|
||||
return names.Trim();
|
||||
}
|
||||
|
||||
public bool CastRay(Vector3 origin, Vector3 dir, float distance, out VoxelTreeRaycaster.HitInfo outHit)
|
||||
{
|
||||
if (raycaster.Raycast(root, origin, dir, distance, out var hit))
|
||||
{
|
||||
outHit = hit;
|
||||
// Debug.Log($"Ray hit voxel at {hit.point}, dist={hit.distance:F2}");
|
||||
//Debug.DrawLine(origin, hit.point, Color.red, 2f);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
outHit = hit;
|
||||
//Debug.Log("No voxel hit");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public VoxelRaycastGPU.BatchData[] CastGpuRay( in VoxelRaycastGPU.BatchData[] batchData, int datasLenght )
|
||||
{
|
||||
return gpuRayCaster.Raycast( in batchData, datasLenght );
|
||||
}
|
||||
}
|
||||
2
Assets/Scripts/VoxelOctree/VoxelTreeManager.cs.meta
Normal file
2
Assets/Scripts/VoxelOctree/VoxelTreeManager.cs.meta
Normal file
@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 81db4efe0049de943a98566ddc2ff7e2
|
||||
Reference in New Issue
Block a user