├── Assets ├── Resources.meta ├── Resources │ ├── 2-2-AlphaBlenedTex.png │ ├── 2-2-AlphaBlenedTex.png.meta │ ├── Baked_Octrees.meta │ ├── Baked_Octrees │ │ ├── 30x30x30x5.txt │ │ ├── 30x30x30x5.txt.meta │ │ ├── 30x30x30x5_2.txt │ │ └── 30x30x30x5_2.txt.meta │ ├── Main Camera.prefab │ ├── Main Camera.prefab.meta │ ├── Materials.meta │ ├── Materials │ │ ├── Debug_Materials.meta │ │ ├── Debug_Materials │ │ │ ├── Line_Material.mat │ │ │ └── Line_Material.mat.meta │ │ ├── EarthMaterial.mat │ │ ├── EarthMaterial.mat.meta │ │ ├── Particle_Materials.meta │ │ ├── Particle_Materials │ │ │ ├── Particle Material_Quads.mat │ │ │ ├── Particle Material_Quads.mat.meta │ │ │ ├── Particle Material_Spheres.mat │ │ │ └── Particle Material_Spheres.mat.meta │ │ ├── lambert1.mat │ │ └── lambert1.mat.meta │ ├── Models.meta │ ├── Models │ │ ├── Half_Pipe.fbx │ │ └── Half_Pipe.fbx.meta │ ├── Octree.prefab │ ├── Octree.prefab.meta │ ├── Shaders.meta │ └── Shaders │ │ ├── FlowingParticles.compute │ │ ├── FlowingParticles.compute.meta │ │ ├── GPUParticle.cginc │ │ ├── GPUParticle.cginc.meta │ │ ├── GeoShader_Quads.shader │ │ ├── GeoShader_Quads.shader.meta │ │ ├── GeoShader_Spheres.shader │ │ ├── GeoShader_Spheres.shader.meta │ │ ├── Random.cginc │ │ └── Random.cginc.meta ├── Scenes.meta ├── Scenes │ ├── Test Scenes.meta │ └── Test Scenes │ │ ├── Flowing Particles.meta │ │ ├── Flowing Particles.unity │ │ ├── Flowing Particles.unity.meta │ │ ├── Flowing Particles │ │ ├── LightingData.asset │ │ ├── LightingData.asset.meta │ │ ├── ReflectionProbe-0.exr │ │ └── ReflectionProbe-0.exr.meta │ │ ├── OBB_Testing.unity │ │ ├── OBB_Testing.unity.meta │ │ ├── Spherical_Rays.unity │ │ ├── Spherical_Rays.unity.meta │ │ ├── Voxelization_Test.unity │ │ └── Voxelization_Test.unity.meta ├── Scripts.meta └── Scripts │ ├── Compute_Handlers.meta │ ├── Compute_Handlers │ ├── Particle_Flowing.cs │ └── Particle_Flowing.cs.meta │ ├── Octree.meta │ ├── Octree │ ├── Baked_Octree.cs │ ├── Baked_Octree.cs.meta │ ├── Octree.cs │ ├── Octree.cs.meta │ ├── Octree_Baker.cs │ ├── Octree_Baker.cs.meta │ ├── Voxel_Debugger.cs │ ├── Voxel_Debugger.cs.meta │ ├── Voxelizer.cs │ └── Voxelizer.cs.meta │ ├── SmoothMouseLook.cs │ ├── SmoothMouseLook.cs.meta │ ├── Testing_Scripts.meta │ └── Testing_Scripts │ ├── Make_Icosahedron.cs │ ├── Make_Icosahedron.cs.meta │ ├── OBB_Bounds.cs │ ├── OBB_Bounds.cs.meta │ ├── PointsOnSphere.cs │ └── PointsOnSphere.cs.meta ├── README.md ├── gif_1.gif ├── gif_2.gif └── gif_3.gif /Assets/Resources.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 7150f877bfd85624aad5d42bf70b95b3 3 | folderAsset: yes 4 | timeCreated: 1492303457 5 | licenseType: Pro 6 | DefaultImporter: 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /Assets/Resources/2-2-AlphaBlenedTex.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Brozef92/Unity-GPU-Particles/65ba327ab819822a53d284d783ccbcaf4f4b5623/Assets/Resources/2-2-AlphaBlenedTex.png -------------------------------------------------------------------------------- /Assets/Resources/2-2-AlphaBlenedTex.png.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: b6f48dc63a1ccc64da843d635772b74b 3 | timeCreated: 1497142543 4 | licenseType: Pro 5 | TextureImporter: 6 | fileIDToRecycleName: {} 7 | serializedVersion: 2 8 | mipmaps: 9 | mipMapMode: 0 10 | enableMipMap: 1 11 | linearTexture: 0 12 | correctGamma: 0 13 | fadeOut: 0 14 | borderMipMap: 0 15 | mipMapFadeDistanceStart: 1 16 | mipMapFadeDistanceEnd: 3 17 | bumpmap: 18 | convertToNormalMap: 0 19 | externalNormalMap: 0 20 | heightScale: 0.25 21 | normalMapFilter: 0 22 | isReadable: 0 23 | grayScaleToAlpha: 1 24 | generateCubemap: 0 25 | cubemapConvolution: 0 26 | cubemapConvolutionSteps: 7 27 | cubemapConvolutionExponent: 1.5 28 | seamlessCubemap: 0 29 | textureFormat: -1 30 | maxTextureSize: 2048 31 | textureSettings: 32 | filterMode: -1 33 | aniso: 16 34 | mipBias: -1 35 | wrapMode: 1 36 | nPOTScale: 1 37 | lightmap: 0 38 | rGBM: 0 39 | compressionQuality: 50 40 | allowsAlphaSplitting: 0 41 | spriteMode: 0 42 | spriteExtrude: 1 43 | spriteMeshType: 1 44 | alignment: 0 45 | spritePivot: {x: 0.5, y: 0.5} 46 | spriteBorder: {x: 0, y: 0, z: 0, w: 0} 47 | spritePixelsToUnits: 100 48 | alphaIsTransparency: 1 49 | textureType: 0 50 | buildTargetSettings: [] 51 | spriteSheet: 52 | sprites: [] 53 | outline: [] 54 | spritePackingTag: 55 | userData: 56 | assetBundleName: 57 | assetBundleVariant: 58 | -------------------------------------------------------------------------------- /Assets/Resources/Baked_Octrees.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 94775a1865bb935499b3ca6c7614c7f7 3 | folderAsset: yes 4 | timeCreated: 1497230044 5 | licenseType: Free 6 | DefaultImporter: 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /Assets/Resources/Baked_Octrees/30x30x30x5.txt.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 76d64a096e2ac6a41b5bd1a0b47d9281 3 | timeCreated: 1497302806 4 | licenseType: Free 5 | TextScriptImporter: 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Assets/Resources/Baked_Octrees/30x30x30x5_2.txt.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 0229c51f1c7150e48b94cfd20602a346 3 | timeCreated: 1497305360 4 | licenseType: Free 5 | TextScriptImporter: 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Assets/Resources/Main Camera.prefab: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Brozef92/Unity-GPU-Particles/65ba327ab819822a53d284d783ccbcaf4f4b5623/Assets/Resources/Main Camera.prefab -------------------------------------------------------------------------------- /Assets/Resources/Main Camera.prefab.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: bc2669bb59b90784fb32f8caebd2aa7c 3 | timeCreated: 1497140193 4 | licenseType: Pro 5 | NativeFormatImporter: 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Assets/Resources/Materials.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: d7ed75a6867e3784d98d6bdd56211ed1 3 | folderAsset: yes 4 | timeCreated: 1492313886 5 | licenseType: Pro 6 | DefaultImporter: 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /Assets/Resources/Materials/Debug_Materials.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 2db62a624a61419419b4da39267195bd 3 | folderAsset: yes 4 | timeCreated: 1496772776 5 | licenseType: Pro 6 | DefaultImporter: 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /Assets/Resources/Materials/Debug_Materials/Line_Material.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Brozef92/Unity-GPU-Particles/65ba327ab819822a53d284d783ccbcaf4f4b5623/Assets/Resources/Materials/Debug_Materials/Line_Material.mat -------------------------------------------------------------------------------- /Assets/Resources/Materials/Debug_Materials/Line_Material.mat.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 036d7250b7070fe40b7426280f633cd5 3 | timeCreated: 1494354806 4 | licenseType: Pro 5 | NativeFormatImporter: 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Assets/Resources/Materials/EarthMaterial.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Brozef92/Unity-GPU-Particles/65ba327ab819822a53d284d783ccbcaf4f4b5623/Assets/Resources/Materials/EarthMaterial.mat -------------------------------------------------------------------------------- /Assets/Resources/Materials/EarthMaterial.mat.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: f722285e4a9493249b748b0ad252a14c 3 | timeCreated: 1492538349 4 | licenseType: Pro 5 | NativeFormatImporter: 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Assets/Resources/Materials/Particle_Materials.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 2e2ac722d71461b4dbb70f421c7690f9 3 | folderAsset: yes 4 | timeCreated: 1496772764 5 | licenseType: Pro 6 | DefaultImporter: 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /Assets/Resources/Materials/Particle_Materials/Particle Material_Quads.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Brozef92/Unity-GPU-Particles/65ba327ab819822a53d284d783ccbcaf4f4b5623/Assets/Resources/Materials/Particle_Materials/Particle Material_Quads.mat -------------------------------------------------------------------------------- /Assets/Resources/Materials/Particle_Materials/Particle Material_Quads.mat.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 3f4f191d4e8b7fe4b95dd63d26d41fcd 3 | NativeFormatImporter: 4 | userData: 5 | assetBundleName: 6 | assetBundleVariant: 7 | -------------------------------------------------------------------------------- /Assets/Resources/Materials/Particle_Materials/Particle Material_Spheres.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Brozef92/Unity-GPU-Particles/65ba327ab819822a53d284d783ccbcaf4f4b5623/Assets/Resources/Materials/Particle_Materials/Particle Material_Spheres.mat -------------------------------------------------------------------------------- /Assets/Resources/Materials/Particle_Materials/Particle Material_Spheres.mat.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 9094136a7d1db0d4898e350d40a28b5e 3 | timeCreated: 1496539438 4 | licenseType: Pro 5 | NativeFormatImporter: 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Assets/Resources/Materials/lambert1.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Brozef92/Unity-GPU-Particles/65ba327ab819822a53d284d783ccbcaf4f4b5623/Assets/Resources/Materials/lambert1.mat -------------------------------------------------------------------------------- /Assets/Resources/Materials/lambert1.mat.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: fba84272e26013842a413c76c3d6039b 3 | timeCreated: 1497140715 4 | licenseType: Pro 5 | NativeFormatImporter: 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Assets/Resources/Models.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 72151ed9bba516d48b2648eca26c27e4 3 | folderAsset: yes 4 | timeCreated: 1497140682 5 | licenseType: Pro 6 | DefaultImporter: 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /Assets/Resources/Models/Half_Pipe.fbx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Brozef92/Unity-GPU-Particles/65ba327ab819822a53d284d783ccbcaf4f4b5623/Assets/Resources/Models/Half_Pipe.fbx -------------------------------------------------------------------------------- /Assets/Resources/Models/Half_Pipe.fbx.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 2ca37bf097d43c44fabba4f8a268ea5a 3 | timeCreated: 1497140715 4 | licenseType: Pro 5 | ModelImporter: 6 | serializedVersion: 19 7 | fileIDToRecycleName: 8 | 100000: //RootNode 9 | 400000: //RootNode 10 | 2300000: //RootNode 11 | 3300000: //RootNode 12 | 4300000: pPipe1 13 | materials: 14 | importMaterials: 1 15 | materialName: 0 16 | materialSearch: 1 17 | animations: 18 | legacyGenerateAnimations: 4 19 | bakeSimulation: 0 20 | resampleRotations: 1 21 | optimizeGameObjects: 0 22 | motionNodeName: 23 | animationImportErrors: 24 | animationImportWarnings: 25 | animationRetargetingWarnings: 26 | animationDoRetargetingWarnings: 0 27 | animationCompression: 1 28 | animationRotationError: 0.5 29 | animationPositionError: 0.5 30 | animationScaleError: 0.5 31 | animationWrapMode: 0 32 | extraExposedTransformPaths: [] 33 | clipAnimations: [] 34 | isReadable: 1 35 | meshes: 36 | lODScreenPercentages: [] 37 | globalScale: 1 38 | meshCompression: 0 39 | addColliders: 0 40 | importBlendShapes: 1 41 | swapUVChannels: 0 42 | generateSecondaryUV: 0 43 | useFileUnits: 1 44 | optimizeMeshForGPU: 1 45 | keepQuads: 0 46 | weldVertices: 1 47 | secondaryUVAngleDistortion: 8 48 | secondaryUVAreaDistortion: 15.000001 49 | secondaryUVHardAngle: 88 50 | secondaryUVPackMargin: 4 51 | useFileScale: 0 52 | tangentSpace: 53 | normalSmoothAngle: 60 54 | normalImportMode: 0 55 | tangentImportMode: 3 56 | importAnimation: 1 57 | copyAvatar: 0 58 | humanDescription: 59 | human: [] 60 | skeleton: [] 61 | armTwist: 0.5 62 | foreArmTwist: 0.5 63 | upperLegTwist: 0.5 64 | legTwist: 0.5 65 | armStretch: 0.05 66 | legStretch: 0.05 67 | feetSpacing: 0 68 | rootMotionBoneName: 69 | hasTranslationDoF: 0 70 | lastHumanDescriptionAvatarSource: {instanceID: 0} 71 | animationType: 0 72 | humanoidOversampling: 1 73 | additionalBone: 0 74 | userData: 75 | assetBundleName: 76 | assetBundleVariant: 77 | -------------------------------------------------------------------------------- /Assets/Resources/Octree.prefab: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Brozef92/Unity-GPU-Particles/65ba327ab819822a53d284d783ccbcaf4f4b5623/Assets/Resources/Octree.prefab -------------------------------------------------------------------------------- /Assets/Resources/Octree.prefab.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 71b74908311a10742b003eefec0d042d 3 | timeCreated: 1494718241 4 | licenseType: Pro 5 | NativeFormatImporter: 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Assets/Resources/Shaders.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 757ae6e26d6c8ad49a9e02933a3c73ce 3 | folderAsset: yes 4 | timeCreated: 1492303457 5 | licenseType: Pro 6 | DefaultImporter: 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /Assets/Resources/Shaders/FlowingParticles.compute: -------------------------------------------------------------------------------- 1 | // Each #kernel tells which function to compile; you can have many kernels 2 | #pragma kernel CS_Initialize 3 | #pragma kernel CS_Emit 4 | #pragma kernel CS_Update 5 | 6 | // Used parts of these... Thank you for making it public 7 | // https://github.com/hecomi/UnityPseudoInstancedGPUParticles/blob/master/Assets/Screen%20Space%20Collision%20GPU%20Particles/Compute%20Shaders/PseudoInstancedGPUParticleUpdater.compute 8 | // https://github.com/keijiro/KvantSpray 9 | 10 | // Particle's data 11 | #include "./GPUParticle.cginc" 12 | #include "./Random.cginc" 13 | 14 | //======================================================================================================== 15 | //Buffers 16 | //======================================================================================================== 17 | 18 | // Particle's data shared with CPU 19 | RWStructuredBuffer particleBuffer; //All our particles 20 | //GPU only accessable memory... 21 | AppendStructuredBuffer deadParticles; //All "dead" indicies 22 | ConsumeStructuredBuffer particlePool; //All active indicies 23 | 24 | //A sparse Octree of SDF nodes packed into an array 25 | //Traverse using Indicies 26 | StructuredBuffer SDF_Buffer; 27 | 28 | //======================================================================================================== 29 | //Global Varriables CPU writable 30 | //======================================================================================================== 31 | 32 | float ParticleLifeTime; 33 | float deltaTime; 34 | float InvParticleMass; 35 | 36 | float3 _EmitterPos; 37 | float3 _EmitterSize; 38 | float4 _EmitterDirection; //Normalized forward vector.xyz, spread.w 39 | 40 | float _ParticleSpeed; 41 | float _SpeedRandomness; 42 | float _ParticleBounce; 43 | 44 | float _ParticleRadius; //size of the particle taken from the Shader that will draw the particles 45 | 46 | int Octree_Size; 47 | int Octree_Depth; 48 | 49 | //Gravity -9.81f 50 | const float3 _Gravity; 51 | 52 | float CollisionThreshold; 53 | 54 | #define THREAD_COUNT 256 55 | 56 | //======================================================================================================== 57 | //Helper Functions for collision detection: using Signed Distance Fields 58 | //======================================================================================================== 59 | //basically this is an octree of a static environment and at the lowest nodes there is a Vector3 60 | //that is the closest surface point to the center of this Node, so it approximates geometry of the scene 61 | //the more nodes you have the more accurate the approximations are. 62 | 63 | bool Intersect(AABB A, float3 P) 64 | { 65 | //http://www.miguelcasillas.com/?p=24 66 | if (P.x > A.Min.x && P.x < A.Max.x && 67 | P.y > A.Min.y && P.y < A.Max.y && 68 | P.z > A.Min.z && P.z < A.Max.z) 69 | { 70 | return true; 71 | } 72 | 73 | return false; 74 | } 75 | 76 | //traverse through the tree and find Which node this Particle is in 77 | int FindNode(float3 P) 78 | { 79 | int index = -1; //return this 80 | 81 | int parent = 0; //which parent index are we using 82 | 83 | //First check if this Particle is even with in the tree 84 | AABB root; 85 | root.Min = SDF_Buffer[0].Min; 86 | root.Max = SDF_Buffer[0].Max; 87 | 88 | bool inTree = Intersect(root, P); 89 | 90 | if (!inTree) 91 | { 92 | return -1; //early out 93 | } 94 | 95 | //Check from this node until you find which Leaf Node this particle is in 96 | 97 | for (int child = 0; child < 8; ++child) 98 | { 99 | index = SDF_Buffer[parent].children[child]; 100 | 101 | AABB node; 102 | node.Min = SDF_Buffer[index].Min; 103 | node.Max = SDF_Buffer[index].Max; 104 | 105 | bool inNode = Intersect(node, P); 106 | 107 | if (inNode == false) 108 | { 109 | continue; //not in this child check the next 110 | } 111 | else 112 | { 113 | //check if intersecting node has children to search 114 | if (SDF_Buffer[index].children[0] != -1) 115 | { 116 | parent = index; 117 | child = -1; 118 | continue; 119 | } 120 | else 121 | { 122 | break; //this node the Particle is in is a leaf node 123 | } 124 | } 125 | } 126 | return index; 127 | } 128 | 129 | //search tree linearly...This was the old way and was slow as fuck 130 | //when there were more than 500,000 particles 131 | int FindNode_Linear(float3 P) 132 | { 133 | int index = -1; //return this 134 | 135 | //First check if this Particle is even with in the tree 136 | AABB root; 137 | root.Min = SDF_Buffer[0].Min; 138 | root.Max = SDF_Buffer[0].Max; 139 | 140 | bool inTree = Intersect(root, P); 141 | 142 | if (!inTree) 143 | { 144 | return -1; //early out 145 | } 146 | 147 | for (index = 0; index < Octree_Size; ++index) 148 | { 149 | AABB node; 150 | node.Min = SDF_Buffer[index].Min; 151 | node.Max = SDF_Buffer[index].Max; 152 | 153 | bool inNode = Intersect(node, P); 154 | 155 | if (inNode == false) 156 | { 157 | continue; //not in this child check the next 158 | } 159 | else if(inNode && SDF_Buffer[index].children[0] == -1) 160 | { 161 | break; 162 | } 163 | } 164 | return index; 165 | } 166 | 167 | //======================================================================================================== 168 | //All Kernels : Init(), Emit(), Update() 169 | //======================================================================================================== 170 | 171 | //Initialize all Particles at the start of a handler script(C#) 172 | [numthreads(THREAD_COUNT, 1, 1)] //X * Y * Z threads 173 | void CS_Initialize(uint3 id : SV_DispatchThreadID) 174 | { 175 | particleBuffer[id.x].active = false; 176 | particleBuffer[id.x].invMass = InvParticleMass; 177 | particleBuffer[id.x].position = (float3)0.0f; 178 | deadParticles.Append(id.x); //Add all particle indicies to our dead list 179 | } 180 | 181 | //Emits particles after they "Die" 182 | [numthreads(THREAD_COUNT, 1, 1)] //X * Y * Z threads 183 | void CS_Emit() 184 | { 185 | uint id = particlePool.Consume(); //take an index from the active particles 186 | 187 | //seed for random functions 188 | float2 seed = float2(id + 1, id + 2); 189 | float3 randomRange = nrand3(seed); 190 | float3 randomVelocity = nrand3(seed + 1); 191 | float3 randomPosition = nrand3(seed + 2); 192 | 193 | //Spawn a particle 194 | Particle p = particleBuffer[id]; 195 | p.active = true; 196 | 197 | //re-position offset from emitter position 198 | p.position = (randomPosition - (float3)0.5f) * _EmitterSize + _EmitterPos; 199 | 200 | //re-velocitize? 201 | float3 vel = (randomVelocity - (float3)0.5f) * 2.0f; 202 | // Spreading 203 | vel = lerp(_EmitterDirection.xyz, vel, _EmitterDirection.w); 204 | 205 | vel = normalize(vel) * _ParticleSpeed; 206 | vel *= 1.0f - nrand(seed, 1) * _SpeedRandomness; 207 | p.velocity = vel; 208 | 209 | //Acceleration is constant just Gravity 210 | p.acceleration = _Gravity * p.invMass; 211 | 212 | p.lifeTime = ParticleLifeTime; 213 | 214 | //Assign our new particle 215 | particleBuffer[id] = p; 216 | } 217 | 218 | 219 | //Updates each particle to move and/or collide 220 | [numthreads(THREAD_COUNT, 1, 1)] //X * Y * Z threads 221 | void CS_Update (uint3 id : SV_DispatchThreadID) 222 | { 223 | Particle p = particleBuffer[id.x]; 224 | 225 | if (p.active) 226 | { 227 | p.lifeTime -= deltaTime; 228 | 229 | if (p.lifeTime <= 0.0f) 230 | { 231 | // particle is "dead" ... GEO shader will not draw it 232 | p.active = false; 233 | p.position = (float3)0.0f; 234 | deadParticles.Append(id.x); 235 | } 236 | else 237 | { 238 | //Simple explicit Euler integration 239 | p.velocity += p.acceleration * deltaTime; 240 | p.position += p.velocity * deltaTime; 241 | 242 | //Particle-Scene collision detection... using "Signed Distance Fields" 243 | if (Octree_Size > 1) 244 | { 245 | //Find which leaf node this Particle is in 246 | int index = FindNode(p.position); 247 | //int index = FindNode_Linear(p.position); //old way slow as fuck 248 | 249 | if (index >= 0 && index < Octree_Size) 250 | { 251 | SDF node = SDF_Buffer[index]; 252 | //now use the Surface Point to find out just how close the Particle is 253 | //to the nearest surface 254 | 255 | float3 pToS = node.Point - p.position; 256 | float distSq = pToS.x*pToS.x + pToS.y*pToS.y + pToS.z*pToS.z; 257 | distSq += _ParticleRadius * _ParticleRadius; //add the rough size of the particle 258 | 259 | if (distSq <= CollisionThreshold + (_ParticleRadius * _ParticleRadius)) 260 | { 261 | //Reflect particle velocity by the surface Normal 262 | float ns = (node.Max.x - node.Min.x) * 0.5f; 263 | float3 nodeCenter = float3(node.Min.x + ns, node.Min.y + ns, node.Min.z + ns); 264 | float3 surfaceNormal = normalize(node.Point - nodeCenter); 265 | float3 vel = p.velocity; 266 | 267 | //only reflect if the Reflect Vector is with-in some range 268 | if (dot(vel, surfaceNormal) > -0.25f) 269 | { 270 | //Reflection formula: https://math.stackexchange.com/questions/13261/how-to-get-a-reflection-vector 271 | p.velocity = vel - (2.0f*((dot(vel, surfaceNormal)*surfaceNormal)) * _ParticleBounce); //works well but doesn't lose much velocity after hit 272 | } 273 | 274 | } 275 | 276 | } 277 | //if particle is outside the Volume just reverse it's velocity 278 | else if (index < 0) 279 | { 280 | //check if they are below the floor of the Octree Tree if so then reduce and negate velocity 281 | float floor = 0.0f - ((SDF_Buffer[0].Max.y - SDF_Buffer[0].Min.y) * 0.5f); 282 | 283 | if (p.position.y < floor) 284 | { 285 | p.position.y = floor + 0.01f; 286 | p.velocity = -(p.velocity * 0.5f); 287 | } 288 | else 289 | { 290 | //p.velocity = -p.velocity * _ParticleBounce; 291 | } 292 | } 293 | } 294 | } 295 | } 296 | 297 | //re-apply to our particle buffer 298 | particleBuffer[id.x] = p; 299 | } 300 | -------------------------------------------------------------------------------- /Assets/Resources/Shaders/FlowingParticles.compute.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 8b4a747c6d24c0249b16907ee5006d24 3 | timeCreated: 1492908156 4 | licenseType: Pro 5 | ComputeShaderImporter: 6 | currentAPIMask: 4 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /Assets/Resources/Shaders/GPUParticle.cginc: -------------------------------------------------------------------------------- 1 | #ifndef _PARTICLE_INCLUDED_ 2 | #define _PARTICLE_INCLUDED_ 3 | 4 | struct Particle //Stride = 48 5 | { 6 | float lifeTime; 7 | float invMass; 8 | float3 position; 9 | float3 velocity; 10 | float3 acceleration; 11 | bool active; 12 | }; 13 | 14 | struct SDF //Stride = 72 Bytes 15 | { 16 | int index; 17 | int children[8]; 18 | float3 Min; //AABB of this node 19 | float3 Max; 20 | float3 Point; //closest surface point 21 | }; 22 | 23 | struct AABB 24 | { 25 | float3 Min; 26 | float3 Max; //Min/Max corners for this AABB 27 | }; 28 | 29 | #endif // _PARTICLE_INCLUDED_ 30 | -------------------------------------------------------------------------------- /Assets/Resources/Shaders/GPUParticle.cginc.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 9752b58d518cefc41bdf8628f1bc8af3 3 | timeCreated: 1493143617 4 | licenseType: Pro 5 | ShaderImporter: 6 | defaultTextures: [] 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /Assets/Resources/Shaders/GeoShader_Quads.shader: -------------------------------------------------------------------------------- 1 | Shader "Custom/GeoShader_Quads" 2 | { 3 | Properties 4 | { 5 | _Color("Main Color", COLOR) = (1,1,1,1) 6 | _SpriteTex ("Base (RGB)", 2D) = "white" {} 7 | _Size ("Size", Range(0.01, 3)) = 0.5 8 | } 9 | 10 | SubShader 11 | { 12 | Pass 13 | { 14 | Tags{ "RenderType" = "Transparent" "Queue" = "Transparent" } 15 | LOD 200 16 | 17 | ZWrite Off // don't write to depth buffer in order to not to occlude other particles 18 | 19 | Blend SrcAlpha OneMinusSrcAlpha // use alpha blending 20 | 21 | Cull Back//Off 22 | 23 | CGPROGRAM 24 | #pragma target 5.0 25 | #pragma vertex VS_Main 26 | #pragma fragment FS_Main 27 | #pragma geometry GS_Main 28 | //#pragma enable_d3d11_debug_symbols //For debugging only 29 | #include "UnityCG.cginc" 30 | 31 | //========================================================================================== 32 | // Data structures 33 | //========================================================================================== 34 | struct GS_INPUT 35 | { 36 | float4 pos : POSITION; 37 | bool active : TEXCOORD0; 38 | }; 39 | 40 | struct FS_INPUT 41 | { 42 | float4 pos : POSITION; 43 | float2 tex0 : TEXCOORD0; 44 | }; 45 | 46 | // Particle's data 47 | #include "./GPUParticle.cginc" 48 | 49 | //========================================================================================== 50 | // Varriables 51 | //========================================================================================== 52 | 53 | float _Size; 54 | float4x4 _VP; 55 | Texture2D _SpriteTex; 56 | SamplerState sampler_SpriteTex; 57 | 58 | StructuredBuffer _ParticleBuffer; 59 | 60 | fixed4 _Color; 61 | 62 | //========================================================================================== 63 | // Shader Programs 64 | //========================================================================================== 65 | 66 | //========================================================================================== 67 | // Vertex Shader 68 | //========================================================================================== 69 | GS_INPUT VS_Main(uint id : SV_VertexID) //appdata_base v 70 | { 71 | //https://gamedev.stackexchange.com/questions/139378/unity-simple-pass-through-geometry-shader 72 | GS_INPUT output = (GS_INPUT)0; 73 | 74 | //Put this vertex into World space from it's local space 75 | float4 vPos = float4(_ParticleBuffer[id].position, 1.0f); 76 | output.pos = mul(_Object2World, vPos); 77 | 78 | //Check if particle is active or not 79 | if (_ParticleBuffer[id].active) 80 | { 81 | output.active = true; 82 | } 83 | else 84 | { 85 | output.active = false; 86 | } 87 | 88 | return output; 89 | } 90 | 91 | //========================================================================================== 92 | // Geometry Shader 93 | //========================================================================================== 94 | [maxvertexcount(4)] 95 | void GS_Main(point GS_INPUT p[1], inout TriangleStream triStream) 96 | { 97 | float3 look = _WorldSpaceCameraPos - p[0].pos; 98 | look = normalize(look); 99 | 100 | //use camera's matrix 101 | float3 up = UNITY_MATRIX_IT_MV[1].xyz; 102 | 103 | float3 right = cross(up, look); 104 | 105 | float halfS = 0.5f * _Size; 106 | 107 | float4 v[4]; 108 | v[0] = float4(p[0].pos + halfS * right - halfS * up, 1.0f); 109 | v[1] = float4(p[0].pos + halfS * right + halfS * up, 1.0f); 110 | v[2] = float4(p[0].pos - halfS * right - halfS * up, 1.0f); 111 | v[3] = float4(p[0].pos - halfS * right + halfS * up, 1.0f); 112 | 113 | //Create Quad only particle is ACTIVE 114 | if (p[0].active) 115 | { 116 | float4x4 vp = mul(UNITY_MATRIX_MVP, _World2Object); //put the Quad into View Space 117 | FS_INPUT pIn; 118 | pIn.pos = mul(vp, v[0]); 119 | pIn.tex0 = float2(1.0f, 0.0f); 120 | triStream.Append(pIn); 121 | 122 | pIn.pos = mul(vp, v[1]); 123 | pIn.tex0 = float2(1.0f, 1.0f); 124 | triStream.Append(pIn); 125 | 126 | pIn.pos = mul(vp, v[2]); 127 | pIn.tex0 = float2(0.0f, 0.0f); 128 | triStream.Append(pIn); 129 | 130 | pIn.pos = mul(vp, v[3]); 131 | pIn.tex0 = float2(0.0f, 1.0f); 132 | triStream.Append(pIn); 133 | } 134 | } 135 | 136 | //========================================================================================== 137 | // Fragment Shader 138 | //========================================================================================== 139 | float4 FS_Main(FS_INPUT input) : COLOR 140 | { 141 | float4 mainTexture = _SpriteTex.Sample(sampler_SpriteTex, input.tex0); 142 | float4 colour = float4(_Color.rgb, 1.0f); 143 | return colour * mainTexture; 144 | } 145 | 146 | ENDCG 147 | } 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /Assets/Resources/Shaders/GeoShader_Quads.shader.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 76673705d7e86ef43973bfff035bab5c 3 | timeCreated: 1492530303 4 | licenseType: Pro 5 | ShaderImporter: 6 | defaultTextures: [] 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /Assets/Resources/Shaders/GeoShader_Spheres.shader: -------------------------------------------------------------------------------- 1 | Shader "Custom/GeoShader_Spheres" 2 | { 3 | Properties 4 | { 5 | _Color("Main Color", COLOR) = (1,1,1,1) 6 | _Size("Size", Range(0.1, 1)) = 0.5 7 | } 8 | 9 | SubShader 10 | { 11 | Pass 12 | { 13 | //Tags{ "RenderType" = "Opaque" } 14 | Tags{ "LightMode" = "ForwardBase" } 15 | LOD 200 16 | 17 | ZWrite On 18 | 19 | //Blend SrcAlpha OneMinusSrcAlpha // use alpha blending 20 | 21 | Cull Off 22 | 23 | CGPROGRAM 24 | #pragma target 5.0 25 | #pragma vertex VS_Main 26 | #pragma fragment FS_Main 27 | #pragma geometry GS_Main 28 | 29 | //Unity FOG 30 | #pragma multi_compile_fog 31 | //#pragma enable_d3d11_debug_symbols //For debugging only 32 | #include "UnityCG.cginc" 33 | 34 | //========================================================================================== 35 | // Data structures 36 | //========================================================================================== 37 | struct GS_INPUT 38 | { 39 | float4 pos : POSITION; 40 | bool active : TEXCOORD0; 41 | }; 42 | 43 | struct FS_INPUT 44 | { 45 | float4 pos : POSITION; 46 | float2 uv : TEXCOORD0; 47 | UNITY_FOG_COORDS(1) 48 | }; 49 | 50 | // Particle's data 51 | #include "./GPUParticle.cginc" 52 | 53 | //========================================================================================== 54 | // Varriables 55 | //========================================================================================== 56 | 57 | float _Size; 58 | float4x4 _VP; 59 | Texture2D _SpriteTex; 60 | SamplerState sampler_SpriteTex; 61 | 62 | StructuredBuffer _ParticleBuffer; 63 | 64 | fixed4 _Color; 65 | 66 | //========================================================================================== 67 | // Shader Programs 68 | //========================================================================================== 69 | 70 | //========================================================================================== 71 | // Vertex Shader 72 | //========================================================================================== 73 | GS_INPUT VS_Main(uint id : SV_VertexID) //appdata_base v 74 | { 75 | //https://gamedev.stackexchange.com/questions/139378/unity-simple-pass-through-geometry-shader 76 | GS_INPUT output = (GS_INPUT)0; 77 | 78 | //Put this vertex into World space from it's local space 79 | float4 vPos = float4(_ParticleBuffer[id].position, 1.0f); 80 | output.pos = mul(_Object2World, vPos); 81 | 82 | //Check if particle is active or not 83 | if (_ParticleBuffer[id].active) 84 | { 85 | output.active = true; 86 | } 87 | else 88 | { 89 | output.active = false; 90 | } 91 | 92 | return output; 93 | } 94 | 95 | //========================================================================================== 96 | // Geometry Shader: Make Icosahedron : https://schneide.wordpress.com/2016/07/15/generating-an-icosphere-in-c/ 97 | //========================================================================================== 98 | [maxvertexcount(60)] 99 | void GS_Main(point GS_INPUT p[1], inout TriangleStream triStream) 100 | { 101 | const float X = _Size * .525731112119133606f; 102 | const float Z = _Size * .850650808352039932f; 103 | const float N = 0.f; 104 | 105 | float4 v[12]; 106 | //{-X, N, Z}, { X,N,Z }, { -X,N,-Z }, { X,N,-Z }, 107 | v[0] = float4(float3(p[0].pos.x - X, p[0].pos.y + N, p[0].pos.z + Z), 1.0f); 108 | v[1] = float4(float3(p[0].pos.x + X, p[0].pos.y + N, p[0].pos.z + Z), 1.0f); 109 | v[2] = float4(float3(p[0].pos.x - X, p[0].pos.y + N, p[0].pos.z - Z), 1.0f); 110 | v[3] = float4(float3(p[0].pos.x + X, p[0].pos.y + N, p[0].pos.z - Z), 1.0f); 111 | 112 | //{ N,Z,X }, { N,Z,-X }, { N,-Z,X }, { N,-Z,-X }, 113 | v[4] = float4(float3(p[0].pos.x + N, p[0].pos.y + Z, p[0].pos.z + X), 1.0f); 114 | v[5] = float4(float3(p[0].pos.x + N, p[0].pos.y + Z, p[0].pos.z - X), 1.0f); 115 | v[6] = float4(float3(p[0].pos.x + N, p[0].pos.y - Z, p[0].pos.z + X), 1.0f); 116 | v[7] = float4(float3(p[0].pos.x + N, p[0].pos.y - Z, p[0].pos.z - X), 1.0f); 117 | 118 | //{ Z,X,N }, { -Z,X, N }, { Z,-X,N }, { -Z,-X, N } 119 | v[8] = float4(float3(p[0].pos.x + Z, p[0].pos.y + X, p[0].pos.z + N), 1.0f); 120 | v[9] = float4(float3(p[0].pos.x - Z, p[0].pos.y + X, p[0].pos.z + N), 1.0f); 121 | v[10] = float4(float3(p[0].pos.x + Z, p[0].pos.y - X, p[0].pos.z + N), 1.0f); 122 | v[11] = float4(float3(p[0].pos.x - Z, p[0].pos.y - X, p[0].pos.z + N), 1.0f); 123 | 124 | //Create Quad only particle is ACTIVE 125 | if (p[0].active) 126 | { 127 | float4x4 vp = mul(UNITY_MATRIX_MVP, _World2Object); //put the Quad into View Space 128 | 129 | //Simple UVs for each triangle 130 | // 1 131 | // / \ 132 | // 0---2 133 | 134 | FS_INPUT pIn; 135 | //{0, 4, 1}, { 0,9,4 }, { 9,5,4 }, { 4,5,8 }, { 4,8,1 }, 136 | 137 | pIn.pos = mul(vp, v[0]);//Triangle 1 138 | pIn.uv = float2(0.1f, 0.1f); 139 | triStream.Append(pIn); 140 | pIn.pos = mul(vp, v[4]); 141 | pIn.uv = float2(0.5f, 0.9f); 142 | triStream.Append(pIn); 143 | pIn.pos = mul(vp, v[1]); 144 | pIn.uv = float2(0.9f, 0.9f); 145 | triStream.Append(pIn); 146 | pIn.pos = mul(vp, v[0]);//Triangle 2 147 | pIn.uv = float2(0.1f, 0.1f); 148 | triStream.Append(pIn); 149 | pIn.pos = mul(vp, v[9]); 150 | pIn.uv = float2(0.5f, 0.9f); 151 | triStream.Append(pIn); 152 | pIn.pos = mul(vp, v[4]); 153 | pIn.uv = float2(0.9f, 0.9f); 154 | triStream.Append(pIn); 155 | pIn.pos = mul(vp, v[9]);//Triangle 3 156 | pIn.uv = float2(0.1f, 0.1f); 157 | triStream.Append(pIn); 158 | pIn.pos = mul(vp, v[5]); 159 | pIn.uv = float2(0.5f, 0.9f); 160 | triStream.Append(pIn); 161 | pIn.pos = mul(vp, v[4]); 162 | pIn.uv = float2(0.9f, 0.9f); 163 | triStream.Append(pIn); 164 | pIn.pos = mul(vp, v[4]);//Triangle 4 165 | pIn.uv = float2(0.1f, 0.1f); 166 | triStream.Append(pIn); 167 | pIn.pos = mul(vp, v[5]); 168 | pIn.uv = float2(0.5f, 0.9f); 169 | triStream.Append(pIn); 170 | pIn.pos = mul(vp, v[8]); 171 | pIn.uv = float2(0.9f, 0.9f); 172 | triStream.Append(pIn); 173 | pIn.pos = mul(vp, v[4]);//Triangle 5 174 | pIn.uv = float2(0.1f, 0.1f); 175 | triStream.Append(pIn); 176 | pIn.pos = mul(vp, v[8]); 177 | pIn.uv = float2(0.5f, 0.9f); 178 | triStream.Append(pIn); 179 | pIn.pos = mul(vp, v[1]); 180 | pIn.uv = float2(0.9f, 0.9f); 181 | triStream.Append(pIn); 182 | 183 | //{ 8,10,1 }, { 8,3,10 }, { 5,3,8 }, { 5,2,3 }, { 2,7,3 }, 184 | pIn.pos = mul(vp, v[8]);//Triangle 6 185 | pIn.uv = float2(0.1f, 0.1f); 186 | triStream.Append(pIn); 187 | pIn.pos = mul(vp, v[10]); 188 | pIn.uv = float2(0.5f, 0.9f); 189 | triStream.Append(pIn); 190 | pIn.pos = mul(vp, v[1]); 191 | pIn.uv = float2(0.9f, 0.9f); 192 | triStream.Append(pIn); 193 | pIn.pos = mul(vp, v[8]);//Triangle 7 194 | pIn.uv = float2(0.1f, 0.1f); 195 | triStream.Append(pIn); 196 | pIn.pos = mul(vp, v[3]); 197 | pIn.uv = float2(0.5f, 0.9f); 198 | triStream.Append(pIn); 199 | pIn.pos = mul(vp, v[10]); 200 | pIn.uv = float2(0.9f, 0.9f); 201 | triStream.Append(pIn); 202 | pIn.pos = mul(vp, v[5]);//Triangle 8 203 | pIn.uv = float2(0.1f, 0.1f); 204 | triStream.Append(pIn); 205 | pIn.pos = mul(vp, v[3]); 206 | pIn.uv = float2(0.5f, 0.9f); 207 | triStream.Append(pIn); 208 | pIn.pos = mul(vp, v[8]); 209 | pIn.uv = float2(0.9f, 0.9f); 210 | triStream.Append(pIn); 211 | pIn.pos = mul(vp, v[5]);//Triangle 9 212 | pIn.uv = float2(0.1f, 0.1f); 213 | triStream.Append(pIn); 214 | pIn.pos = mul(vp, v[2]); 215 | pIn.uv = float2(0.5f, 0.9f); 216 | triStream.Append(pIn); 217 | pIn.pos = mul(vp, v[3]); 218 | pIn.uv = float2(0.9f, 0.9f); 219 | triStream.Append(pIn); 220 | pIn.pos = mul(vp, v[2]);//Triangle 10 221 | pIn.uv = float2(0.1f, 0.1f); 222 | triStream.Append(pIn); 223 | pIn.pos = mul(vp, v[7]); 224 | pIn.uv = float2(0.5f, 0.9f); 225 | triStream.Append(pIn); 226 | pIn.pos = mul(vp, v[3]); 227 | pIn.uv = float2(0.9f, 0.9f); 228 | triStream.Append(pIn); 229 | 230 | //{ 7,10,3 }, { 7,6,10 }, { 7,11,6 }, { 11,0,6 }, { 0,1,6 }, 231 | pIn.pos = mul(vp, v[7]);//Triangle 11 232 | pIn.uv = float2(0.1f, 0.1f); 233 | triStream.Append(pIn); 234 | pIn.pos = mul(vp, v[10]); 235 | pIn.uv = float2(0.5f, 0.9f); 236 | triStream.Append(pIn); 237 | pIn.pos = mul(vp, v[3]); 238 | pIn.uv = float2(0.9f, 0.9f); 239 | triStream.Append(pIn); 240 | pIn.pos = mul(vp, v[7]);//Triangle 12 241 | pIn.uv = float2(0.1f, 0.1f); 242 | triStream.Append(pIn); 243 | pIn.pos = mul(vp, v[6]); 244 | pIn.uv = float2(0.5f, 0.9f); 245 | triStream.Append(pIn); 246 | pIn.pos = mul(vp, v[10]); 247 | pIn.uv = float2(0.9f, 0.9f); 248 | triStream.Append(pIn); 249 | pIn.pos = mul(vp, v[7]);//Triangle 13 250 | pIn.uv = float2(0.1f, 0.1f); 251 | triStream.Append(pIn); 252 | pIn.pos = mul(vp, v[11]); 253 | pIn.uv = float2(0.5f, 0.9f); 254 | triStream.Append(pIn); 255 | pIn.pos = mul(vp, v[6]); 256 | pIn.uv = float2(0.9f, 0.9f); 257 | triStream.Append(pIn); 258 | pIn.pos = mul(vp, v[11]);//Triangle 14 259 | pIn.uv = float2(0.1f, 0.1f); 260 | triStream.Append(pIn); 261 | pIn.pos = mul(vp, v[0]); 262 | pIn.uv = float2(0.5f, 0.9f); 263 | triStream.Append(pIn); 264 | pIn.pos = mul(vp, v[6]); 265 | pIn.uv = float2(0.9f, 0.9f); 266 | triStream.Append(pIn); 267 | pIn.pos = mul(vp, v[0]);//Triangle 15 268 | pIn.uv = float2(0.1f, 0.1f); 269 | triStream.Append(pIn); 270 | pIn.pos = mul(vp, v[1]); 271 | pIn.uv = float2(0.5f, 0.9f); 272 | triStream.Append(pIn); 273 | pIn.pos = mul(vp, v[6]); 274 | pIn.uv = float2(0.9f, 0.9f); 275 | triStream.Append(pIn); 276 | 277 | //{ 6,1,10 }, { 9,0,11 }, { 9,11,2 }, { 9,2,5 }, { 7,2,11 } 278 | pIn.pos = mul(vp, v[6]);//Triangle 16 279 | pIn.uv = float2(0.1f, 0.1f); 280 | triStream.Append(pIn); 281 | pIn.pos = mul(vp, v[1]); 282 | pIn.uv = float2(0.5f, 0.9f); 283 | triStream.Append(pIn); 284 | pIn.pos = mul(vp, v[10]); 285 | pIn.uv = float2(0.9f, 0.9f); 286 | triStream.Append(pIn); 287 | pIn.pos = mul(vp, v[9]);//Triangle 17 288 | pIn.uv = float2(0.1f, 0.1f); 289 | triStream.Append(pIn); 290 | pIn.pos = mul(vp, v[0]); 291 | pIn.uv = float2(0.9f, 0.9f); 292 | triStream.Append(pIn); 293 | pIn.pos = mul(vp, v[11]); 294 | pIn.uv = float2(0.5f, 0.9f); 295 | triStream.Append(pIn); 296 | pIn.pos = mul(vp, v[9]);//Triangle 18 297 | pIn.uv = float2(0.1f, 0.1f); 298 | triStream.Append(pIn); 299 | pIn.pos = mul(vp, v[11]); 300 | pIn.uv = float2(0.5f, 0.9f); 301 | triStream.Append(pIn); 302 | pIn.pos = mul(vp, v[2]); 303 | pIn.uv = float2(0.9f, 0.9f); 304 | triStream.Append(pIn); 305 | pIn.pos = mul(vp, v[9]);//Triangle 19 306 | pIn.uv = float2(0.1f, 0.1f); 307 | triStream.Append(pIn); 308 | pIn.pos = mul(vp, v[2]); 309 | pIn.uv = float2(0.5f, 0.9f); 310 | triStream.Append(pIn); 311 | pIn.pos = mul(vp, v[5]); 312 | pIn.uv = float2(0.9f, 0.9f); 313 | triStream.Append(pIn); 314 | pIn.pos = mul(vp, v[7]);//Triangle 20 315 | pIn.uv = float2(0.1f, 0.1f); 316 | triStream.Append(pIn); 317 | pIn.pos = mul(vp, v[2]); 318 | pIn.uv = float2(0.5f, 0.9f); 319 | triStream.Append(pIn); 320 | pIn.pos = mul(vp, v[11]); 321 | pIn.uv = float2(0.9f, 0.9f); 322 | triStream.Append(pIn); 323 | 324 | //used for applying FOG color 325 | UNITY_TRANSFER_FOG(pIn, pIn.pos); 326 | } 327 | } 328 | 329 | //========================================================================================== 330 | // Fragment Shader 331 | //========================================================================================== 332 | float4 FS_Main(FS_INPUT input) : SV_Target//: COLOR 333 | { 334 | float4 colour = float4(_Color.rgb, 1.0f); 335 | float4 second = colour * 0.05f; 336 | 337 | UNITY_APPLY_FOG(input.fogCoord, colour); 338 | 339 | //because this is Unlit add some "3D" to it by lerping colours 340 | float t = (input.uv.x + input.uv.y) * 0.5f; 341 | colour = lerp(colour, second, t); 342 | 343 | return colour; 344 | } 345 | 346 | ENDCG 347 | } 348 | } 349 | } 350 | -------------------------------------------------------------------------------- /Assets/Resources/Shaders/GeoShader_Spheres.shader.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 6158249197f3a394386004d5e2422ec0 3 | timeCreated: 1496534898 4 | licenseType: Pro 5 | ShaderImporter: 6 | defaultTextures: [] 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /Assets/Resources/Shaders/Random.cginc: -------------------------------------------------------------------------------- 1 | #ifndef RANDOM_INCLUDED 2 | #define RANDOM_INCLUDED 3 | 4 | const float PI = 3.14159265358979f; 5 | 6 | float nrand(float2 co){ 7 | return frac(sin(dot(co.xy, float2(12.9898, 78.233))) * 43758.5453); 8 | } 9 | 10 | float nrand(float2 uv, float salt) { 11 | uv += float2(salt, 0.0); 12 | return nrand(uv); 13 | } 14 | 15 | float3 nrand3(float2 seed){ 16 | float t = sin(seed.x + seed.y * 1e3); 17 | return float3(frac(t*1e4), frac(t*1e6), frac(t*1e5)); 18 | } 19 | 20 | float3 random_orth(float2 seed) { 21 | // float u = (nrand(seed) + 1.0) * 0.5; 22 | float u = nrand(seed); 23 | 24 | float3 axis; 25 | 26 | if (u < 0.166) axis = float3(0, 0, 1); 27 | else if (u < 0.332) axis = float3(0, 0, -1); 28 | else if (u < 0.498) axis = float3(0, 1, 0); 29 | else if (u < 0.664) axis = float3(0, -1, 0); 30 | else if (u < 0.83) axis = float3(-1, 0, 0); 31 | else axis = float3(1, 0, 0); 32 | 33 | return axis; 34 | } 35 | 36 | float3 random_positive_orth(float2 seed) { 37 | float u = (nrand(seed) + 1) * 0.5; 38 | 39 | float3 axis; 40 | 41 | if (u < 0.333) axis = float3(0, 0, 1); 42 | else if (u < 0.666) axis = float3(0, 1, 0); 43 | else axis = float3(1, 0, 0); 44 | 45 | return axis; 46 | } 47 | 48 | // Uniformaly distributed points on a unit sphere 49 | // http://mathworld.wolfram.com/SpherePointPicking.html 50 | float3 random_point_on_sphere(float2 uv) { 51 | float u = nrand(uv) * 2 - 1; 52 | float theta = nrand(uv + 0.333) * PI * 2; 53 | float u2 = sqrt(1 - u * u); 54 | return float3(u2 * cos(theta), u2 * sin(theta), u); 55 | } 56 | 57 | #endif // RANDOM_INCLUDED 58 | -------------------------------------------------------------------------------- /Assets/Resources/Shaders/Random.cginc.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 3c080a8faf444d6468a759a7dc40e95a 3 | timeCreated: 1493146452 4 | licenseType: Pro 5 | ShaderImporter: 6 | defaultTextures: [] 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /Assets/Scenes.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: df89d4f7a6555f14f8910a33c7e1b5b0 3 | folderAsset: yes 4 | timeCreated: 1492303457 5 | licenseType: Pro 6 | DefaultImporter: 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /Assets/Scenes/Test Scenes.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: fc13f21865c04a14a87ad4acd81f239a 3 | folderAsset: yes 4 | timeCreated: 1492542672 5 | licenseType: Pro 6 | DefaultImporter: 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /Assets/Scenes/Test Scenes/Flowing Particles.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: e454d897ff799f749bc3918cfcd354d7 3 | folderAsset: yes 4 | timeCreated: 1497296273 5 | licenseType: Free 6 | DefaultImporter: 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /Assets/Scenes/Test Scenes/Flowing Particles.unity: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Brozef92/Unity-GPU-Particles/65ba327ab819822a53d284d783ccbcaf4f4b5623/Assets/Scenes/Test Scenes/Flowing Particles.unity -------------------------------------------------------------------------------- /Assets/Scenes/Test Scenes/Flowing Particles.unity.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 5885b2a7ac8ab8944a8d33fe68c3d58c 3 | timeCreated: 1492907437 4 | licenseType: Pro 5 | DefaultImporter: 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Assets/Scenes/Test Scenes/Flowing Particles/LightingData.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Brozef92/Unity-GPU-Particles/65ba327ab819822a53d284d783ccbcaf4f4b5623/Assets/Scenes/Test Scenes/Flowing Particles/LightingData.asset -------------------------------------------------------------------------------- /Assets/Scenes/Test Scenes/Flowing Particles/LightingData.asset.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 761471d6ae105184ab63c8cf60ae24b0 3 | timeCreated: 1497307610 4 | licenseType: Free 5 | NativeFormatImporter: 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Assets/Scenes/Test Scenes/Flowing Particles/ReflectionProbe-0.exr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Brozef92/Unity-GPU-Particles/65ba327ab819822a53d284d783ccbcaf4f4b5623/Assets/Scenes/Test Scenes/Flowing Particles/ReflectionProbe-0.exr -------------------------------------------------------------------------------- /Assets/Scenes/Test Scenes/Flowing Particles/ReflectionProbe-0.exr.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 2c6594b8922e2724dbccda1863363912 3 | timeCreated: 1497307609 4 | licenseType: Free 5 | TextureImporter: 6 | fileIDToRecycleName: 7 | 8900000: generatedCubemap 8 | serializedVersion: 2 9 | mipmaps: 10 | mipMapMode: 0 11 | enableMipMap: 1 12 | linearTexture: 0 13 | correctGamma: 0 14 | fadeOut: 0 15 | borderMipMap: 0 16 | mipMapFadeDistanceStart: 1 17 | mipMapFadeDistanceEnd: 3 18 | bumpmap: 19 | convertToNormalMap: 0 20 | externalNormalMap: 0 21 | heightScale: 0.25 22 | normalMapFilter: 0 23 | isReadable: 0 24 | grayScaleToAlpha: 0 25 | generateCubemap: 6 26 | cubemapConvolution: 1 27 | cubemapConvolutionSteps: 7 28 | cubemapConvolutionExponent: 1.5 29 | seamlessCubemap: 1 30 | textureFormat: -1 31 | maxTextureSize: 2048 32 | textureSettings: 33 | filterMode: 2 34 | aniso: 0 35 | mipBias: 0 36 | wrapMode: 1 37 | nPOTScale: 1 38 | lightmap: 0 39 | rGBM: 0 40 | compressionQuality: 100 41 | allowsAlphaSplitting: 0 42 | spriteMode: 0 43 | spriteExtrude: 1 44 | spriteMeshType: 1 45 | alignment: 0 46 | spritePivot: {x: 0.5, y: 0.5} 47 | spriteBorder: {x: 0, y: 0, z: 0, w: 0} 48 | spritePixelsToUnits: 100 49 | alphaIsTransparency: 0 50 | textureType: 3 51 | buildTargetSettings: [] 52 | spriteSheet: 53 | sprites: [] 54 | outline: [] 55 | spritePackingTag: 56 | userData: 57 | assetBundleName: 58 | assetBundleVariant: 59 | -------------------------------------------------------------------------------- /Assets/Scenes/Test Scenes/OBB_Testing.unity: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Brozef92/Unity-GPU-Particles/65ba327ab819822a53d284d783ccbcaf4f4b5623/Assets/Scenes/Test Scenes/OBB_Testing.unity -------------------------------------------------------------------------------- /Assets/Scenes/Test Scenes/OBB_Testing.unity.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 6d2017cf22a533040a74c89adde7f749 3 | timeCreated: 1497146638 4 | licenseType: Pro 5 | DefaultImporter: 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Assets/Scenes/Test Scenes/Spherical_Rays.unity: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Brozef92/Unity-GPU-Particles/65ba327ab819822a53d284d783ccbcaf4f4b5623/Assets/Scenes/Test Scenes/Spherical_Rays.unity -------------------------------------------------------------------------------- /Assets/Scenes/Test Scenes/Spherical_Rays.unity.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: fb55d9ed707bb0d449d1aafe6f5b4a4f 3 | timeCreated: 1497306945 4 | licenseType: Free 5 | DefaultImporter: 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Assets/Scenes/Test Scenes/Voxelization_Test.unity: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Brozef92/Unity-GPU-Particles/65ba327ab819822a53d284d783ccbcaf4f4b5623/Assets/Scenes/Test Scenes/Voxelization_Test.unity -------------------------------------------------------------------------------- /Assets/Scenes/Test Scenes/Voxelization_Test.unity.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 137807bea6de1ae4aabfd48427d1f6fc 3 | timeCreated: 1494117584 4 | licenseType: Pro 5 | DefaultImporter: 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Assets/Scripts.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 40835cd304e219341ab1fd524574ff9b 3 | folderAsset: yes 4 | timeCreated: 1492303457 5 | licenseType: Pro 6 | DefaultImporter: 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /Assets/Scripts/Compute_Handlers.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: ee1ab4abd0b65f64b92ab66d4c01c488 3 | folderAsset: yes 4 | timeCreated: 1492303457 5 | licenseType: Pro 6 | DefaultImporter: 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /Assets/Scripts/Compute_Handlers/Particle_Flowing.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Collections; 3 | using System.Runtime.InteropServices; //used for sizeof fucntion because C# can't safely use sizeof() 4 | 5 | [RequireComponent(typeof(Baked_Octree))] 6 | public class Particle_Flowing : MonoBehaviour 7 | { 8 | //Solution to reading ComputeBuffer in a shader found at:http://answers.unity3d.com/questions/1235815/unity-shader-writing-and-reading-computebuffer-in.html 9 | 10 | //Integration http://gafferongames.com/game-physics/integration-basics/ 11 | 12 | //Also using code from: https://github.com/hecomi/UnityPseudoInstancedGPUParticles/blob/master/Assets/Screen%20Space%20Collision%20GPU%20Particles/Scripts/PseudoInstancedGPUParticleManager.cs 13 | // : https://github.com/keijiro/KvantSpray 14 | 15 | 16 | const int JOB_SIZE = 256; // Number of particle to update concurrently...this MUST match compute kernel(s)!!!! 17 | 18 | //============================================================================================================================================= 19 | //Structs 20 | //============================================================================================================================================= 21 | public struct Particle 22 | { 23 | public float invMass; 24 | public float lifeTime; 25 | public Vector3 pos; 26 | public Vector3 vel; 27 | public Vector3 acc; 28 | public bool active; 29 | }; 30 | 31 | //============================================================================================================================================= 32 | //Privates 33 | //============================================================================================================================================= 34 | 35 | //Kernel IDs of the Compute shader compiled functions, These are used to Dispatch the correct function(kernel) 36 | int mKernelID_Init, mKernelID_Emit, mKernelID_Update; 37 | 38 | ComputeBuffer CB_particleBuffer; 39 | ComputeBuffer CB_particlePool; //All the "dead" particles 40 | ComputeBuffer CB_particleCounterCopy; //used to Copy particlePool counter, it is on GPU memory so I think this is how you find out how many elements are in it 41 | ComputeBuffer CB_Octree; //A spare octree that approximates scene geometry 42 | int[] particleCounter; //used to get how many particles are inside the Append/Consume Buffers 43 | 44 | int mJobSlice; // How many particles can be worked on at a time 45 | 46 | Vector3 EmitterPoint = Vector3.zero; 47 | Vector4 EmitterDir = new Vector4(0.0f, 1.0f, 0.0f, 0.5f); //Normalized vector.xyz, spread.w 48 | 49 | bool isInitialized = false; 50 | bool isReleased = false; 51 | 52 | public Baked_Octree octree; //a pre-baked octree as a prefab 53 | public string Octree_File; //name of the file 54 | 55 | //============================================================================================================================================= 56 | //Publics 57 | //============================================================================================================================================= 58 | public int particleCount = 100000; 59 | public float ParticleMass = 0.1f; 60 | public float particleLifeTime = 10.0f; 61 | [Range(5.0f, 50.0f)] 62 | public float particleFlowRate = 10.0f; //How many particles are emitted at once 63 | public float particleInitialSpeed = 4.0f; //initial velocity scalar 64 | [Range(0.0f, 1.0f)] 65 | public float particleBounciness = 0.95f; 66 | [Range(0.0f, 1.0f)] 67 | public float SpeedRandomness = 0.5f; //Viscous material maybe 68 | public Material material; // Material used to draw the Particle on screen. using special Geometry shader 69 | public ComputeShader computeShader;// Compute shader used to update the Particles. 70 | public GameObject Emitter; //where the particle are spawned 71 | public Vector3 EmitterSize = Vector3.one; 72 | [Range(0.05f, 0.3f)] 73 | public float EmitterSpread = 0.2f; 74 | public Vector3 Gravity = new Vector3(0.0f, -9.81f, 0.0f); 75 | 76 | public float CollisionThreshold = 1.5f; 77 | 78 | //============================================================================================================================================= 79 | //Helper function to determine how many particles are in the ParticlePoolBuffer on the GPU 80 | //============================================================================================================================================= 81 | int GetParticlePoolSize() 82 | { 83 | CB_particleCounterCopy.SetData(particleCounter); 84 | ComputeBuffer.CopyCount(CB_particlePool, CB_particleCounterCopy, 0); 85 | CB_particleCounterCopy.GetData(particleCounter); 86 | return particleCounter[0]; 87 | } 88 | 89 | //============================================================================================================================================= 90 | //Helper function used to clean up Compute buffers 91 | //============================================================================================================================================= 92 | void ReleaseBuffers() 93 | { 94 | //I think these are COM objects so you have to release them when finished 95 | if(CB_Octree != null) 96 | { 97 | CB_Octree.Release(); 98 | } 99 | if (CB_particleBuffer != null) 100 | { 101 | CB_particleBuffer.Release(); 102 | } 103 | if (CB_particlePool != null) 104 | { 105 | CB_particlePool.Release(); 106 | } 107 | if (CB_particleCounterCopy != null) 108 | { 109 | CB_particleCounterCopy.Release(); 110 | } 111 | 112 | isReleased = true; 113 | } 114 | 115 | //============================================================================================================================================= 116 | //Helper function used to initialize the Compute Shader 117 | //============================================================================================================================================= 118 | void Initialize_ComputeShader () 119 | { 120 | // Calculate the number of warp needed to handle all the particles 121 | if (particleCount <= 0) 122 | { 123 | particleCount = 10000; 124 | } 125 | 126 | mJobSlice = Mathf.CeilToInt((float)particleCount / JOB_SIZE); 127 | 128 | if (Emitter) 129 | { 130 | EmitterPoint = Emitter.transform.position; 131 | EmitterPoint.y -= Emitter.transform.localScale.y; 132 | EmitterDir = new Vector4(Emitter.transform.up.x, Emitter.transform.up.y, Emitter.transform.up.z, EmitterSpread); 133 | } 134 | else 135 | { 136 | EmitterDir.w = EmitterSpread; 137 | } 138 | 139 | //Now load a pre-baked Octree from a file 140 | octree.FileName = Octree_File; 141 | octree.Load_Tree(); 142 | 143 | if(octree.Initialized == false) 144 | { 145 | Debug.Log("Failed to Load " + "Assets/Resources/Baked_Octrees/" + Octree_File + ".txt"); 146 | return; 147 | } 148 | 149 | CB_Octree = new ComputeBuffer(octree.nodeArray.Length, Voxelizer.SDF_Node.Stride, ComputeBufferType.Default); 150 | CB_Octree.SetData(octree.nodeArray); 151 | 152 | // Create the ComputeBuffers holding the Particles on the GPU 153 | CB_particleBuffer = new ComputeBuffer(particleCount, Marshal.SizeOf(typeof(Particle))); 154 | CB_particlePool = new ComputeBuffer(particleCount, sizeof(int), ComputeBufferType.Append); //Buffer is like a Stack for Appending/Consuming things on the GPU 155 | //CB_particlePool.SetCounterValue(0); //This shouldn't even be necessary??? 156 | CB_particleCounterCopy = new ComputeBuffer(4, sizeof(int), ComputeBufferType.DrawIndirect); //.IndirectArguments for Unity 5.4 and up 157 | particleCounter = new int[] { 0, 1, 0, 0 }; 158 | 159 | //Set Compute shader varriables..if you want to dynamically change these then you have to set these in FixedUpdate every frame 160 | computeShader.SetVector("_EmitterPos", EmitterPoint); //after particle is "dead" the computeShader will re-emit it 161 | computeShader.SetVector("_EmitterSize", EmitterSize); 162 | computeShader.SetVector("_EmitterDirection", EmitterDir); 163 | computeShader.SetFloat("ParticleLifeTime", particleLifeTime); 164 | computeShader.SetFloat("InvParticleMass", ParticleMass); 165 | computeShader.SetFloat("_ParticleSpeed", particleInitialSpeed); 166 | computeShader.SetFloat("_SpeedRandomness", SpeedRandomness); 167 | computeShader.SetFloat("_ParticleBounce", particleBounciness); 168 | computeShader.SetVector("_Gravity", Gravity); 169 | computeShader.SetFloat("CollisionThreshold", CollisionThreshold); 170 | 171 | float particleRadius = material.GetFloat("_Size"); 172 | computeShader.SetFloat("_ParticleRadius", particleRadius); 173 | 174 | //Set the SDF octree that was made earlier 175 | if (CB_Octree != null) 176 | { 177 | int updateKernel = computeShader.FindKernel("CS_Update"); 178 | 179 | computeShader.SetInt("Octree_Size", CB_Octree.count); 180 | computeShader.SetInt("Octree_Depth", octree.TreeDepth); 181 | computeShader.SetBuffer(updateKernel, "SDF_Buffer", CB_Octree); 182 | 183 | Debug.Log("OCTREE SIZE = " + Voxelizer.SDF_Node.Stride * CB_Octree.count + " BYTES"); 184 | Debug.Log("OCTREE DEPTH = " + octree.TreeDepth); 185 | } 186 | else 187 | { 188 | computeShader.SetInt("Octree_Size", 0); 189 | computeShader.SetInt("Octree_Depth", 0); 190 | } 191 | 192 | isInitialized = true; 193 | 194 | //Dispatch the Initialize kernel on the GPU this will initialize all our particles 195 | Dispatch_Init(); 196 | } 197 | 198 | //============================================================================================================================================= 199 | //Called when this script is Enabled/Disabled or Object is destroyed 200 | //============================================================================================================================================= 201 | void OnEnabled() 202 | { 203 | if(!isInitialized) 204 | { 205 | Debug.Log("OnEnabled.Initialzing"); 206 | Initialize_ComputeShader(); 207 | } 208 | } 209 | 210 | void OnDisable() 211 | { 212 | if(!isReleased) 213 | { 214 | Debug.Log("OnDisable.Releasing"); 215 | ReleaseBuffers(); 216 | isInitialized = false; 217 | } 218 | } 219 | 220 | void OnDestroy() 221 | { 222 | if(!isReleased) 223 | { 224 | Debug.Log("OnDestroy.Releasing"); 225 | ReleaseBuffers(); 226 | isInitialized = false; 227 | } 228 | } 229 | 230 | //============================================================================================================================================= 231 | //All Dispatch functions to run our Compute Shader functions(kernels) 232 | //============================================================================================================================================= 233 | void Dispatch_Init() 234 | { 235 | mKernelID_Init = computeShader.FindKernel("CS_Initialize"); 236 | //Debug.Log("Kernel_Init: " + mKernelID_Init.ToString()); 237 | 238 | computeShader.SetBuffer(mKernelID_Init, "particleBuffer", CB_particleBuffer); 239 | computeShader.SetBuffer(mKernelID_Init, "deadParticles", CB_particlePool); 240 | computeShader.Dispatch(mKernelID_Init, mJobSlice, 1, 1); //Launch!! 241 | } 242 | 243 | void Dispatch_Emit(int ParticleGroup) 244 | { 245 | mKernelID_Emit = computeShader.FindKernel("CS_Emit"); 246 | //Debug.Log("Kernel_Emit: " + mKernelID_Emit.ToString()); 247 | 248 | computeShader.SetBuffer(mKernelID_Emit, "particleBuffer", CB_particleBuffer); 249 | computeShader.SetBuffer(mKernelID_Emit, "particlePool", CB_particlePool); 250 | computeShader.SetFloat("deltaTime", Time.deltaTime); 251 | 252 | //Find out how many particles to emit 253 | int numParticles = GetParticlePoolSize(); 254 | int jobSlice = Mathf.Min(ParticleGroup, numParticles / JOB_SIZE); 255 | 256 | if(jobSlice > 0) 257 | { 258 | computeShader.Dispatch(mKernelID_Emit, jobSlice, 1, 1); //Launch!! 259 | } 260 | } 261 | 262 | void Dispatch_Update() 263 | { 264 | mKernelID_Update = computeShader.FindKernel("CS_Update"); 265 | //Debug.Log("Kernel_Update: " + mKernelID_Update.ToString()); 266 | 267 | computeShader.SetFloat("deltaTime", Time.deltaTime); 268 | computeShader.SetBuffer(mKernelID_Update, "particleBuffer", CB_particleBuffer); 269 | computeShader.SetBuffer(mKernelID_Update, "deadParticles", CB_particlePool); 270 | computeShader.Dispatch(mKernelID_Update, mJobSlice, 1, 1); //Launch!! mJobSlice 271 | } 272 | 273 | //============================================================================================================================================= 274 | //Mono Functions 275 | //============================================================================================================================================= 276 | void Start() 277 | { 278 | if(!isInitialized) 279 | { 280 | Debug.Log("Start.initializing"); 281 | Initialize_ComputeShader(); 282 | } 283 | else 284 | { 285 | Debug.Log("Start.Reinitializing"); 286 | ReleaseBuffers(); 287 | Initialize_ComputeShader(); 288 | } 289 | } 290 | 291 | void FixedUpdate() 292 | { 293 | //Dispatch both kernels in the compute shader 294 | 295 | if(isInitialized) 296 | { 297 | if (Emitter.activeSelf) 298 | { 299 | int particleCount = Mathf.CeilToInt(particleFlowRate); 300 | Dispatch_Emit(particleCount); 301 | } 302 | 303 | Dispatch_Update(); 304 | } 305 | } 306 | 307 | //============================================================================================================================================= 308 | //OnRenderObject is called AFTER camera has rendered the scene. 309 | //============================================================================================================================================= 310 | void OnRenderObject() 311 | { 312 | if (Camera.current == Camera.main) 313 | { 314 | material.SetBuffer("_ParticleBuffer", CB_particleBuffer); 315 | 316 | //Debug.Log("Rendering Geometry!!!"); 317 | material.SetPass(0); 318 | 319 | //Draws mesh on the GPU in a geometry shader 320 | //Draw Points(In the Geo Shader it will turn them to Quads), vertex Count = 1, and Instance count = NumParticles 321 | Graphics.DrawProcedural(MeshTopology.Points, particleCount); 322 | } 323 | } 324 | 325 | //Optionally Draw Tree in the Editor 326 | void OnDrawGizmos() 327 | { 328 | if(octree) 329 | { 330 | octree.DrawVoxelGrid(Color.black); 331 | } 332 | } 333 | } 334 | -------------------------------------------------------------------------------- /Assets/Scripts/Compute_Handlers/Particle_Flowing.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 97bc311882c60404abefda5ad4c9a71d 3 | timeCreated: 1492907449 4 | licenseType: Pro 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Assets/Scripts/Octree.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 9e1537de38c2eac478e09301488a1822 3 | folderAsset: yes 4 | timeCreated: 1497229500 5 | licenseType: Free 6 | DefaultImporter: 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /Assets/Scripts/Octree/Baked_Octree.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Collections; 3 | using System.Collections.Generic; //used for List 4 | using System.IO; //for reading/writing files 5 | 6 | //This class reads a file and Builds a Sparse Octree array based on that file 7 | 8 | public class Baked_Octree : MonoBehaviour 9 | { 10 | [HideInInspector] 11 | public string FileName; 12 | 13 | [HideInInspector] 14 | public Vector3 Position; //the center of this Octree 15 | 16 | [HideInInspector] 17 | public Voxelizer.SDF_Node[] nodeArray; 18 | 19 | [HideInInspector] 20 | public float Root_Size; 21 | float Smallest_Size; 22 | 23 | [HideInInspector] 24 | public int TreeDepth; 25 | 26 | [HideInInspector] 27 | public bool Initialized = false; 28 | 29 | [HideInInspector] 30 | public bool DrawTree = false; 31 | 32 | [HideInInspector] 33 | public bool DrawNormals = true; 34 | 35 | [HideInInspector] 36 | public bool DrawNodes = true; 37 | 38 | //Material for drawing the grid 39 | public Material LineMaterial; 40 | //use this to draw all the lines in one single draw call 41 | List All_Lines = null; 42 | 43 | //============================================================================================================================================= 44 | //simple structs used for Debugging here 45 | //============================================================================================================================================= 46 | //used to make a cube into Lines 47 | public struct CUBE 48 | { 49 | public Vector3 v0, v1, v2, v3, v4, v5, v6, v7; 50 | } 51 | //used to draw Lines using GL lines 52 | private struct LINE 53 | { 54 | public LINE(Vector3 a, Vector3 b, bool n) 55 | { 56 | p1 = a; 57 | p2 = b; 58 | isNormal = n; 59 | } 60 | public Vector3 p1, p2; 61 | public bool isNormal; 62 | } 63 | 64 | //============================================================================================================================================= 65 | //called from Particles Start() function 66 | //============================================================================================================================================= 67 | public void Load_Tree() 68 | { 69 | Initialized = false; 70 | //read from file 71 | string path = "Assets/Resources/Baked_Octrees/" + FileName + ".txt"; 72 | string SavedTree = System.IO.File.ReadAllText(path);// PlayerPrefs.GetString(FileName); 73 | 74 | if (SavedTree != "") 75 | { 76 | //read the tree 77 | // Depth,Count;RootSize/Node[0]/Node[1]/..../Node[N] 78 | 79 | string word = ""; 80 | char c = ' '; 81 | int j = 0; 82 | int NodeCount = 0; 83 | 84 | //First read all the header stuff before reading each node 85 | for(int i = 0; i < SavedTree.Length; ++i) 86 | { 87 | c = SavedTree[i]; 88 | 89 | //check for delimeters 90 | if (c == '/') 91 | { 92 | Root_Size = float.Parse(word); 93 | word = ""; 94 | j = i + 1; //ignore starting '/' 95 | break; 96 | } 97 | else if (c == ',') 98 | { 99 | TreeDepth = int.Parse(word); 100 | word = ""; 101 | } 102 | else if (c == ';') 103 | { 104 | NodeCount = int.Parse(word); 105 | word = ""; 106 | } 107 | else 108 | word += c; 109 | } 110 | 111 | //Debug.Log(TreeDepth); 112 | //Debug.Log(NodeCount); 113 | //Debug.Log(Root_Size); 114 | 115 | nodeArray = new Voxelizer.SDF_Node[NodeCount]; 116 | int nI = 0; 117 | 118 | //Now read all the Nodes 119 | for (int k = j; k < SavedTree.Length; ++k) 120 | { 121 | c = SavedTree[k]; 122 | 123 | if (c == '/') 124 | { 125 | nodeArray[nI] = ParseNode(word); 126 | //Debug.Log(nodeArray[nI].ToString()); 127 | word = ""; 128 | nI++; 129 | } 130 | else if(c == '*') //end of nodes 131 | { 132 | nodeArray[nI] = ParseNode(word); 133 | //Debug.Log(nodeArray[nI].ToString()); 134 | j = k + 1; 135 | word = ""; 136 | break; 137 | } 138 | else 139 | word += c; 140 | } 141 | 142 | //finally read it's position 143 | Position = new Vector3(); 144 | int axis = 0; 145 | for(int k = j; k < SavedTree.Length; ++k) 146 | { 147 | c = SavedTree[k]; 148 | if (c == ',') 149 | { 150 | switch (axis) 151 | { 152 | case 0: 153 | Position.x = float.Parse(word); 154 | break; 155 | case 1: 156 | Position.y = float.Parse(word); 157 | break; 158 | case 2: 159 | Position.z = float.Parse(word); 160 | break; 161 | default: 162 | break; 163 | } 164 | word = ""; 165 | axis++; 166 | } 167 | else 168 | word += c; 169 | } 170 | 171 | } //End of Reading nodeArray from string/file 172 | 173 | //Now build the Octree from lines 174 | if (All_Lines == null) 175 | { 176 | All_Lines = new List(); 177 | } 178 | 179 | Smallest_Size = Root_Size * 2.0f; 180 | for(int i = TreeDepth; i >= 0; --i) 181 | { 182 | Smallest_Size *= 0.5f; 183 | } 184 | 185 | //Build the Nodes into an List of Lines so we can Draw them all at once 186 | BuildNodeLines(); 187 | 188 | //Log_NodeArray(); 189 | 190 | DrawTree = false; //Drawing a massive Tree is Costly as Fuck even if it's a single draw call 191 | Initialized = true; 192 | } 193 | 194 | //============================================================================================================================================= 195 | //helper function to read a string and format it into a Node 196 | //============================================================================================================================================= 197 | Voxelizer.SDF_Node ParseNode(string word) 198 | { 199 | Voxelizer.SDF_Node node = new Voxelizer.SDF_Node(); 200 | 201 | int item = 0; 202 | string str = ""; 203 | word += ','; //required to get through all items 204 | char c = word[0]; 205 | 206 | //Node Format : int,int[8],Vector3,Vector3,Vector3 207 | for (int i = 0; i < word.Length; ++i) 208 | { 209 | c = word[i]; 210 | 211 | if(c == ',') 212 | { 213 | switch (item) 214 | { 215 | case 0: 216 | node.index = int.Parse(str); 217 | break; 218 | case 1: //Node Index 219 | node.c0 = int.Parse(str); 220 | break; 221 | case 2: //Node Children 222 | node.c1 = int.Parse(str); 223 | break; 224 | case 3: 225 | node.c2 = int.Parse(str); 226 | break; 227 | case 4: 228 | node.c3 = int.Parse(str); 229 | break; 230 | case 5: 231 | node.c4 = int.Parse(str); 232 | break; 233 | case 6: 234 | node.c5 = int.Parse(str); 235 | break; 236 | case 7: 237 | node.c6 = int.Parse(str); 238 | break; 239 | case 8: 240 | node.c7 = int.Parse(str); 241 | break; 242 | case 9: //Node Min 243 | node.Min.x = float.Parse(str); 244 | break; 245 | case 10: 246 | node.Min.y = float.Parse(str); 247 | break; 248 | case 11: 249 | node.Min.z = float.Parse(str); 250 | break; 251 | case 12: //Node Max 252 | node.Max.x = float.Parse(str); 253 | break; 254 | case 13: 255 | node.Max.y = float.Parse(str); 256 | break; 257 | case 14: 258 | node.Max.z = float.Parse(str); 259 | break; 260 | case 15: //Node SurfacePoint 261 | node.SurfacePoint.x = float.Parse(str); 262 | break; 263 | case 16: 264 | node.SurfacePoint.y = float.Parse(str); 265 | break; 266 | case 17: //never gets called.... 267 | node.SurfacePoint.z = float.Parse(str); 268 | break; 269 | default: 270 | break; 271 | } 272 | str = ""; 273 | item++; 274 | } 275 | else 276 | { 277 | str += c; 278 | } 279 | } 280 | return node; 281 | } 282 | 283 | //============================================================================================================================================= 284 | //Helper function to debug the nodeArray..To see if the Indexing is correct 285 | //============================================================================================================================================= 286 | void Log_NodeArray() 287 | { 288 | Debug.Log("Tree Size = " + nodeArray.Length); 289 | //for safety... Using Debug.Log that much is slow as hell 290 | if (nodeArray.Length > 500) 291 | return; 292 | 293 | for (int i = 0; i < nodeArray.Length; ++i) 294 | { 295 | Debug.Log("Node[" + i + "] index: " + nodeArray[i].index + " AABB: " + nodeArray[i].Min + ", " + nodeArray[i].Max); 296 | 297 | if (nodeArray[i].c0 == -1) 298 | { 299 | Debug.Log("\tNode [" + i + "] child[0]: " + nodeArray[i].c0); 300 | } 301 | else 302 | { 303 | if (nodeArray[nodeArray[i].c0].c0 != -1) 304 | { 305 | Debug.Log("\tNode [" + i + "] child[0]: " + nodeArray[i].c0 + "_[8]"); 306 | } 307 | else 308 | { 309 | Debug.Log("\tNode [" + i + "] child[0]: " + nodeArray[i].c0 + "_[0]"); 310 | } 311 | 312 | } 313 | if (nodeArray[i].c1 == -1) 314 | { 315 | Debug.Log("\tNode [" + i + "] child[1]: " + nodeArray[i].c1); 316 | } 317 | else 318 | { 319 | if (nodeArray[nodeArray[i].c1].c0 != -1) 320 | { 321 | Debug.Log("\tNode [" + i + "] child[1]: " + nodeArray[i].c1 + "_[8]"); 322 | } 323 | else 324 | { 325 | Debug.Log("\tNode [" + i + "] child[1]: " + nodeArray[i].c1 + "_[0]"); 326 | } 327 | } 328 | if (nodeArray[i].c2 == -1) 329 | { 330 | Debug.Log("\tNode [" + i + "] child[2]: " + nodeArray[i].c2); 331 | } 332 | else 333 | { 334 | if (nodeArray[nodeArray[i].c2].c0 != -1) 335 | { 336 | Debug.Log("\tNode [" + i + "] child[2]: " + nodeArray[i].c2 + "_[8]"); 337 | } 338 | else 339 | { 340 | Debug.Log("\tNode [" + i + "] child[2]: " + nodeArray[i].c2 + "_[0]"); 341 | } 342 | } 343 | if (nodeArray[i].c3 == -1) 344 | { 345 | Debug.Log("\tNode [" + i + "] child[3]: " + nodeArray[i].c3); 346 | } 347 | else 348 | { 349 | if (nodeArray[nodeArray[i].c3].c0 != -1) 350 | { 351 | Debug.Log("\tNode [" + i + "] child[3]: " + nodeArray[i].c3 + "_[8]"); 352 | } 353 | else 354 | { 355 | Debug.Log("\tNode [" + i + "] child[3]: " + nodeArray[i].c3 + "_[0]"); 356 | } 357 | } 358 | if (nodeArray[i].c4 == -1) 359 | { 360 | Debug.Log("\tNode [" + i + "] child[4]: " + nodeArray[i].c4); 361 | } 362 | else 363 | { 364 | if (nodeArray[nodeArray[i].c4].c0 != -1) 365 | { 366 | Debug.Log("\tNode [" + i + "] child[4]: " + nodeArray[i].c4 + "_[8]"); 367 | } 368 | else 369 | { 370 | Debug.Log("\tNode [" + i + "] child[4]: " + nodeArray[i].c4 + "_[0]"); 371 | } 372 | } 373 | if (nodeArray[i].c5 == -1) 374 | { 375 | Debug.Log("\tNode [" + i + "] child[5]: " + nodeArray[i].c5); 376 | } 377 | else 378 | { 379 | if (nodeArray[nodeArray[i].c5].c0 != -1) 380 | { 381 | Debug.Log("\tNode [" + i + "] child[5]: " + nodeArray[i].c5 + "_[8]"); 382 | } 383 | else 384 | { 385 | Debug.Log("\tNode [" + i + "] child[5]: " + nodeArray[i].c5 + "_[0]"); 386 | } 387 | } 388 | if (nodeArray[i].c6 == -1) 389 | { 390 | Debug.Log("\tNode [" + i + "] child[6]: " + nodeArray[i].c6); 391 | } 392 | else 393 | { 394 | if (nodeArray[nodeArray[i].c6].c0 != -1) 395 | { 396 | Debug.Log("\tNode [" + i + "] child[6]: " + nodeArray[i].c6 + "_[8]"); 397 | } 398 | else 399 | { 400 | Debug.Log("\tNode [" + i + "] child[6]: " + nodeArray[i].c6 + "_[0]"); 401 | } 402 | } 403 | if (nodeArray[i].c7 == -1) 404 | { 405 | Debug.Log("\tNode [" + i + "] child[7]: " + nodeArray[i].c7); 406 | } 407 | else 408 | { 409 | if (nodeArray[nodeArray[i].c7].c0 != -1) 410 | { 411 | Debug.Log("\tNode [" + i + "] child[7]: " + nodeArray[i].c7 + "_[8]"); 412 | } 413 | else 414 | { 415 | Debug.Log("\tNode [" + i + "] child[7]: " + nodeArray[i].c7 + "_[0]"); 416 | } 417 | } 418 | } 419 | } 420 | 421 | //===================================================================================================================== 422 | //Debug drawing functions 423 | //===================================================================================================================== 424 | 425 | public void DrawVoxelGrid(Color colour) 426 | { 427 | if (LineMaterial) 428 | { 429 | if (DrawTree && Initialized) 430 | { 431 | DrawAll_Lines(colour); 432 | } 433 | } 434 | } 435 | 436 | void BuildNodeLines() 437 | { 438 | for(int i = 0; i < nodeArray.Length; ++i) 439 | { 440 | 441 | Voxelizer.SDF_Node n = nodeArray[i]; 442 | 443 | // the Cube 444 | float s = (n.Max.x - n.Min.x) * 0.5f; 445 | Vector3 p = new Vector3(n.Min.x + s, n.Min.y + s, n.Min.z + s); 446 | 447 | /* 448 | * 0---2 449 | * / /| 450 | * 1---3 | 451 | * | 4 : 6 452 | * 5---7/ 453 | * 454 | * Y Z 455 | * | / 456 | * |/___X 457 | */ 458 | 459 | CUBE c; 460 | //top 461 | c.v0 = new Vector3(p.x - s, p.y + s, p.z + s); 462 | c.v1 = new Vector3(p.x - s, p.y + s, p.z - s); 463 | c.v2 = new Vector3(p.x + s, p.y + s, p.z + s); 464 | c.v3 = new Vector3(p.x + s, p.y + s, p.z - s); 465 | //bottom 466 | c.v4 = new Vector3(p.x - s, p.y - s, p.z + s); 467 | c.v5 = new Vector3(p.x - s, p.y - s, p.z - s); 468 | c.v6 = new Vector3(p.x + s, p.y - s, p.z + s); 469 | c.v7 = new Vector3(p.x + s, p.y - s, p.z - s); 470 | 471 | AddCube(c); 472 | 473 | //at lowest nodes draw the Nearest surface Vector 474 | if (n.c0 == -1 && s*2.0f <= Smallest_Size) 475 | { 476 | AddLine(p, n.SurfacePoint, true); 477 | } 478 | } 479 | } 480 | 481 | //using GL Lines : https://gamedev.stackexchange.com/questions/96964/how-to-correctly-draw-a-line-in-unity 482 | void AddCube(CUBE c) 483 | { 484 | 485 | //Top square 486 | All_Lines.Add(new LINE(c.v0, c.v1, false)); 487 | All_Lines.Add(new LINE(c.v1, c.v3, false)); 488 | All_Lines.Add(new LINE(c.v3, c.v2, false)); 489 | All_Lines.Add(new LINE(c.v2, c.v0, false)); 490 | 491 | //Bottom square 492 | All_Lines.Add(new LINE(c.v4, c.v5, false)); 493 | All_Lines.Add(new LINE(c.v5, c.v7, false)); 494 | All_Lines.Add(new LINE(c.v7, c.v6, false)); 495 | All_Lines.Add(new LINE(c.v6, c.v4, false)); 496 | 497 | //Connecting Top and Bottom 498 | All_Lines.Add(new LINE(c.v0, c.v4, false)); 499 | All_Lines.Add(new LINE(c.v1, c.v5, false)); 500 | All_Lines.Add(new LINE(c.v3, c.v7, false)); 501 | All_Lines.Add(new LINE(c.v2, c.v6, false)); 502 | } 503 | 504 | //Add a line to our List 505 | void AddLine(Vector3 p, Vector3 p2, bool normal) 506 | { 507 | if (p2 == Vector3.zero) 508 | return; //un-initialized Bottom node 509 | else 510 | All_Lines.Add(new LINE(p, p2, normal)); 511 | } 512 | 513 | void DrawAll_Lines(Color colour) 514 | { 515 | if (All_Lines == null) 516 | return; 517 | 518 | GL.PushMatrix(); 519 | LineMaterial.SetPass(0); 520 | LineMaterial.SetColor("_Color", colour); 521 | GL.Color(colour); 522 | GL.Begin(GL.LINES); 523 | for (int i = 0; i < All_Lines.Count; ++i) 524 | { 525 | if(All_Lines[i].isNormal && DrawNormals) 526 | { 527 | GL.Vertex(All_Lines[i].p1); 528 | GL.Vertex(All_Lines[i].p2); 529 | } 530 | else if(!All_Lines[i].isNormal && DrawNodes) 531 | { 532 | GL.Vertex(All_Lines[i].p1); 533 | GL.Vertex(All_Lines[i].p2); 534 | } 535 | } 536 | 537 | GL.End(); 538 | GL.PopMatrix(); 539 | } 540 | 541 | //Helper function to draw OBB using Gizmos class 542 | static public void Draw_OBB(Octree.OBB obb) 543 | { 544 | Vector3[] Positions = new Vector3[8]; 545 | Vector3 v = new Vector3(obb.e.x * obb.s.x, obb.e.y * obb.s.y, obb.e.z * obb.s.z); 546 | 547 | Positions[0] = obb.c + (((obb.x - obb.c) * v.x) + ((obb.y - obb.c) * v.y) + ((obb.z - obb.c) * v.z)); 548 | Positions[1] = obb.c + (((obb.c - obb.x) * v.x) + ((obb.y - obb.c) * v.y) + ((obb.z - obb.c) * v.z)); 549 | Positions[2] = obb.c + (((obb.x - obb.c) * v.x) + ((obb.c - obb.y) * v.y) + ((obb.z - obb.c) * v.z)); 550 | Positions[3] = obb.c + (((obb.c - obb.x) * v.x) + ((obb.c - obb.y) * v.y) + ((obb.z - obb.c) * v.z)); 551 | 552 | Positions[4] = obb.c + (((obb.x - obb.c) * v.x) + ((obb.y - obb.c) * v.y) + ((obb.c - obb.z) * v.z)); 553 | Positions[5] = obb.c + (((obb.c - obb.x) * v.x) + ((obb.y - obb.c) * v.y) + ((obb.c - obb.z) * v.z)); 554 | Positions[6] = obb.c + (((obb.x - obb.c) * v.x) + ((obb.c - obb.y) * v.y) + ((obb.c - obb.z) * v.z)); 555 | Positions[7] = obb.c + (((obb.c - obb.x) * v.x) + ((obb.c - obb.y) * v.y) + ((obb.c - obb.z) * v.z)); 556 | 557 | Gizmos.color = Color.green; 558 | Gizmos.DrawLine(Positions[0], Positions[1]); 559 | Gizmos.DrawLine(Positions[1], Positions[3]); 560 | Gizmos.DrawLine(Positions[3], Positions[2]); 561 | Gizmos.DrawLine(Positions[2], Positions[0]); 562 | 563 | Gizmos.DrawLine(Positions[4], Positions[5]); 564 | Gizmos.DrawLine(Positions[5], Positions[7]); 565 | Gizmos.DrawLine(Positions[7], Positions[6]); 566 | Gizmos.DrawLine(Positions[6], Positions[4]); 567 | //Connectors 568 | Gizmos.DrawLine(Positions[0], Positions[4]); 569 | Gizmos.DrawLine(Positions[1], Positions[5]); 570 | Gizmos.DrawLine(Positions[3], Positions[7]); 571 | Gizmos.DrawLine(Positions[2], Positions[6]); 572 | } 573 | } 574 | -------------------------------------------------------------------------------- /Assets/Scripts/Octree/Baked_Octree.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 8f5aaf1369195124ca2f9e553762347f 3 | timeCreated: 1497229539 4 | licenseType: Free 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Assets/Scripts/Octree/Octree.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | 5 | //======================================================================================================================== 6 | //Following this guy: https://www.youtube.com/watch?v=m0guE7804to 7 | //Thank you!!!! 8 | //======================================================================================================================== 9 | 10 | 11 | //======================================================================================================================== 12 | //Structure to used for Signed Distance Fields 13 | //======================================================================================================================== 14 | public class SDF_Struct 15 | { 16 | public Vector3 mPosition; //position of the CubeNode(Octet) 17 | public float mSize; //size of the Cube (X,Y,Z) 18 | public Vector3 mPoint; //Point on the nearest geometry 19 | public float mDistance; //distance to nearest Geometry surface...this isn't really necessary 20 | } 21 | 22 | public enum OctreeIndex //An OctreeNode is sub-divided into 8 smaller Cubes 23 | { 24 | /* 25 | * 0---2 26 | * / /| 27 | * 1---3 | 28 | * | 4 : 6 29 | * 5---7/ 30 | * 31 | * Y Z 32 | * | / 33 | * |/___X 34 | */ 35 | 36 | //Use Bit flags to do this...faster look ups for sure 37 | //Left Bit : Top or Bottom 38 | //Mid Bit : Left or Right 39 | //Low Bit : Front or Back 40 | 41 | TopLeftFront = 5, //(101) 42 | TopLeftBack = 4, //(100) 43 | TopRightFront = 7,//(111) 44 | TopRightBack = 6, //(110) 45 | 46 | BottomLeftFront = 1, //(001) 47 | BottomLeftBack = 0, //(000) 48 | BottomRightFront = 3, //(011) 49 | BottomRightBack = 2 //(010) 50 | } 51 | 52 | //======================================================================================================================== 53 | //octree class 54 | //======================================================================================================================== 55 | 56 | public class Octree 57 | { 58 | //A node(Cube) used in the tree 59 | public class OctreeNode 60 | { 61 | //Members 62 | int mNodeIndex; //creation order of this node 63 | int mParentIndex; 64 | 65 | SDF_Struct mSDF; 66 | int mNodeDepth; //how deep is this node from Root 67 | OctreeNode mParent; 68 | OctreeNode[] mSubNodes; 69 | public bool mHasGeo; //if this node has geometry 70 | AABB mAABB; //this nodes AABB 71 | bool mInsideMesh; //if this node is inside a collider 72 | 73 | Octree mOctree; //which Octree this node belongs too 74 | 75 | //Methods 76 | public OctreeNode(Octree owner, Vector3 position, float size, int depth, int ParentIndex, OctreeNode parent) 77 | { 78 | mOctree = owner; 79 | //Increase the number of nodes now 80 | mOctree.mNumberNodes += 1; 81 | 82 | //Debug.Log("Current Index = " + mOctree.mNumberNodes + " ,parent = " + ParentIndex); 83 | 84 | mSDF = new SDF_Struct(); 85 | 86 | mSDF.mPosition = position; 87 | mSDF.mSize = size; 88 | 89 | mNodeDepth = depth; 90 | mParentIndex = ParentIndex; 91 | mParent = parent; 92 | mNodeIndex = mOctree.mNumberNodes; //this node was just created so it's the newest one 93 | 94 | //for now arrbitrary values here 95 | mSDF.mPoint = new Vector3(999999.9f, 999999.9f, 999999.9f); 96 | mSDF.mDistance = 999999.9f;//Mathf.Infinity; 97 | mInsideMesh = false; 98 | 99 | mAABB = new AABB(); 100 | mAABB.Max.x = position.x + size; 101 | mAABB.Max.y = position.y + size; 102 | mAABB.Max.z = position.z + size; 103 | mAABB.Min.x = position.x - size; 104 | mAABB.Min.y = position.y - size; 105 | mAABB.Min.z = position.z - size; 106 | 107 | //check if Node intersects with scene geometry AABBs 108 | mHasGeo = HasGeo(); 109 | 110 | //Lowest level dept = -1 111 | if (mHasGeo && mNodeDepth >= 0) 112 | { 113 | SubDivide(); 114 | } 115 | } 116 | 117 | //SubDivide the Node into 8 smaller nodes 118 | public void SubDivide() 119 | { 120 | mSubNodes = new OctreeNode[8]; 121 | float halfSize = mSDF.mSize * 0.5f; 122 | 123 | for (int i = 0; i < 8; ++i) 124 | { 125 | Vector3 newPos = mSDF.mPosition; 126 | 127 | //Check which Cube this should be 128 | /* 129 | * 0---2 130 | * / /| 131 | * 1---3 | 132 | * | 4 : 6 133 | * 5---7/ 134 | * 135 | * Y Z 136 | * | / 137 | * |/___X 138 | */ 139 | 140 | switch (i) 141 | { 142 | case (int)OctreeIndex.TopLeftFront: 143 | newPos.x -= halfSize; newPos.y += halfSize; newPos.z += halfSize; 144 | break; 145 | case (int)OctreeIndex.TopLeftBack: 146 | newPos.x -= halfSize; newPos.y += halfSize; newPos.z -= halfSize; 147 | break; 148 | case (int)OctreeIndex.TopRightFront: 149 | newPos.x += halfSize; newPos.y += halfSize; newPos.z += halfSize; 150 | break; 151 | case (int)OctreeIndex.TopRightBack: 152 | newPos.x += halfSize; newPos.y += halfSize; newPos.z -= halfSize; 153 | break; 154 | case (int)OctreeIndex.BottomLeftFront: 155 | newPos.x -= halfSize; newPos.y -= halfSize; newPos.z += halfSize; 156 | break; 157 | case (int)OctreeIndex.BottomLeftBack: 158 | newPos.x -= halfSize; newPos.y -= halfSize; newPos.z -= halfSize; 159 | break; 160 | case (int)OctreeIndex.BottomRightFront: 161 | newPos.x += halfSize; newPos.y -= halfSize; newPos.z += halfSize; 162 | break; 163 | case (int)OctreeIndex.BottomRightBack: 164 | newPos.x += halfSize; newPos.y -= halfSize; newPos.z -= halfSize; 165 | break; 166 | default: 167 | break; 168 | } 169 | mSubNodes[i] = new OctreeNode(mOctree, newPos, halfSize, mNodeDepth-1, mNodeIndex, this); 170 | }//End of For loop 171 | } 172 | 173 | //This node has no subNode children and it can be ignored 174 | public bool IsLeaf() 175 | { 176 | return mSubNodes == null; 177 | } 178 | 179 | //Check if Node AABB intersects with any mesh OBBs 180 | bool HasGeo() 181 | { 182 | bool hasGeo = false; 183 | 184 | for(int i = 0; i < All_OBBs.Count; ++i) 185 | { 186 | if (hasGeo) 187 | { 188 | break; 189 | } 190 | else 191 | { 192 | hasGeo = Intersect(mAABB, All_OBBs[i]); 193 | } 194 | } 195 | 196 | return hasGeo; 197 | } 198 | 199 | //public Getters for private stuff 200 | public IEnumerable Nodes 201 | { 202 | get 203 | { 204 | return mSubNodes; 205 | } 206 | } 207 | 208 | public Vector3 Position 209 | { 210 | get { return mSDF.mPosition; } 211 | } 212 | 213 | public SDF_Struct SDF 214 | { 215 | get { return mSDF; } 216 | } 217 | 218 | public int Depth 219 | { 220 | get { return mNodeDepth; } 221 | } 222 | 223 | public int NodeIndex 224 | { 225 | get { return mNodeIndex; } 226 | set { mNodeIndex = value; } 227 | } 228 | public int ParentIndex 229 | { 230 | get { return mParentIndex; } 231 | } 232 | 233 | public bool InsideMesh 234 | { 235 | get 236 | { 237 | return mInsideMesh; 238 | } 239 | set 240 | { 241 | mInsideMesh = value; 242 | } 243 | } 244 | 245 | public OctreeNode Parent 246 | { 247 | get { return mParent; } 248 | } 249 | 250 | public float size 251 | { 252 | get { return mSDF.mSize; } 253 | } 254 | 255 | public static bool Intersect(AABB A, OBB B) 256 | { 257 | //Function found at https://github.com/gszauer/GamePhysicsCookbook/blob/master/Code/Geometry3D.cpp 258 | //nice clean way of using Separating axis theorum 259 | 260 | Vector3[] axis = new Vector3[15]; 261 | axis[0] = new Vector3(1.0f, 0.0f, 0.0f); //AABB x axis 262 | axis[1] = new Vector3(0.0f, 1.0f, 0.0f); //AABB y axis 263 | axis[2] = new Vector3(0.0f, 0.0f, 1.0f); //AABB z axis 264 | axis[3] = B.x; 265 | axis[4] = B.y; 266 | axis[5] = B.z; 267 | 268 | //Fill in the remaining axis 269 | for (int i = 0; i < 3; ++i) 270 | { 271 | axis[6 + i * 3 + 0] = Vector3.Cross(axis[i], axis[0]); 272 | axis[6 + i * 3 + 1] = Vector3.Cross(axis[i], axis[1]); 273 | axis[6 + i * 3 + 2] = Vector3.Cross(axis[i], axis[2]); 274 | } 275 | 276 | for(int i = 0; i < 15; ++i) 277 | { 278 | if(OverlapOnAxis(A, B, axis[i]) == false) 279 | { 280 | return false; //Separating axis found 281 | } 282 | } 283 | 284 | return true; 285 | } 286 | static bool OverlapOnAxis(AABB A, OBB B, Vector3 axis) 287 | { 288 | //Found at: https://github.com/gszauer/GamePhysicsCookbook/blob/master/Code/Geometry3D.cpp 289 | 290 | //using Vec2.x = min, Vec2.y = max 291 | Vector2 a = GetInterval(A, axis); 292 | Vector2 b = GetInterval(B, axis); 293 | 294 | return (b.x <= a.y) && (a.x <= b.y); 295 | } 296 | 297 | static Vector2 GetInterval(AABB aabb, Vector3 axis) 298 | { 299 | //Found at: https://github.com/gszauer/GamePhysicsCookbook/blob/master/Code/Geometry3D.cpp 300 | Vector3 m = aabb.Min; 301 | Vector3 M = aabb.Max; 302 | 303 | Vector3[] vertex = new Vector3[8]; 304 | vertex[0] = new Vector3(m.x, M.y, M.z); 305 | vertex[1] = new Vector3(m.x, M.y, m.z); 306 | vertex[2] = new Vector3(m.x, m.y, M.z); 307 | vertex[3] = new Vector3(m.x, m.y, m.z); 308 | vertex[4] = new Vector3(M.x, M.y, M.z); 309 | vertex[5] = new Vector3(M.x, M.y, m.z); 310 | vertex[6] = new Vector3(M.x, m.y, M.z); 311 | vertex[7] = new Vector3(M.x, m.y, m.z); 312 | 313 | Vector2 result; 314 | result.x = result.y = Vector3.Dot(axis, vertex[0]); 315 | 316 | for(int i = 1; i < 8; ++i) 317 | { 318 | float projection = Vector3.Dot(axis, vertex[i]); 319 | result.x = (projection < result.x) ? projection : result.x; 320 | result.y = (projection > result.y) ? projection : result.y; 321 | } 322 | 323 | return result; 324 | } 325 | static Vector2 GetInterval(OBB obb, Vector3 axis) 326 | { 327 | //Found at: https://github.com/gszauer/GamePhysicsCookbook/blob/master/Code/Geometry3D.cpp 328 | Vector3[] vertex = new Vector3[8]; 329 | 330 | //OBB extents taking scaling into account 331 | Vector3 e = new Vector3(obb.e.x * obb.s.x, obb.e.y * obb.s.y, obb.e.z * obb.s.z); 332 | 333 | vertex[0] = obb.c + (((obb.x - obb.c) * e.x) + ((obb.y - obb.c) * e.y) + ((obb.z - obb.c) * e.z)); 334 | vertex[1] = obb.c + (((obb.c - obb.x) * e.x) + ((obb.y - obb.c) * e.y) + ((obb.z - obb.c) * e.z)); 335 | vertex[2] = obb.c + (((obb.x - obb.c) * e.x) + ((obb.c - obb.y) * e.y) + ((obb.z - obb.c) * e.z)); 336 | vertex[3] = obb.c + (((obb.c - obb.x) * e.x) + ((obb.c - obb.y) * e.y) + ((obb.z - obb.c) * e.z)); 337 | vertex[4] = obb.c + (((obb.x - obb.c) * e.x) + ((obb.y - obb.c) * e.y) + ((obb.c - obb.z) * e.z)); 338 | vertex[5] = obb.c + (((obb.c - obb.x) * e.x) + ((obb.y - obb.c) * e.y) + ((obb.c - obb.z) * e.z)); 339 | vertex[6] = obb.c + (((obb.x - obb.c) * e.x) + ((obb.c - obb.y) * e.y) + ((obb.c - obb.z) * e.z)); 340 | vertex[7] = obb.c + (((obb.c - obb.x) * e.x) + ((obb.c - obb.y) * e.y) + ((obb.c - obb.z) * e.z)); 341 | 342 | Vector2 result; 343 | result.x = result.y = Vector3.Dot(axis, vertex[0]); 344 | 345 | for(int i = 1; i < 8; ++i) 346 | { 347 | float projection = Vector3.Dot(axis, vertex[i]); 348 | result.x = (projection < result.x) ? projection : result.x; 349 | result.y = (projection > result.y) ? projection : result.y; 350 | } 351 | 352 | 353 | return result; 354 | } 355 | 356 | static bool Intersect(AABB A, AABB B) 357 | { 358 | //http://www.miguelcasillas.com/?p=30 359 | return (A.Max.x > B.Min.x && 360 | A.Min.x < B.Max.x && 361 | A.Max.y > B.Min.y && 362 | A.Min.y < B.Max.y && 363 | A.Max.z > B.Min.z && 364 | A.Min.z < B.Max.z); 365 | } 366 | static bool Intersect(AABB A, Vector3 P) 367 | { 368 | //http://www.miguelcasillas.com/?p=24 369 | if(P.x > A.Min.x && P.x < A.Max.x && 370 | P.y > A.Min.y && P.y < A.Max.y && 371 | P.z > A.Min.z && P.z < A.Max.z) 372 | { 373 | return true; 374 | } 375 | 376 | return false; 377 | } 378 | 379 | public bool IsLowestNode() 380 | { 381 | return mNodeDepth < 0; 382 | } 383 | } 384 | 385 | //Subdividing the Octree based on AABBs is pretty bad beacuse you can't get good depth and accuracy 386 | public class AABB 387 | { 388 | public Vector3 Min,Max; //Min/Max corners for this AABB 389 | } 390 | 391 | public class OBB 392 | { 393 | //Center, Basis Vecs, Extents 394 | public Vector3 c, x , y , z, e; 395 | //scale of OBB 396 | public Vector3 s; 397 | 398 | public float[] OrientationArray 399 | { 400 | get 401 | { 402 | float[] o = new float[9]; 403 | o[0] = x.x; 404 | o[1] = x.y; 405 | o[2] = x.z; 406 | o[3] = y.x; 407 | o[4] = y.y; 408 | o[5] = y.z; 409 | o[6] = z.x; 410 | o[7] = z.y; 411 | o[8] = z.z; 412 | 413 | return o; 414 | } 415 | } 416 | 417 | public Vector3[] Axis 418 | { 419 | get 420 | { 421 | Vector3[] axis = new Vector3[3]; 422 | axis[0] = x; 423 | axis[1] = y; 424 | axis[2] = z; 425 | return axis; 426 | } 427 | } 428 | } 429 | 430 | //======================================================================================================================== 431 | //Members 432 | //======================================================================================================================== 433 | private OctreeNode mRoot; 434 | private int mNumberNodes; 435 | 436 | //All the Scene Geometry in the Node 437 | public static List All_OBBs = null; 438 | 439 | //======================================================================================================================== 440 | //Methods 441 | //======================================================================================================================== 442 | public Octree (Vector3 position, float size, int depth) 443 | { 444 | mNumberNodes = -1; 445 | mRoot = new OctreeNode(this, position, size, depth, -1, null); 446 | } 447 | 448 | //using bit flags to find which index this Node should be 449 | private int GetIndexOfPosition(Vector3 lookUpPosition, Vector3 nodePosition) 450 | { 451 | int index = 0; 452 | 453 | /* 454 | * 0---2 455 | * / /| 456 | * 1---3 | 457 | * | 4 : 6 458 | * 5---7/ 459 | * 460 | * Y Z 461 | * | / 462 | * |/___X 463 | */ 464 | 465 | //ternary operations huzza! 466 | index |= lookUpPosition.y > nodePosition.y ? 4 : 0; //either top(100) or bottom(000) 467 | index |= lookUpPosition.x > nodePosition.x ? 2 : 0; //then right(010) or left(000) 468 | index |= lookUpPosition.z > nodePosition.z ? 1 : 0;// then front(001) or bottom(000) 469 | 470 | return index; 471 | } 472 | 473 | public OctreeNode GetRootNode() 474 | { 475 | return mRoot; 476 | } 477 | 478 | public int Size 479 | { 480 | get 481 | { 482 | return mNumberNodes; 483 | } 484 | } 485 | } 486 | 487 | -------------------------------------------------------------------------------- /Assets/Scripts/Octree/Octree.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 1b1b8cad9ced96d4591d4d3704e732b5 3 | timeCreated: 1494119242 4 | licenseType: Pro 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Assets/Scripts/Octree/Octree_Baker.cs: -------------------------------------------------------------------------------- 1 | #if UNITY_EDITOR 2 | 3 | using UnityEngine; 4 | using UnityEditor; 5 | using System.Collections; 6 | 7 | //A class that will bake the Octree 8 | [CustomEditor(typeof(Voxelizer))] 9 | public class Octree_Baker : Editor 10 | { 11 | public override void OnInspectorGUI() 12 | { 13 | DrawDefaultInspector(); 14 | 15 | Voxelizer octreeBuilder = (Voxelizer)target; 16 | if(GUILayout.Button("Bake Octree!")) 17 | { 18 | octreeBuilder.BAKE_OCTREE(); 19 | } 20 | 21 | if(GUILayout.Button("Delete Octree!")) 22 | { 23 | octreeBuilder.DeleteOctree(); 24 | } 25 | } 26 | } 27 | #endif -------------------------------------------------------------------------------- /Assets/Scripts/Octree/Octree_Baker.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 82863285b00c2ba47aff6fb5dd7cd78e 3 | timeCreated: 1497230793 4 | licenseType: Free 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Assets/Scripts/Octree/Voxel_Debugger.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Collections; 3 | 4 | [RequireComponent(typeof(Camera))] 5 | [RequireComponent(typeof(Particle_Flowing))] 6 | public class Voxel_Debugger : MonoBehaviour 7 | { 8 | Baked_Octree VoxelGrid = null; 9 | public KeyCode DebugDrawKey = KeyCode.Space; //Toggle debug drawing entire tree 10 | public KeyCode DebugNormals = KeyCode.N; //Toggel drawing Surface Normals 11 | public KeyCode DebugNodes = KeyCode.M; //Toggel drawing Nodes 12 | 13 | // Use this for initialization 14 | void Start () 15 | { 16 | VoxelGrid = GetComponent().octree; 17 | } 18 | 19 | void Update() 20 | { 21 | if(Input.GetKeyDown(DebugDrawKey)) 22 | { 23 | VoxelGrid.DrawTree = !VoxelGrid.DrawTree; 24 | } 25 | 26 | if(Input.GetKeyDown(DebugNormals)) 27 | { 28 | VoxelGrid.DrawNormals = !VoxelGrid.DrawNormals; 29 | } 30 | 31 | if (Input.GetKeyDown(DebugNodes)) 32 | { 33 | VoxelGrid.DrawNodes = !VoxelGrid.DrawNodes; 34 | } 35 | } 36 | 37 | //Draw the Grid after this camera has drawn everything else 38 | void OnPostRender() 39 | { 40 | VoxelGrid.DrawVoxelGrid(Color.black); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Assets/Scripts/Octree/Voxel_Debugger.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 3a8794d95ce1d8443b8219b727bc9e89 3 | timeCreated: 1494355751 4 | licenseType: Pro 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Assets/Scripts/Octree/Voxelizer.cs: -------------------------------------------------------------------------------- 1 | #if UNITY_EDITOR 2 | 3 | using UnityEngine; 4 | using UnityEditor; //for making handling assets/folders 5 | 6 | using System.Collections; 7 | using System.Collections.Generic; //used for List 8 | using System.Linq; //used for sorting lists 9 | using System.IO; //for Read/Write of files 10 | 11 | [ExecuteInEditMode] 12 | public class Voxelizer : MonoBehaviour 13 | { 14 | [Range(10, 50)] 15 | public int VoxelSize = 20; //How big the initial Root Cube is 16 | [Range(1, 5)] 17 | public int TreeDepth = 2; 18 | 19 | [HideInInspector] 20 | public Octree OCTREE = null; 21 | 22 | //read through the Tree and make a list out of them and then convert that list to an continuous ARRAY 23 | List Unpacked_Tree = null; 24 | [HideInInspector] 25 | public SDF_Node[] nodeArray; 26 | 27 | //This is used to compare each mesh because for Spheres it is better to use AABB rather than OBB 28 | public Mesh SphereExample; 29 | 30 | [HideInInspector] 31 | public bool Initialized = false; 32 | 33 | //used to find closes point to colliders 34 | Vector3[] RayDirs; 35 | 36 | public string Octree_Name; 37 | public int RayDirections = 100; //How many Rays to shoot outward from each node 38 | 39 | //===================================================================================================================== 40 | // This function creates the Octree and then saves it out as a Prefab 41 | //===================================================================================================================== 42 | public void BAKE_OCTREE() 43 | { 44 | Debug.Log("Baking Octree: " + Octree_Name + "....."); 45 | Initialized = false; 46 | 47 | Build_Octree(); //the Meat and the potatoes 48 | 49 | Debug.Log("Finished Baking Octree!!"); 50 | 51 | //Now save the Octree 52 | bool folderExists = AssetDatabase.IsValidFolder("Assets/Resources/Baked_Octrees/"); 53 | if(!folderExists) 54 | { 55 | AssetDatabase.CreateFolder("Assets/Resources/", "Baked_Octrees"); 56 | } 57 | 58 | string path = "Assets/Resources/Baked_Octrees/" + Octree_Name + ".txt"; 59 | 60 | SaveTree(path); 61 | } 62 | 63 | //===================================================================================================================== 64 | //Deletes the currently named File 65 | //===================================================================================================================== 66 | public void DeleteOctree() 67 | { 68 | string path = "Assets/Resources/Baked_Octrees/" + Octree_Name + ".txt"; 69 | 70 | bool removed = AssetDatabase.DeleteAsset(path); 71 | if (removed) 72 | { 73 | Debug.Log("Removed: " + path); 74 | } 75 | else 76 | { 77 | Debug.Log("Cannot Remove: " + path); 78 | } 79 | 80 | } 81 | //===================================================================================================================== 82 | //this Function saves the linear Node array as a text file 83 | //===================================================================================================================== 84 | void SaveTree(string path) 85 | { 86 | string file = ""; 87 | 88 | //Start with the Tree Depth ,number of nodes; and Root Size 89 | file += TreeDepth.ToString() + "," + nodeArray.Length.ToString() + ";" + VoxelSize.ToString(); 90 | 91 | for(int i = 0; i < nodeArray.Length; ++i) 92 | { 93 | SDF_Node n = nodeArray[i]; 94 | file += "/" + n.index.ToString() + "," + n.c0.ToString() + "," + n.c1.ToString() 95 | + "," + n.c2.ToString() + "," + n.c3.ToString() + "," + n.c4.ToString() 96 | + "," + n.c5.ToString() + "," + n.c6.ToString() + "," + n.c7.ToString() 97 | + "," + n.Min.x.ToString() + "," + n.Min.y.ToString() + "," + n.Min.z.ToString() 98 | + "," + n.Max.x.ToString() + "," + n.Max.y.ToString() + "," + n.Max.z.ToString() 99 | + "," + n.SurfacePoint.x.ToString() + "," + n.SurfacePoint.y.ToString() + "," + n.SurfacePoint.z.ToString(); 100 | } 101 | //finally add the center of the Octree 102 | Vector3 p = transform.position; 103 | file += '*' + p.x.ToString() + "," + p.y.ToString() + "," + p.z.ToString() + ","; 104 | 105 | //PlayerPrefs.SetString(Octree_Name, file); 106 | System.IO.File.WriteAllText(path, file); 107 | } 108 | 109 | //===================================================================================================================== 110 | // structs used in this class and maybe elsewhere 111 | //===================================================================================================================== 112 | 113 | //This is the format that the compute buffer will use 114 | public struct SDF_Node 115 | { 116 | public int index; //can't use pointers so this should work too 117 | public int c0, c1, c2, c3, c4, c5, c6, c7; 118 | 119 | public Vector3 Min, Max; //AABB of this node 120 | public Vector3 SurfacePoint; //closest surface point in this node 121 | 122 | public static int Stride 123 | { 124 | get 125 | { 126 | return 72; 127 | } 128 | } 129 | 130 | public override string ToString() 131 | { 132 | return index.ToString() + ", " + Min.ToString() + ", " + Max.ToString() + ", " + SurfacePoint.ToString(); 133 | } 134 | } 135 | 136 | //this will be used to unpack the Tree into a reable List 137 | public class TreeNode 138 | { 139 | public Octree.OctreeNode mParent; 140 | public Octree.OctreeNode mNode; 141 | public Octree.OctreeNode[] mChildren = null; 142 | } 143 | 144 | //simple struct used when looking for the closest collider to a point 145 | class ColliderDistance 146 | { 147 | public float distance; 148 | public int index; 149 | } 150 | 151 | //===================================================================================================================== 152 | //This initializes this Octree 153 | //===================================================================================================================== 154 | void Build_Octree () 155 | { 156 | RayDirs = PointsOnSphere.GetPoints((float)RayDirections); 157 | 158 | //First step all Scene geometry send their Meshes to the Octree class 159 | if(Octree.All_OBBs == null) 160 | { 161 | Octree.All_OBBs = new List(); 162 | } 163 | 164 | if(Unpacked_Tree == null) 165 | { 166 | Unpacked_Tree = new List(); 167 | } 168 | 169 | //send all scene Geometry(AABBs) to the Octree class 170 | StreamGeometry(); 171 | 172 | //Create a new Octree only ONCE this is not a dynamic scene!!!! 173 | OCTREE = new Octree(transform.position, VoxelSize, TreeDepth); 174 | 175 | Octree.OctreeNode root = OCTREE.GetRootNode(); 176 | 177 | //Now for each Leaf node at the bottom of the Tree populate it 178 | //ie. Find the closest Surface geometry to it and populate its SDF structure 179 | PopulateTree(root); 180 | 181 | //Structure the Octree into a single list of tree nodes 182 | UnpackTree(root); 183 | 184 | //Finaly Step: Store the UnpackedOctree into a linear Array(ComputeBuffer) 185 | PackTree(); 186 | 187 | Initialized = true; 188 | } 189 | 190 | //===================================================================================================================== 191 | // Helper functions to Generate the Octree and then Pack it 192 | //===================================================================================================================== 193 | 194 | //send all scene Geometry(OBBs) to the Octree class 195 | void StreamGeometry() 196 | { 197 | MeshRenderer[] allMesh = FindObjectsOfType(typeof(MeshRenderer)) as MeshRenderer[]; 198 | 199 | Octree.AABB volume = new Octree.AABB(); //this is the volume of the Octree Root node 200 | Vector3 p = transform.position; 201 | volume.Max.x = p.x + VoxelSize; 202 | volume.Max.y = p.y + VoxelSize; 203 | volume.Max.z = p.z + VoxelSize; 204 | volume.Min.x = p.x - VoxelSize; 205 | volume.Min.y = p.y - VoxelSize; 206 | volume.Min.z = p.z - VoxelSize; 207 | 208 | //All the AABBs 209 | for(int i = 0; i < allMesh.Length; ++i) 210 | { 211 | if (allMesh[i].enabled == false) 212 | continue; 213 | 214 | //world position bounds 215 | Octree.AABB meshAABB = new Octree.AABB(); 216 | meshAABB.Max = allMesh[i].bounds.max; 217 | meshAABB.Min = allMesh[i].bounds.min; 218 | 219 | if(IsInOctree(meshAABB, volume)) 220 | { 221 | //Now Because Unity uses AABBs for all its bounds we first have to construct our own OBBs 222 | Octree.OBB obb = new Octree.OBB(); 223 | 224 | Vector3 c = allMesh[i].bounds.center; 225 | Mesh m = allMesh[i].gameObject.GetComponent().sharedMesh; 226 | 227 | obb.c = c; 228 | obb.e = m.bounds.extents; 229 | obb.s = allMesh[i].transform.localScale; 230 | 231 | //if it's a sphere use AABB instead 232 | if (m == SphereExample) 233 | { 234 | 235 | obb.x = c + Vector3.right; 236 | obb.y = c + Vector3.up; 237 | obb.z = c + Vector3.forward; 238 | 239 | } 240 | else 241 | { 242 | obb.x = c + allMesh[i].transform.right; 243 | obb.y = c + allMesh[i].transform.up; 244 | obb.z = c + allMesh[i].transform.forward; 245 | } 246 | 247 | Octree.All_OBBs.Add(obb); 248 | } 249 | } 250 | } 251 | 252 | bool IsInOctree(Octree.AABB A, Octree.AABB B) 253 | { 254 | //http://www.miguelcasillas.com/?p=30 255 | return (A.Max.x > B.Min.x && 256 | A.Min.x < B.Max.x && 257 | A.Max.y > B.Min.y && 258 | A.Min.y < B.Max.y && 259 | A.Max.z > B.Min.z && 260 | A.Min.z < B.Max.z); 261 | } 262 | 263 | //Recursively search the tree and for each leaf node at the bottom depth populate it's 264 | //SDF structure by raycasting to all Scene Geometry and find the closest Surface Point 265 | void PopulateTree(Octree.OctreeNode node) 266 | { 267 | if (!node.IsLeaf()) 268 | { 269 | var subNodes = node.Nodes; 270 | foreach (var subNode in subNodes) 271 | { 272 | PopulateTree(subNode); 273 | } 274 | } 275 | else 276 | { 277 | if(node.IsLowestNode()) 278 | { 279 | //find the closest Surface Point to this Mesh 280 | //Unity 5.7 has Physics.ClosestPoint(Vec3 p) 281 | 282 | //This is my version of it 283 | Vector3 surfacePoint = ClosesPointOnCollider(node.Position); 284 | if(surfacePoint == node.Position) 285 | { 286 | node.InsideMesh = true; 287 | } 288 | else 289 | { 290 | node.InsideMesh = false; 291 | node.SDF.mPoint = surfacePoint; 292 | node.SDF.mDistance = (surfacePoint - node.Position).magnitude; 293 | } 294 | } 295 | } 296 | } 297 | 298 | //recursively go through the Octree and convert them into a simple structure 299 | void UnpackTree(Octree.OctreeNode node) 300 | { 301 | TreeNode current = new TreeNode(); 302 | 303 | var subNodes = node.Nodes; 304 | 305 | //set this up 306 | current.mParent = node.Parent; 307 | current.mNode = node; 308 | int i = 0; 309 | if (subNodes != null) 310 | { 311 | current.mChildren = new Octree.OctreeNode[8]; 312 | foreach (var subNode in subNodes) 313 | { 314 | current.mChildren[i] = subNode; 315 | ++i; 316 | } 317 | } 318 | else 319 | { 320 | current.mChildren = null; 321 | } 322 | 323 | //assign this node to our array only if It's not inside a mesh 324 | if(!current.mNode.InsideMesh) 325 | { 326 | Unpacked_Tree.Add(current); 327 | } 328 | 329 | //if this Node has children recurse 330 | if (!node.IsLeaf()) 331 | { 332 | foreach (var subNode in subNodes) 333 | { 334 | UnpackTree(subNode); 335 | } 336 | } 337 | } 338 | //Linearly search the Unpacked Tree to find this nodes index 339 | int FindIndex(Octree.OctreeNode node, ref List tree) 340 | { 341 | int index = -1; 342 | 343 | for(index = 0; index < tree.Count; ++index) 344 | { 345 | if (tree[index].mNode == node) 346 | return index; 347 | } 348 | 349 | return -1; 350 | } 351 | 352 | //Final step Get the Unpacked tree and then pack it into a linear Buffer 353 | void PackTree() 354 | { 355 | //the unpacked Tree is recursively built so we have to turn it into a linear list here before we copy it with proper 356 | //indexing into the final LinearList below 357 | List LinearUnpackedTree = new List(); 358 | for(int i = 0; i < Unpacked_Tree.Count-1; ++i) //ignore last node some reason the recursion fucks up when adding an extra garbage node 359 | { 360 | LinearUnpackedTree.Add(Unpacked_Tree[i]); 361 | } 362 | Unpacked_Tree.Clear(); 363 | 364 | //Go through the unpacked tree and build it linearly 365 | List LinearList = new List(); 366 | 367 | for(int i = 0; i < LinearUnpackedTree.Count; ++i) 368 | { 369 | SDF_Node current = new SDF_Node(); 370 | 371 | Octree.OctreeNode node = LinearUnpackedTree[i].mNode; 372 | Vector3 p = node.Position; 373 | float s = node.SDF.mSize; 374 | 375 | current.Min.x = p.x - s; 376 | current.Min.y = p.y - s; 377 | current.Min.z = p.z - s; 378 | 379 | current.Max.x = p.x + s; 380 | current.Max.y = p.y + s; 381 | current.Max.z = p.z + s; 382 | 383 | current.index = i; 384 | current.SurfacePoint = node.SDF.mPoint; 385 | if (LinearUnpackedTree[i].mChildren != null) 386 | { 387 | current.c0 = FindIndex(LinearUnpackedTree[i].mChildren[0], ref LinearUnpackedTree); 388 | current.c1 = FindIndex(LinearUnpackedTree[i].mChildren[1], ref LinearUnpackedTree); 389 | current.c2 = FindIndex(LinearUnpackedTree[i].mChildren[2], ref LinearUnpackedTree); 390 | current.c3 = FindIndex(LinearUnpackedTree[i].mChildren[3], ref LinearUnpackedTree); 391 | current.c4 = FindIndex(LinearUnpackedTree[i].mChildren[4], ref LinearUnpackedTree); 392 | current.c5 = FindIndex(LinearUnpackedTree[i].mChildren[5], ref LinearUnpackedTree); 393 | current.c6 = FindIndex(LinearUnpackedTree[i].mChildren[6], ref LinearUnpackedTree); 394 | current.c7 = FindIndex(LinearUnpackedTree[i].mChildren[7], ref LinearUnpackedTree); 395 | } 396 | else 397 | { 398 | current.c0 = -1; 399 | current.c1 = -1; 400 | current.c2 = -1; 401 | current.c3 = -1; 402 | current.c4 = -1; 403 | current.c5 = -1; 404 | current.c6 = -1; 405 | current.c7 = -1; 406 | } 407 | 408 | LinearList.Add(current); 409 | } 410 | 411 | //used to set a ComputeBuffer later on 412 | nodeArray = LinearList.ToArray(); 413 | } 414 | 415 | //============================================================================================================================================= 416 | //Helper Functions to Find the closest approximate point on a collider from a Vector3.. Note Unity 5.7 and up has this as Physics.ClosestPoint 417 | //============================================================================================================================================= 418 | Vector3 ClosesPointOnCollider(Vector3 p) 419 | { 420 | Vector3 closestPoint = p; 421 | 422 | Collider[] allColliders = GameObject.FindObjectsOfType(); 423 | 424 | //first find the closest collider to the Point 425 | 426 | List distances = new List(); 427 | 428 | for(int c = 0; c < allColliders.Length; ++c) 429 | { 430 | Collider currentCollider = allColliders[c]; 431 | ColliderDistance curr = new ColliderDistance(); 432 | 433 | curr.distance = (p - currentCollider.transform.position).sqrMagnitude; 434 | curr.index = c; 435 | 436 | distances.Add(curr); 437 | } 438 | 439 | distances = distances.OrderBy(c => c.distance).ToList(); 440 | 441 | //The closest Collider should be first in the list 442 | Collider closestCollider = allColliders[distances[0].index]; 443 | if(closestCollider.GetType() == typeof(SphereCollider)) //this is much nicer for a sphere 444 | { 445 | RaycastHit hit; 446 | Ray r = new Ray(); 447 | r.origin = p; 448 | r.direction = closestCollider.transform.position - p; 449 | if (closestCollider.Raycast(r, out hit, VoxelSize)) 450 | { 451 | closestPoint = hit.point; 452 | } 453 | } 454 | else //Ray cast in out-ward directions 455 | { 456 | List hits = new List(); 457 | //now from the point P raycast in a whole bunch of directions 458 | for (int r = 0; r < RayDirs.Length; ++r) 459 | { 460 | Ray ray = new Ray(); 461 | ray.origin = p; 462 | ray.direction = RayDirs[r]; 463 | 464 | RaycastHit hit; 465 | if (closestCollider.Raycast(ray, out hit, VoxelSize)) 466 | { 467 | hits.Add(hit); 468 | } 469 | } 470 | 471 | if (hits.Count > 1) 472 | { 473 | //find the closest hit 474 | hits = hits.OrderBy(h => (h.point - p).sqrMagnitude).ToList(); 475 | closestPoint = hits[0].point; 476 | } 477 | else if (hits.Count == 1) 478 | { 479 | closestPoint = hits[0].point; 480 | } 481 | } 482 | 483 | return closestPoint; 484 | } 485 | 486 | //============================================================================================================================================= 487 | //Draw all the OBBs and the max extents of this Octree 488 | //============================================================================================================================================= 489 | void OnDrawGizmos() 490 | { 491 | if (Octree.All_OBBs != null) 492 | { 493 | Gizmos.color = Color.green; 494 | for (int i = 0; i < Octree.All_OBBs.Count; ++i) 495 | { 496 | Baked_Octree.Draw_OBB(Octree.All_OBBs[i]); 497 | } 498 | } 499 | 500 | //Draw the maximum extent of the Octree even if there is no octree 501 | Gizmos.color = Color.grey; 502 | Gizmos.DrawWireCube(transform.position, new Vector3(VoxelSize * 2, VoxelSize * 2, VoxelSize * 2)); 503 | } 504 | } 505 | 506 | #endif -------------------------------------------------------------------------------- /Assets/Scripts/Octree/Voxelizer.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 445fe6cdb1c25f44eae293cabcaa39b8 3 | timeCreated: 1494117628 4 | licenseType: Pro 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Assets/Scripts/SmoothMouseLook.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | 5 | //Found at:http://wiki.unity3d.com/index.php/SmoothMouseLook 6 | 7 | public class SmoothMouseLook : MonoBehaviour 8 | { 9 | 10 | public enum RotationAxes { MouseXAndY = 0, MouseX = 1, MouseY = 2 } 11 | public RotationAxes axes = RotationAxes.MouseXAndY; 12 | public float sensitivityX = 15F; 13 | public float sensitivityY = 15F; 14 | 15 | public float minimumX = -360F; 16 | public float maximumX = 360F; 17 | 18 | public float minimumY = -60F; 19 | public float maximumY = 60F; 20 | 21 | float rotationX = 0F; 22 | float rotationY = 0F; 23 | 24 | private List rotArrayX = new List(); 25 | float rotAverageX = 0F; 26 | 27 | private List rotArrayY = new List(); 28 | float rotAverageY = 0F; 29 | 30 | public float frameCounter = 20; 31 | 32 | public float MoveSpeed = 5.0f; 33 | 34 | Quaternion originalRotation; 35 | 36 | void Update() 37 | { 38 | if (axes == RotationAxes.MouseXAndY) 39 | { 40 | rotAverageY = 0f; 41 | rotAverageX = 0f; 42 | 43 | rotationY += Input.GetAxis("Mouse Y") * sensitivityY; 44 | rotationX += Input.GetAxis("Mouse X") * sensitivityX; 45 | 46 | rotArrayY.Add(rotationY); 47 | rotArrayX.Add(rotationX); 48 | 49 | if (rotArrayY.Count >= frameCounter) 50 | { 51 | rotArrayY.RemoveAt(0); 52 | } 53 | if (rotArrayX.Count >= frameCounter) 54 | { 55 | rotArrayX.RemoveAt(0); 56 | } 57 | 58 | for (int j = 0; j < rotArrayY.Count; j++) 59 | { 60 | rotAverageY += rotArrayY[j]; 61 | } 62 | for (int i = 0; i < rotArrayX.Count; i++) 63 | { 64 | rotAverageX += rotArrayX[i]; 65 | } 66 | 67 | rotAverageY /= rotArrayY.Count; 68 | rotAverageX /= rotArrayX.Count; 69 | 70 | rotAverageY = ClampAngle(rotAverageY, minimumY, maximumY); 71 | rotAverageX = ClampAngle(rotAverageX, minimumX, maximumX); 72 | 73 | Quaternion yQuaternion = Quaternion.AngleAxis(rotAverageY, Vector3.left); 74 | Quaternion xQuaternion = Quaternion.AngleAxis(rotAverageX, Vector3.up); 75 | 76 | transform.localRotation = originalRotation * xQuaternion * yQuaternion; 77 | } 78 | else if (axes == RotationAxes.MouseX) 79 | { 80 | rotAverageX = 0f; 81 | 82 | rotationX += Input.GetAxis("Mouse X") * sensitivityX; 83 | 84 | rotArrayX.Add(rotationX); 85 | 86 | if (rotArrayX.Count >= frameCounter) 87 | { 88 | rotArrayX.RemoveAt(0); 89 | } 90 | for (int i = 0; i < rotArrayX.Count; i++) 91 | { 92 | rotAverageX += rotArrayX[i]; 93 | } 94 | rotAverageX /= rotArrayX.Count; 95 | 96 | rotAverageX = ClampAngle(rotAverageX, minimumX, maximumX); 97 | 98 | Quaternion xQuaternion = Quaternion.AngleAxis(rotAverageX, Vector3.up); 99 | transform.localRotation = originalRotation * xQuaternion; 100 | } 101 | else 102 | { 103 | rotAverageY = 0f; 104 | 105 | rotationY += Input.GetAxis("Mouse Y") * sensitivityY; 106 | 107 | rotArrayY.Add(rotationY); 108 | 109 | if (rotArrayY.Count >= frameCounter) 110 | { 111 | rotArrayY.RemoveAt(0); 112 | } 113 | for (int j = 0; j < rotArrayY.Count; j++) 114 | { 115 | rotAverageY += rotArrayY[j]; 116 | } 117 | rotAverageY /= rotArrayY.Count; 118 | 119 | rotAverageY = ClampAngle(rotAverageY, minimumY, maximumY); 120 | 121 | Quaternion yQuaternion = Quaternion.AngleAxis(rotAverageY, Vector3.left); 122 | transform.localRotation = originalRotation * yQuaternion; 123 | } 124 | 125 | //added code for moving the camera 126 | Vector3 mousePosition = gameObject.transform.position; 127 | float moveForward = Input.GetAxis("Vertical") * MoveSpeed * Time.deltaTime; 128 | float moveSide = Input.GetAxis("Horizontal") * MoveSpeed * Time.deltaTime; 129 | 130 | mousePosition.x += moveSide; 131 | mousePosition.z += moveForward; 132 | 133 | Vector3 dir = mousePosition - gameObject.transform.position; 134 | 135 | gameObject.transform.Translate(dir, Space.Self); 136 | } 137 | 138 | void Start() 139 | { 140 | Rigidbody rb = GetComponent(); 141 | if (rb) 142 | rb.freezeRotation = true; 143 | originalRotation = transform.localRotation; 144 | } 145 | 146 | public static float ClampAngle(float angle, float min, float max) 147 | { 148 | angle = angle % 360; 149 | if ((angle >= -360F) && (angle <= 360F)) 150 | { 151 | if (angle < -360F) 152 | { 153 | angle += 360F; 154 | } 155 | if (angle > 360F) 156 | { 157 | angle -= 360F; 158 | } 159 | } 160 | return Mathf.Clamp(angle, min, max); 161 | } 162 | } -------------------------------------------------------------------------------- /Assets/Scripts/SmoothMouseLook.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: abc2a6e39d1568f4cb7dca371b722ac8 3 | timeCreated: 1492536350 4 | licenseType: Pro 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Assets/Scripts/Testing_Scripts.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 2d6dddac14907e947903b798c8ddbd3a 3 | folderAsset: yes 4 | timeCreated: 1497306957 5 | licenseType: Free 6 | DefaultImporter: 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /Assets/Scripts/Testing_Scripts/Make_Icosahedron.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Collections; 3 | 4 | public class Make_Icosahedron : MonoBehaviour 5 | { 6 | //Found at: https://schneide.wordpress.com/2016/07/15/generating-an-icosphere-in-c/ 7 | 8 | float X = 0.525731112119133606f; 9 | float Z = 0.850650808352039932f; 10 | float N = 0.0f; 11 | 12 | public Material material; 13 | public float size = 4.0f; 14 | public Mesh mesh; 15 | 16 | GameObject[] Verticies; 17 | 18 | // Use this for initialization 19 | void Start () 20 | { 21 | X *= size; 22 | Z *= size; 23 | 24 | Verticies = new GameObject[12]; 25 | for(int i = 0; i < 12; ++i) 26 | { 27 | Verticies[i] = new GameObject(); 28 | Verticies[i].name = i.ToString(); 29 | Verticies[i].AddComponent(typeof(MeshFilter)); 30 | Verticies[i].AddComponent(typeof(MeshRenderer)); 31 | Verticies[i].GetComponent().mesh = mesh; 32 | Verticies[i].transform.parent = gameObject.transform; 33 | Verticies[i].GetComponent().material = material; 34 | } 35 | 36 | Vector3[] positions = new Vector3[12]; 37 | //{-X,N,Z}, {X,N,Z}, {-X,N,-Z}, {X,N,-Z}, 38 | positions[0] = new Vector3(-X, N, Z); 39 | positions[1] = new Vector3( X, N, Z); 40 | positions[2] = new Vector3(-X, N, -Z); 41 | positions[3] = new Vector3( X, N, -Z); 42 | //{N,Z,X}, {N,Z,-X}, {N,-Z,X}, {N,-Z,-X}, 43 | positions[4] = new Vector3( N, Z, X); 44 | positions[5] = new Vector3(N, Z, -X); 45 | positions[6] = new Vector3(N, -Z, X); 46 | positions[7] = new Vector3(N, -Z, -X); 47 | //{Z,X,N}, {-Z,X, N}, {Z,-X,N}, {-Z,-X, N} 48 | positions[8] = new Vector3(Z, X, N); 49 | positions[9] = new Vector3(-Z, X, N); 50 | positions[10] = new Vector3(Z, -X, N); 51 | positions[11] = new Vector3(-Z, -X, N); 52 | 53 | for(int i = 0; i < 12; ++i) 54 | { 55 | Verticies[i].transform.position = positions[i]; 56 | } 57 | } 58 | 59 | void Update() 60 | { 61 | 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /Assets/Scripts/Testing_Scripts/Make_Icosahedron.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 351ef0e22b1d7e2448ad8206f23a0d85 3 | timeCreated: 1496543898 4 | licenseType: Pro 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Assets/Scripts/Testing_Scripts/OBB_Bounds.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Collections; 3 | 4 | //For some reason all Bounds in Unity are done using AABB which is stupid as fuck 5 | //But luckily Box Colliders are OBBs 6 | 7 | [ExecuteInEditMode] 8 | public class OBB_Bounds : MonoBehaviour 9 | { 10 | MeshRenderer meshRenderer = null; 11 | MeshFilter meshFilter = null; 12 | Octree.OBB obb; 13 | 14 | static Octree.AABB aabb = null; 15 | static float size = 5.0f; 16 | 17 | // Use this for initialization 18 | void Awake () 19 | { 20 | meshRenderer = GetComponent(); 21 | meshFilter = GetComponent(); 22 | 23 | obb = new Octree.OBB(); 24 | 25 | if(aabb == null) 26 | { 27 | aabb = new Octree.AABB(); 28 | aabb.Min = new Vector3(-size, -size, -size); 29 | aabb.Max = new Vector3(size, size, size); 30 | } 31 | } 32 | 33 | void Update() 34 | { 35 | Vector3 c = meshRenderer.bounds.center; 36 | Mesh m = meshFilter.sharedMesh; 37 | 38 | if(obb != null) 39 | { 40 | obb.c = c; 41 | obb.x = c + transform.right; 42 | obb.y = c + transform.up; 43 | obb.z = c + transform.forward; 44 | obb.e = m.bounds.extents; 45 | obb.s = transform.localScale; 46 | } 47 | } 48 | 49 | //Helper function to draw OBB using Gizmos class 50 | void Draw_OBB(Color colour) 51 | { 52 | Vector3[] Positions = new Vector3[8]; 53 | Vector3 e = new Vector3(obb.e.x * obb.s.x, obb.e.y * obb.s.y, obb.e.z * obb.s.z); 54 | 55 | Positions[0] = obb.c + (((obb.x - obb.c) * e.x) + ((obb.y - obb.c) * e.y) + ((obb.z - obb.c) * e.z)); 56 | Positions[1] = obb.c + (((obb.c - obb.x) * e.x) + ((obb.y - obb.c) * e.y) + ((obb.z - obb.c) * e.z)); 57 | Positions[2] = obb.c + (((obb.x - obb.c) * e.x) + ((obb.c - obb.y) * e.y) + ((obb.z - obb.c) * e.z)); 58 | Positions[3] = obb.c + (((obb.c - obb.x) * e.x) + ((obb.c - obb.y) * e.y) + ((obb.z - obb.c) * e.z)); 59 | Positions[4] = obb.c + (((obb.x - obb.c) * e.x) + ((obb.y - obb.c) * e.y) + ((obb.c - obb.z) * e.z)); 60 | Positions[5] = obb.c + (((obb.c - obb.x) * e.x) + ((obb.y - obb.c) * e.y) + ((obb.c - obb.z) * e.z)); 61 | Positions[6] = obb.c + (((obb.x - obb.c) * e.x) + ((obb.c - obb.y) * e.y) + ((obb.c - obb.z) * e.z)); 62 | Positions[7] = obb.c + (((obb.c - obb.x) * e.x) + ((obb.c - obb.y) * e.y) + ((obb.c - obb.z) * e.z)); 63 | 64 | Gizmos.color = colour; 65 | Gizmos.DrawLine(Positions[0], Positions[1]); 66 | Gizmos.DrawLine(Positions[1], Positions[3]); 67 | Gizmos.DrawLine(Positions[3], Positions[2]); 68 | Gizmos.DrawLine(Positions[2], Positions[0]); 69 | 70 | Gizmos.DrawLine(Positions[4], Positions[5]); 71 | Gizmos.DrawLine(Positions[5], Positions[7]); 72 | Gizmos.DrawLine(Positions[7], Positions[6]); 73 | Gizmos.DrawLine(Positions[6], Positions[4]); 74 | //Connectors 75 | Gizmos.DrawLine(Positions[0], Positions[4]); 76 | Gizmos.DrawLine(Positions[1], Positions[5]); 77 | Gizmos.DrawLine(Positions[3], Positions[7]); 78 | Gizmos.DrawLine(Positions[2], Positions[6]); 79 | } 80 | 81 | void Draw_AABB() 82 | { 83 | Baked_Octree.CUBE c = new Baked_Octree.CUBE(); 84 | Vector3 p = Vector3.zero; 85 | float s = (aabb.Max.x - aabb.Min.x) * 0.5f; 86 | 87 | //top 88 | c.v0 = new Vector3(p.x - s, p.y + s, p.z + s); 89 | c.v1 = new Vector3(p.x - s, p.y + s, p.z - s); 90 | c.v2 = new Vector3(p.x + s, p.y + s, p.z + s); 91 | c.v3 = new Vector3(p.x + s, p.y + s, p.z - s); 92 | //bottom 93 | c.v4 = new Vector3(p.x - s, p.y - s, p.z + s); 94 | c.v5 = new Vector3(p.x - s, p.y - s, p.z - s); 95 | c.v6 = new Vector3(p.x + s, p.y - s, p.z + s); 96 | c.v7 = new Vector3(p.x + s, p.y - s, p.z - s); 97 | 98 | Gizmos.color = Color.black; 99 | //Top square 100 | Gizmos.DrawLine(c.v0, c.v1); 101 | Gizmos.DrawLine(c.v1, c.v3); 102 | Gizmos.DrawLine(c.v3, c.v2); 103 | Gizmos.DrawLine(c.v2, c.v0); 104 | 105 | //Bottom square 106 | Gizmos.DrawLine(c.v4, c.v5); 107 | Gizmos.DrawLine(c.v5, c.v7); 108 | Gizmos.DrawLine(c.v7, c.v6); 109 | Gizmos.DrawLine(c.v6, c.v4); 110 | 111 | //Connecting Top and Bottom 112 | Gizmos.DrawLine(c.v0, c.v4); 113 | Gizmos.DrawLine(c.v1, c.v5); 114 | Gizmos.DrawLine(c.v3, c.v7); 115 | Gizmos.DrawLine(c.v2, c.v6); 116 | } 117 | 118 | void OnDrawGizmos() 119 | { 120 | if(obb != null) 121 | { 122 | Gizmos.color = Color.red; 123 | Gizmos.DrawLine(obb.c, obb.x); 124 | Gizmos.color = Color.green; 125 | Gizmos.DrawLine(obb.c, obb.y); 126 | Gizmos.color = Color.blue; 127 | Gizmos.DrawLine(obb.c, obb.z); 128 | 129 | if(Octree.OctreeNode.Intersect(aabb, obb)) 130 | { 131 | Draw_OBB(Color.red); 132 | } 133 | else 134 | { 135 | Draw_OBB(Color.green); 136 | } 137 | } 138 | 139 | if (aabb != null) 140 | { 141 | Draw_AABB(); 142 | } 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /Assets/Scripts/Testing_Scripts/OBB_Bounds.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: c48538d99507c1a468aab5220c05bea3 3 | timeCreated: 1497141431 4 | licenseType: Pro 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Assets/Scripts/Testing_Scripts/PointsOnSphere.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Collections; 3 | using System.Collections.Generic; //for List 4 | 5 | //a Class to test how to Points/Vectors on a sphere.. used later on when Raycasting spherically from a point 6 | //Code found at : http://answers.unity3d.com/questions/410992/how-do-i-get-raycasts-to-cast-symmetrically.html 7 | [ExecuteInEditMode] 8 | public class PointsOnSphere : MonoBehaviour 9 | { 10 | [Range(1.0f, 10.0f)] 11 | public float Radius = 5.0f; 12 | 13 | public float NumberOfPoints = 10; 14 | Vector3[] points; 15 | 16 | public static Vector3[] GetPoints(float numPoints) 17 | { 18 | List points = new List(); 19 | float i = Mathf.PI * (3 - Mathf.Sqrt(5)); 20 | float offset = 2 / numPoints; 21 | float halfOffset = 0.5f * offset; 22 | float y = 0; 23 | float r = 0; 24 | float phi = 0; 25 | int currPoint = 0; 26 | for (; currPoint < numPoints; currPoint++) 27 | { 28 | y = currPoint * offset - 1 + halfOffset; 29 | r = Mathf.Sqrt(1 - y * y); 30 | phi = currPoint * i; 31 | Vector3 point = new Vector3(Mathf.Cos(phi) * r, y, Mathf.Sin(phi) * r); 32 | if (!points.Contains(point)) points.Add(point); 33 | } 34 | return points.ToArray(); 35 | } 36 | 37 | // Update is called once per frame 38 | void Update () 39 | { 40 | points = GetPoints(NumberOfPoints); 41 | } 42 | 43 | //Draws them in editor 44 | void OnDrawGizmos() 45 | { 46 | //Draws all the points 47 | Gizmos.color = Color.black; 48 | Vector3 p = transform.position; 49 | for(int i = 0; i < points.Length; ++i) 50 | { 51 | Gizmos.DrawLine(p, points[i] * Radius); 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Assets/Scripts/Testing_Scripts/PointsOnSphere.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: b12c55dceeb277948aff23f312f1a07c 3 | timeCreated: 1497306986 4 | licenseType: Free 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Unity-GPU-Particles 2 | Using Direct X compute shaders to emit and update up to 5,000,000 particles simultaneously. Particle-Mesh collision detection is done using a pre-baked octree. 3 | 4 | 5 | To use simply arrange the mesh as you wish and click Bake Octree. 6 | For maximum tree depth it may take between 1-2 minutes to complete. 7 | 8 | Collision accuracy really depends on number of nodes and tree depth, but the octree creation can take extremely 9 | long beyond a depth of 5. 10 | 11 | 12 | ![](https://github.com/Brozef92/Unity-GPU-Particles/blob/master/gif_1.gif) 13 | ![](https://github.com/Brozef92/Unity-GPU-Particles/blob/master/gif_2.gif) 14 | ![](https://github.com/Brozef92/Unity-GPU-Particles/blob/master/gif_3.gif) 15 | -------------------------------------------------------------------------------- /gif_1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Brozef92/Unity-GPU-Particles/65ba327ab819822a53d284d783ccbcaf4f4b5623/gif_1.gif -------------------------------------------------------------------------------- /gif_2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Brozef92/Unity-GPU-Particles/65ba327ab819822a53d284d783ccbcaf4f4b5623/gif_2.gif -------------------------------------------------------------------------------- /gif_3.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Brozef92/Unity-GPU-Particles/65ba327ab819822a53d284d783ccbcaf4f4b5623/gif_3.gif --------------------------------------------------------------------------------