Local project added

This commit is contained in:
2025-10-19 22:56:25 +02:00
parent 7c15a8b78d
commit 8124165e9b
96 changed files with 17552 additions and 0 deletions

8
Assets/Assets.meta Normal file
View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: b53c8651f27ed2b419c441d4b493a178
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 4f9fcd1547970e24e8255ee7abe800fe
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,283 @@
// VoxelRaycastOctree.compute
#pragma kernel CSMain
// Match the C# struct layout exactly
struct LinearNode
{
// ---- Bloc 1 (16 bytes)
float penetrationFactor; // 4
float reflexionFactor; // 4
uint childMask; // 4
uint childBase; // 4
// ---- Bloc 7 (16 bytes)
uint isLeaf; // 4
uint isOccupied; // 4
uint pad0; // 4
uint pad1; // 4
};
// Ray and Hit definitions used in buffers
struct RayData
{
float pad;
float3 direction;
};
struct BatchData
{
float3 origin;
float maxDistance;
};
struct HitData
{
float penetrationFactor;
float reflexionFactor;
float lastDistance;
float pad0;
float3 origin; // float3 + 1 padding
float pad1;
float3 position; // float3 + 1 padding
float pad2;
float distance;
uint hit;
float2 pad3;
};
struct StackEntry
{
int nodeIndex;
float3 center;
float halfSize;
};
// Buffers
StructuredBuffer<LinearNode> nodes;
StructuredBuffer<RayData> rays;
StructuredBuffer<BatchData> batchDatas;
AppendStructuredBuffer<BatchData> hits;
RWStructuredBuffer<uint> hitCount;
int nodeCount;
int raysPerBatch;
float3 rootCenter;
float rootHalfSize;
int rootIndex;
float3 ClosestPointOnAABB(float3 hitPos, float3 boxCenter, float halfSize)
{
float3 minB = boxCenter - halfSize;
float3 maxB = boxCenter + halfSize;
// Clamp dans le cube
float3 q = clamp(hitPos, minB, maxB);
// On mesure la distance à chaque face
float3 distToMin = abs(q - minB);
float3 distToMax = abs(maxB - q);
// On garde la plus proche face sur chaque axe
float3 faceDist = min(distToMin, distToMax);
// Trouver laxe le plus "libre" (le plus éloigné dune face)
// -> on veut les deux plus proches axes => arête
float3 result = q;
// Compter combien d'axes sont "libres"
int numInside = 0;
[unroll]
for (int i = 0; i < 3; i++)
{
float dMin = distToMin[i];
float dMax = distToMax[i];
if (dMin < dMax)
result[i] = minB[i];
else
result[i] = maxB[i];
}
return result;
}
inline bool IntersectAABB_fast(
float3 center,
float halfSize,
float3 origin,
float3 dir,
float3 invDir, // pré-calculé : 1.0 / dir (avec protection contre 0)
out float tEntry,
out float tExit)
{
float3 minB = center - halfSize;
float3 maxB = center + halfSize;
// Calcul des distances d'entrée et sortie sur chaque axe
float3 t1 = (minB - origin) * invDir;
float3 t2 = (maxB - origin) * invDir;
// Trouver les valeurs d'entrée et sortie globales
float3 tMin = min(t1, t2);
float3 tMax = max(t1, t2);
// tEntry = le moment où on entre dans le cube
// tExit = le moment où on sort
tEntry = max(max(tMin.x, tMin.y), tMin.z);
tExit = min(min(tMax.x, tMax.y), tMax.z);
// Test d'intersection
return tExit >= max(tEntry, 0.0);
}
// AABB-ray intersection (slab method), returns whether intersects and entry distance
bool IntersectAABB(float3 boxCenter, float halfSize, float3 rayOrig, float3 rayDir, float invDirX, float invDirY, float invDirZ, out float tMin, out float tMax)
{
const float EPS = 0.2;
float3 minB = boxCenter - halfSize;
float3 maxB = boxCenter + halfSize;
tMin = -1e20;
tMax = 1e20;
[unroll]
for (int i = 0; i < 3; i++)
{
float ro = (i==0) ? rayOrig.x : (i==1) ? rayOrig.y : rayOrig.z;
float rd = (i==0) ? rayDir.x : (i==1) ? rayDir.y : rayDir.z;
float mn = (i==0) ? minB.x : (i==1) ? minB.y : minB.z;
float mx = (i==0) ? maxB.x : (i==1) ? maxB.y : maxB.z;
float invD = (i==0) ? invDirX : (i==1) ? invDirY : invDirZ;
if (abs(rd) < 1e-6)
{
if (ro < mn || ro > mx) return false;
}
else
{
float t1 = (mn - ro) * invD;
float t2 = (mx - ro) * invD;
if (t1 > t2) { float tmp = t1; t1 = t2; t2 = tmp; }
if (t1 > tMin) tMin = t1;
if (t2 < tMax) tMax = t2;
if (tMin > tMax) return false;
}
}
// Skip self-intersections
if (tMax < EPS) return false;
if (tMin < EPS) tMin = tMax; // Move entry forward if starting inside
return true;
}
[numthreads(8,8,1)] // 2D dispatch (8x8 = 64 threads)
void CSMain(uint3 id : SV_DispatchThreadID)
{
uint rayIndex = id.x;
uint batchIndex = id.y;
if (rayIndex >= rays.Length || batchIndex >= batchDatas.Length) return;
RayData r = rays[rayIndex];
BatchData b = batchDatas[batchIndex];
BatchData outHit;
outHit.origin = b.origin;
outHit.maxDistance = b.maxDistance;
float3 invDir=(0,0,0);
invDir.x = (abs(r.direction.x) < 1e-6) ? 1e8 : 1.0 / r.direction.x;
invDir.y = (abs(r.direction.y) < 1e-6) ? 1e8 : 1.0 / r.direction.y;
invDir.z = (abs(r.direction.z) < 1e-6) ? 1e8 : 1.0 / r.direction.z;
StackEntry stack[64];
int sp = 0;
StackEntry entry;
entry.nodeIndex = rootIndex;
entry.center = rootCenter;
entry.halfSize = rootHalfSize ;
stack[sp++] = entry;
bool hasHit = false;
StackEntry lastEntry = entry;
while (sp > 0)
{
StackEntry e = stack[--sp];
if (e.nodeIndex < 0 || e.nodeIndex >= nodeCount) continue;
LinearNode n = nodes[e.nodeIndex];
float tEntry, tExit;
if (!IntersectAABB_fast(e.center, e.halfSize, b.origin, r.direction, invDir, tEntry, tExit))
continue;
if ( tEntry >= outHit.maxDistance ) continue;
// Feuille
if (n.isLeaf == 1)
{
if (n.isOccupied == 1)
{
float tHit = max(tEntry, 0);
if (tHit < outHit.maxDistance)
{
hasHit = true;
outHit.maxDistance = tHit;
lastEntry = e;
}
}
continue;
}
if (n.childMask != 0)
{
float childHalf = e.halfSize * 0.5;
for (uint i = 0; i < 8; i++)
{
if (((n.childMask >> i) & 1u) == 0) continue;
uint offset = countbits(n.childMask & ((1u << i) - 1u));
int childIndex = n.childBase + offset;
float3 offsetVec = childHalf * float3(
(i & 4u) ? 1 : -1,
(i & 2u) ? 1 : -1,
(i & 1u) ? 1 : -1
);
entry.nodeIndex = childIndex;
entry.center = e.center + offsetVec;
entry.halfSize = childHalf ;
stack[sp++] = entry;
}
}
}
if ( hasHit )
{
LinearNode n = nodes[lastEntry.nodeIndex];
float tHit = outHit.maxDistance;
outHit.maxDistance = ( b.maxDistance - outHit.maxDistance ) * n.reflexionFactor;
float3 hitPos = b.origin + r.direction * tHit;
outHit.origin = ClosestPointOnAABB(hitPos, lastEntry.center, lastEntry.halfSize);
float3 dirOffset = b.origin - outHit.origin;
outHit.origin = outHit.origin + dirOffset;
hits.Append( outHit );
}
}

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: beacc211952bec342847019386af6944
ComputeShaderImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,14 @@
fileFormatVersion: 2
guid: 289c1b55c9541489481df5cc06664110
ScriptedImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 2
userData:
assetBundleName:
assetBundleVariant:
script: {fileID: 11500000, guid: 8404be70184654265930450def6a9037, type: 3}
generateWrapperCode: 0
wrapperCodePath:
wrapperClassName:
wrapperCodeNamespace:

8
Assets/Scenes.meta Normal file
View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 9c53962885c2c4f449125a979d6ad240
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 9fc0d4010bbf28b4594072e72b8655ab
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 36fcd9a1630f3f24385483a403e785d4
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 3b3c5e2cdf290b044bda9e0b5d10b99c
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,10 @@
using UnityEngine;
[CreateAssetMenu]
public class AudioMaterial : ScriptableObject
{
[Range(0f, 1)]
public float reflexionFactor = 1;
[Range(0f, 1)]
public float penetrationFactor = 1;
}

View File

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 865747647382995429248173da88c527

View File

@ -0,0 +1,16 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 865747647382995429248173da88c527, type: 3}
m_Name: Default
m_EditorClassIdentifier: Assembly-CSharp::AudioMaterial
reflexionFactor: 0.453
penetrationFactor: 0.468

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: f4dd01b169c72e54689f3ca58713902a
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,16 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 865747647382995429248173da88c527, type: 3}
m_Name: HighReflexion
m_EditorClassIdentifier: Assembly-CSharp::AudioMaterial
reflexionFactor: 0.868
penetrationFactor: 0

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 58ebbfa597f7b56499162cc5660da3ba
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,16 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 865747647382995429248173da88c527, type: 3}
m_Name: LowReflexion
m_EditorClassIdentifier: Assembly-CSharp::AudioMaterial
reflexionFactor: 0.254
penetrationFactor: 1

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 565a3cc5861104e4194172d221d2c530
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,16 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 865747647382995429248173da88c527, type: 3}
m_Name: NoReflexion
m_EditorClassIdentifier: Assembly-CSharp::AudioMaterial
reflexionFactor: 0
penetrationFactor: 0

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 0e665b23d6e511748a112663a3bcb8ca
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

8
Assets/Scripts.meta Normal file
View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 76fc03dac074adf43b7fe73d37a31762
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,6 @@
using UnityEngine;
public class AudioObject : MonoBehaviour
{
public AudioMaterial audioMaterialSettings;
}

View File

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: c0d5c94b23fe7cf40a75a538e5abe377

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 4c021777962f9e642b6e423c5ebe2331
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View 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 las)
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);
}
}

View File

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 7af577c689fa40b4a9291ff83dbde469

View 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");
}
}
}

View File

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 98b63d0a77a360e4d94b27528bc3000d

View 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);
}
}
}
}

View File

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 31dcad4d65772e349b756df7096c55d0

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 54c344627e0b6a1488c3c7d6c6d49bb5
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 3ac02403714fd184095cb0c32517d4b0
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View 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;
}
}

View File

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 13827c41731283f4bb6202cac32e51ab

View 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;
}
}

View File

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 968c451685e18e54a9cb1d419f303c54

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 11755919bdf10884492adbb91aa6837b
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View 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);
}
}

View File

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: d8d6d3db0d1de6a488ad343cfc26da8d

View 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);
}
}

View File

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 46218eff2d8f9a6489444052fb6a7ef6

View 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();
}
}

View File

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: f13f71d059d1b854bb51ac64bede9396

View 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);
}
}

View File

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 45419c0456ed1a74f97cc990244648a5

View 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 );
}
}

View File

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 81db4efe0049de943a98566ddc2ff7e2

8
Assets/_Recovery.meta Normal file
View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: d2c0a51753504044d9e8dd164621c71e
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

1059
Assets/_Recovery/0 (1).unity Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: b75a8120316b223418651a7302530538
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

1075
Assets/_Recovery/0 (2).unity Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 819cb888b6aaa5d44aa96275fb22d3cc
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

1055
Assets/_Recovery/0 (3).unity Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: dc4f3c4e9daeb06438435e3a5fa414e4
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

1215
Assets/_Recovery/0 (4).unity Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 719f70f416b2614478e050ed298da5d0
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

1345
Assets/_Recovery/0 (5).unity Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 23c8a017bdfc66a42a2e255742f157a3
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

1345
Assets/_Recovery/0 (6).unity Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 53ee69d9a4c7c0a4c97f7dbbde41e9d7
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

1365
Assets/_Recovery/0 (7).unity Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 1901bdb0efa01424bb786ccf7cbe0183
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

1347
Assets/_Recovery/0 (8).unity Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 46400ed5594b43e47b156f906ee7f293
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

1059
Assets/_Recovery/0.unity Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 1a835e3f24c2cf944bf346dbe2ca35bb
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant: