├── AnimatedProjector.cs ├── FPSDisplay.cs ├── Guide.pdf ├── Parallel.cs ├── Process.compute ├── README.md ├── Underwater.cs ├── main.cs └── moveCam.cs /AnimatedProjector.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | // Change the texture showing underwater light effect (caustics) 6 | // at the desired frequency (here 30Hz) 7 | // The textures are taken from an array inside Unity editor 8 | public class AnimatedProjector : MonoBehaviour { 9 | 10 | public float fps = 30.0f; 11 | public Texture2D[] frames; 12 | 13 | int frameIndex; 14 | Projector projector; 15 | 16 | // Use this for initialization 17 | void Start () { 18 | projector = GetComponent (); 19 | NextFrame (); 20 | InvokeRepeating ("NextFrame", 1 / fps, 1 / fps); 21 | } 22 | 23 | void NextFrame() { 24 | projector.material.SetTexture ("_ShadowTex", frames [frameIndex]); 25 | frameIndex = (frameIndex + 1) % frames.Length; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /FPSDisplay.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using UnityEngine; 3 | 4 | // Displaying the actual FPS rate of the application 5 | public class FPSDisplay : MonoBehaviour { 6 | 7 | 8 | float deltaTime = 0.0f; 9 | 10 | void Update() 11 | { 12 | deltaTime += (Time.deltaTime - deltaTime) * 0.1f; // Smooth evolution of the FPS, to avoid sudden changes 13 | } 14 | 15 | void OnGUI() 16 | { 17 | int w = Screen.width, h = Screen.height; 18 | 19 | GUIStyle style = new GUIStyle(); 20 | 21 | Rect rect = new Rect(0, 0, w, h * 2 / 100); 22 | style.alignment = TextAnchor.UpperLeft; 23 | style.fontSize = h * 2 / 100; 24 | style.normal.textColor = new Color (0.0f, 0.0f, 0.5f, 1.0f); 25 | float msec = deltaTime * 1000.0f; 26 | float fps = 1.0f / deltaTime; 27 | string text = string.Format("{0:0.0} ms ({1:0.} fps)", msec, fps); 28 | GUI.Label(rect, text, style); 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /Guide.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IntelSoftware/unity-parallel-gpu/87ce86792ff467afa1ed06c4456eda9ad62ec4c0/Guide.pdf -------------------------------------------------------------------------------- /Parallel.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using UnityEngine; 3 | using System; 4 | 5 | public delegate void DelegateFor(int i); 6 | public delegate void DelegateProcess(int from, int to); 7 | 8 | 9 | public class Parallel : MonoBehaviour { 10 | 11 | public static void For(int from, int to, DelegateFor delFor){ 12 | DelegateProcess process = delegate (int chunkStart, int chunkEnd) { 13 | for(int i = chunkStart; i < chunkEnd; ++i) 14 | delFor(i); 15 | }; 16 | 17 | int cores = Environment.ProcessorCount; 18 | int chunks = (to - from) / cores; 19 | 20 | IAsyncResult[] asyncResults = new IAsyncResult[cores]; 21 | 22 | int end = 0; 23 | for (int i = 0; i < cores; ++i) { 24 | int start = i * chunks; 25 | end = Math.Min (start + chunks, to); 26 | asyncResults [i] = process.BeginInvoke (start, end, null, null); 27 | } 28 | 29 | for (int i = end ; i < to; ++i) 30 | delFor (i); 31 | 32 | for (int i = 0; i < cores; ++i) 33 | process.EndInvoke (asyncResults [i]); 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /Process.compute: -------------------------------------------------------------------------------- 1 | // Each #kernel tells which function to compile; you can have many kernels 2 | #pragma kernel Calc 3 | 4 | class State{ 5 | float speed; 6 | float3 position, forward; 7 | float4 rotation; 8 | }; 9 | 10 | class Rock{ 11 | float3 position; 12 | float radius; 13 | }; 14 | 15 | float3 settings; 16 | float3 borders; 17 | float2 velocities; 18 | float deltaTime; 19 | float3 speeds; 20 | 21 | #define MAX_SPEED speeds.x 22 | #define ROT_SPEED speeds.y 23 | #define OOBOUNDS_SPEED speeds.z 24 | 25 | #define TOTAL_FISH settings.x 26 | #define NUM_ROCKS settings.y 27 | #define DIST_NEIGHBOR settings.z 28 | 29 | #define BORDER_X borders.x 30 | #define BORDER_Y borders.y 31 | #define BORDER_Z borders.z 32 | 33 | #define AVOID_VELOCITY velocities.x 34 | #define DIR_VELOCITY velocities.y 35 | 36 | #define DELTA_TIME deltaTime 37 | 38 | RWStructuredBuffer readState; 39 | RWStructuredBuffer writeState; 40 | RWStructuredBuffer Rocks; 41 | 42 | bool Neighbor(float3 other, float3 curr, float dist, float limit); 43 | bool OutofBounds(float3 position); 44 | float3 random ( float3 low, float3 high ); 45 | float random ( float low, float high ); 46 | void seed ( int value ); 47 | 48 | // [numthreads(1024,1,1)] (16,8,8) (32,32,1) 49 | [numthreads(16,8,8)] 50 | void Calc (uint id : SV_GroupIndex, uint3 idT : SV_GroupID) // uint3 SV_DispatchThreadID ou uint SV_GroupIndex 51 | { 52 | int numNeighbors = 0, index = 16*8*8/4*idT.x + id; 53 | State other, current = readState[index]; 54 | float speed = 0; 55 | float3 zero = {0,0,0},one = {BORDER_X,BORDER_Y,BORDER_Z}; // "one" represent the biggest point in the tank, use for the range of the random function 56 | float3 position = current.position, curfwd = current.forward, center = zero, forward = zero, avoid = zero; 57 | float4 rotation = current.rotation; 58 | // Each fish has to look at all the other fishes, if one is close enough to create a flock, the flocking behavior is set. 59 | for(int i= 0; i flocking rules 83 | { 84 | forward = (DIR_VELOCITY * forward / numNeighbors) + (AVOID_VELOCITY * avoid) + (center / numNeighbors) + curfwd - position; 85 | speed = current.speed +(((speed/numNeighbors)-current.speed)*0.5); // linearly converges to the average speed of the flock 86 | } 87 | 88 | if(OutofBounds(position)){ // if a fish reaches the limit of the area, change its direction & speed 89 | speed = MAX_SPEED * random(0.01, 1.01); 90 | seed((int)(index+3) * DELTA_TIME*1000 * (100*speed)); 91 | forward = normalize(forward) + (DELTA_TIME * OOBOUNDS_SPEED *(random(-one,one) - position)); 92 | seed((int)(index+4) * DELTA_TIME*1000 * (100*speed)); 93 | } 94 | 95 | // Check if the fish is close to a rock - if so, add an avoidance force 96 | int iObs =0; 97 | float3 avoidObs = {0,0,0}; 98 | float3 ahead = position + normalize(forward) * DELTA_TIME * speed; 99 | float3 ahead2 = position + normalize(forward) * DELTA_TIME * speed / 2; 100 | while(iObs < NUM_ROCKS){ 101 | if(distance(ahead,Rocks[iObs].position) < Rocks[iObs].radius){ 102 | avoidObs = normalize(ahead - Rocks[iObs].position) * MAX_SPEED; // MAX_SPEED ou speed 103 | if((avoidObs.x != zero.x)||(avoidObs.y != zero.y)||(avoidObs.z != zero.z)) 104 | iObs = NUM_ROCKS; 105 | } 106 | if(distance(ahead2,Rocks[iObs].position) < Rocks[iObs].radius){ 107 | avoidObs = normalize(ahead2 - Rocks[iObs].position) * MAX_SPEED; 108 | if((avoidObs.x != zero.x)||(avoidObs.y != zero.y)||(avoidObs.z != zero.z)) 109 | iObs = NUM_ROCKS; 110 | } 111 | iObs++; 112 | } 113 | forward += avoidObs; 114 | 115 | position += DELTA_TIME * speed * normalize(forward); // translate the fish 116 | 117 | writeState[index].speed = speed; 118 | writeState[index].position = position; // updating every properties which are not rotations 119 | writeState[index].forward = normalize(forward); 120 | } 121 | 122 | // If two fishes could be considered as neighbors or not. Depending of their distance but also the direcion they are facing, using dot product. 123 | bool Neighbor(float3 other, float3 curr, float dist, float limit){ 124 | if(dist > limit) // if distance greater than the neighbor limit 125 | return false; 126 | float scal = dot(other, curr); 127 | float test, a, b, c = 0.5 * limit; 128 | if(scal<0){ 129 | a = -0.2 * limit; 130 | b = a + c; 131 | } 132 | else{ 133 | a = -0.24 * limit; // if they are facing almost the same direction (dot product > 0) 134 | b = -a + 0.5 * limit; // they're likely more suitable to be in the same flock 135 | } 136 | test = (pow(scal,2)*a) + (b*scal) + c; // maximal distance to get them as neighbors regarding their properties - quadratic function 137 | return dist <= test; 138 | } 139 | 140 | bool OutofBounds(float3 position){ 141 | if (abs(position.x) >= BORDER_X) 142 | return true; 143 | if (abs(position.y) >= BORDER_Y) 144 | return true; 145 | if (abs(position.z) >= BORDER_Z) 146 | return true; 147 | return false; 148 | } 149 | 150 | // Random HLSL functions, provided by NVidia 151 | 152 | #define RANDOM_IA 16807 153 | #define RANDOM_IM 2147483647 154 | #define RANDOM_AM (1.0f/float(RANDOM_IM)) 155 | #define RANDOM_IQ 127773 156 | #define RANDOM_IR 2836 157 | #define RANDOM_MASK 123459876 158 | 159 | 160 | int random_x; 161 | 162 | 163 | float random () 164 | { 165 | int k; 166 | float ans; 167 | 168 | random_x ^= RANDOM_MASK; 169 | k = random_x / RANDOM_IQ; 170 | random_x = RANDOM_IA * (random_x - k * RANDOM_IQ ) - RANDOM_IR * k; 171 | 172 | if ( random_x < 0 ) 173 | random_x += RANDOM_IM; 174 | 175 | ans = RANDOM_AM * random_x; 176 | random_x ^= RANDOM_MASK; 177 | 178 | return ans; 179 | } 180 | 181 | 182 | float random ( float low, float high ) 183 | { 184 | float v = random(); 185 | return low * ( 1.0f - v ) + high * v; 186 | } 187 | 188 | float3 random ( float3 low, float3 high ) 189 | { 190 | float3 v = float3( random(), random(), random() ); 191 | return low * ( 1.0f - v ) + high * v; 192 | } 193 | 194 | 195 | void seed ( int value ) 196 | { 197 | random_x = value; 198 | random(); 199 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # unity-parallel-cpu 2 | 3 | Source code for the Intel parallel project with Unity. 4 | School of fish is executed, with the ability to choose if the application should be executed in the CPU, inside one singlethread or multi threads or, inside the GPU. 5 | 6 | AnimatedProjector.cs : Script to project the caustics light textures on the ground. 7 | 8 | fishState.cs : Contains the class representing the physical properties of each fish. 9 | 10 | FPSDisplay.cs : Script to display the frames per second. 11 | 12 | moveCam.cs : Script to move the camera using the directional keys and the mouse. 13 | 14 | Parallel.cs : Class providing the ability to do multithreading. 15 | 16 | Process.compute : Compute shader written in HLSL to execute the flocking algorithm in the GPU. 17 | 18 | Underwater.cs : Script to add underwater effects to the scene. 19 | 20 | We can also set some parameters from the command line using the following : 21 | 22 | - “-t” : size of the allowed area for the fishes ( <~> tank) 23 | 24 | - “-f” : number of fishes to display 25 | 26 | - “-r” : number of rocks to add to the scene 27 | 28 | - “-n” : maximal distance for two fishes to be neighbor 29 | 30 | - “-m” : mode to launch the application. 0: CPU Singlethread. 1: CPU Multithread. 2: GPU. 31 | 32 | - “-s” : (at the END) if this is set, display a simpler scene with a visible tank, like below. If not the scene will look more realistic with dynamic water, and background 33 | -------------------------------------------------------------------------------- /Underwater.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | // Depending of the position of the camera 6 | // Changing some rendering parameters 7 | // Like enabling a fog, along with a background color 8 | // To give a sort of underwater effect 9 | 10 | public class Underwater : MonoBehaviour { 11 | 12 | public Camera cam; 13 | 14 | bool fog; 15 | Color fogColor; 16 | float fogDensity; 17 | Material skybox; 18 | Material noSkybox; 19 | 20 | public static float limit; 21 | public static bool mode = false; 22 | void Start () { 23 | fog = RenderSettings.fog; 24 | fogColor = RenderSettings.fogColor; 25 | fogDensity = RenderSettings.fogDensity; 26 | skybox = RenderSettings.skybox; 27 | cam.backgroundColor = new Color (0, 0.4f, 0.7f, 1); 28 | } 29 | 30 | // Update is called once per frame 31 | void Update () { 32 | if (mode && transform.position.y < limit) { // if camera under lvl of water -> underwater render settings 33 | RenderSettings.fog = true; 34 | RenderSettings.fogColor = new Color (0, 0.4f, 0.7f, 0.6f); 35 | RenderSettings.fogDensity = 0.04f; 36 | RenderSettings.skybox = noSkybox; 37 | } else { 38 | RenderSettings.fog = fog; 39 | RenderSettings.fogColor = fogColor; 40 | RenderSettings.fogDensity = fogDensity; 41 | RenderSettings.skybox = skybox; 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /main.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | public class main : MonoBehaviour { 6 | struct fishState { 7 | 8 | public float speed; 9 | public Vector3 position, forward; 10 | public Quaternion rotation; 11 | 12 | public fishState(fishState state){ 13 | speed = state.speed; 14 | position = state.position; 15 | forward = state.forward; 16 | rotation = state.rotation; 17 | } 18 | 19 | public fishState(float s, Vector3 pos, Vector3 fwd, Quaternion rot){ 20 | speed = s; 21 | position = pos; 22 | forward = fwd; 23 | rotation = rot; 24 | } 25 | 26 | public void Set(float s, Vector3 pos, Vector3 fwd, Quaternion rot){ 27 | speed = s; 28 | position = pos; 29 | forward = fwd; 30 | rotation = rot; 31 | } 32 | }; 33 | // Links to the items we want to display 34 | public ComputeShader shader; 35 | // Fish 36 | public Mesh mesh; 37 | public Material material; 38 | // Aquarium 39 | public GameObject tank; 40 | public GameObject ground; 41 | // Water 42 | public Terrain terrain; 43 | public GameObject water; 44 | public GameObject projector; 45 | // Obstacles 46 | public GameObject rock; 47 | GameObject[] rocks; 48 | struct s_Rock{ 49 | public Vector3 position; 50 | public float radius; 51 | } 52 | s_Rock[] obstacles; 53 | 54 | // Parameters of the application 55 | public static float tankHeight = 5f; // height of the tank 56 | public static bool mode = true; // false: aquarium - true: water 57 | int appMode = 02; // 0: Single thread 1: Multi thread 2: GPU 58 | float distNeighbor = 1.1f; // maximal distance for two fish to create a flock together 59 | int numFishes = 1000; // number of fish 60 | int numRocks = 50; // number of rocks into the scene 61 | int instanceLimit = 1023; // max number of instances which could be drawn at the same time 62 | 63 | // Movement and scene properties -- constants 64 | float deltaTime; // time to complete the last frame 65 | 66 | //Speeds : Maximum speed for a fish, rotation speed 67 | //speed to add when near to the limits of the area (i.e edge reactivity) 68 | const float max_speed = 3.5f, rotationSpeed = 2.2f * 7, outOfBoundsSpeed = 7; 69 | // Flocking : weight/velocity of each flocking rule. 70 | //cohesion velocity is not provided, should be left with a unit weight 71 | const float avoid_velocity = 0.3f, direction_velocity = 8; 72 | // Dimension : How long/deep the area will be 73 | // related to the height. how many times longer/deeper than higher 74 | const float length = 4.0f, depth = 2.75f; 75 | // Borders of the area following each direction 76 | float borderX, borderY, borderZ; 77 | Vector3 scale = Vector3.one; // scale of the mesh to draw, keeping this the same for all fishes. 78 | 79 | 80 | public delegate void Updater(); 81 | Updater updater; 82 | public delegate void UpdaterMode(int i, int max); 83 | UpdaterMode updaterMode; 84 | 85 | // data 86 | Matrix4x4[][] fishArray; // contains properties needed to draw the fish 87 | fishState[][] states; // physical properties of the fish to calculate the flocks 88 | int nbmat, left; // number of matrices to store all the instances of fish, and number of fish left in the last matrix 89 | int nbGroups; // GPU : number of group of threads which are needed to compute the calculation, 1 thread per fish 90 | int idx = 0; 91 | int kernel; // GPU : index of the function (kernel) inside the compute shader, which needs to be run for the flocking algorithm 92 | 93 | // swap list for read/write 94 | int read = 0, write = 1, tmp; 95 | 96 | // Use this for initialization 97 | void Start () { 98 | Init (); // Inititialize the data to match the parameters of the application 99 | for (int i = 0; i < numFishes; i++) { 100 | if (i != 0 && i % instanceLimit == 0) 101 | idx++; 102 | SetFish (i); // Creation of a fish 103 | } 104 | Debug.Log (string.Format ("Total fish: {0} \t Instance limit: {1} \t Nb matrices: {2} \t Fish within last matrix: {3} \t tankHeight: {4}", numFishes, instanceLimit, nbmat, left, tankHeight)); 105 | } 106 | 107 | // Update is called once per frame 108 | void Update () { 109 | updater (); 110 | } 111 | 112 | void UpdateStates(int i){ // called for each fish after the shader has done all calculations 113 | if (states [write] [i].forward != Vector3.zero) 114 | states[write][i].rotation = Quaternion.Slerp(states[read][i].rotation, 115 | Quaternion.LookRotation(states[write][i].forward), rotationSpeed * deltaTime); 116 | // setting the TRS matrix to draw the fish 117 | fishArray [idx] [i % instanceLimit].SetTRS(states [write] [i].position, states [write] [i].rotation, scale); 118 | } 119 | 120 | void RunShader(){ // Setting and filling the buffers about the states of the fishes 121 | ComputeBuffer rState = new ComputeBuffer (numFishes, System.Runtime.InteropServices.Marshal.SizeOf (states[0][0])); 122 | ComputeBuffer wState = new ComputeBuffer (numFishes, System.Runtime.InteropServices.Marshal.SizeOf (states[0][0])); 123 | shader.SetBuffer (kernel, "readState", rState); 124 | shader.SetBuffer(kernel, "writeState", wState); 125 | rState.SetData (states[read]); 126 | wState.SetData (states [write]); 127 | // run the shader : flocking algorithm 128 | shader.Dispatch (kernel, nbGroups, 1, 1); 129 | // Once the work is done, get back the written data and save it 130 | wState.GetData (states[write]); 131 | } 132 | 133 | 134 | void Calc(int index){ // Update the properties of each fish 135 | fishState other, current = states [read] [index]; 136 | Vector3 center = Vector3.zero, forward = Vector3.zero, avoid = Vector3.zero, position = current.position, curfwd = current.forward; 137 | Quaternion rotation = current.rotation; 138 | float speed = 0.0f, curspeed = current.speed; 139 | int numNeighbors = 0; 140 | System.Random random = new System.Random (); 141 | 142 | // Each fish has to look at all the other fish, if one is close enough to create a flock, the flocking behavior is set. 143 | for (int j = 0; j < numFishes; j++) { 144 | if (index != j) { 145 | other = states [read] [j]; 146 | Vector3 othpos = other.position, othfwd = other.forward; 147 | if(Call(position.x, othpos.x, distNeighbor)){ // Check if the x-position of two fish is close enough to create a flock 148 | float dist = Vector3.Distance (othpos, position); // To avoid always calculating the distance, which is quite heavy 149 | if (Neighbor (othfwd, curfwd, dist, distNeighbor)) { 150 | numNeighbors++; // UPDATE FLOCKING BEHAVIOR 151 | center += othpos; // COHESION 152 | speed += other.speed; 153 | forward += othfwd; // ALIGNMENT 154 | if (dist <= 0.30f) // SEPARATION 155 | avoid += (position - othpos).normalized / dist; 156 | } 157 | } 158 | } 159 | } 160 | 161 | if (numNeighbors == 0) { // if the fish has no one to swim with 162 | speed = curspeed; 163 | forward = curfwd; 164 | } else { // if not alone, updating its properties considering its neighbors <-> flocking rules 165 | forward = (direction_velocity * forward / numNeighbors) + (avoid_velocity * avoid) + (center / numNeighbors) + curfwd - position; 166 | speed = curspeed + (((speed / numNeighbors) - curspeed) * 0.50f); // linearly converges to the average speed of the flock 167 | } 168 | 169 | if (OutofBounds (position)) { // if a fish reaches the limit of the tank, change its direction and speed 170 | Vector3 rand = new Vector3 (borderX * Mathf.Cos (360 * (float)random.NextDouble ()), 171 | borderY * Mathf.Cos (360 * (float)random.NextDouble ()), 172 | borderZ * Mathf.Cos (360 * (float)random.NextDouble ())); 173 | forward = forward.normalized + (deltaTime * outOfBoundsSpeed * (rand - position)); 174 | speed = (float)random.NextDouble () * max_speed; 175 | } 176 | 177 | forward += Rocks (position, forward, speed, max_speed); // Check if the fish is close to a rock - if so, add an avoidance force 178 | position += (forward.normalized * deltaTime * speed); // Translate the fish 179 | 180 | if (forward != Vector3.zero) 181 | rotation = Quaternion.Slerp (rotation, Quaternion.LookRotation (forward), rotationSpeed * deltaTime); // Rotate the fish towards its forward direction 182 | 183 | states[write][index].Set(speed, position, forward.normalized, rotation); // update the write state buffer 184 | fishArray [idx] [index % instanceLimit].SetTRS (position, rotation, scale); // update the matrix to draw the fish 185 | } 186 | 187 | // If two fish are close enough to maybe be neighbors 188 | // Considering their x-position 189 | bool Call(float xA, float xB, float limit){ 190 | float abs = (xA - xB); 191 | abs *= abs; 192 | if (abs < (limit * limit)) 193 | return true; 194 | return false; 195 | } 196 | 197 | // Considering a fish 198 | // Will check if this fish is about to collide into any rocks of the scene 199 | Vector3 Rocks(Vector3 position, Vector3 fwd, float speed, float max){ 200 | Vector3 avoid; 201 | fwd = fwd.normalized * deltaTime * speed; 202 | for (int i = 0; i < obstacles.Length; i++) { 203 | avoid = Rock (position, fwd, obstacles[i].position, obstacles[i].radius); 204 | if (avoid != Vector3.zero) 205 | return avoid; 206 | } 207 | return Vector3.zero; 208 | } 209 | 210 | // Considering a fish 211 | // Will check if this fish is about to collide into a rock of the scene 212 | Vector3 Rock(Vector3 pos, Vector3 fwd, Vector3 rocpos, float scale){ 213 | Vector3 avoid = Vector3.zero; 214 | Vector3 ahead = pos + fwd; 215 | Vector3 ahead2 = pos + fwd/2; 216 | if(Vector3.Distance(ahead,rocpos) < scale){ 217 | avoid = (ahead - rocpos).normalized * max_speed; 218 | return avoid; 219 | } 220 | if(Vector3.Distance(ahead2,rocpos) < scale){ 221 | avoid = (ahead2 - rocpos).normalized * max_speed; 222 | return avoid; 223 | } 224 | return avoid; 225 | } 226 | 227 | // If two fish could be considered neighbors or not. 228 | // Depending of their distance but also their forward direction, using dot product of vectors. 229 | bool Neighbor(Vector3 selected, Vector3 focus, float dist, float neighbor){ 230 | if (dist > neighbor) { 231 | return false; 232 | } 233 | float scal = Vector3.Dot (selected, focus); 234 | float a, b, c = (0.5f * neighbor); 235 | if(scal<0){ 236 | a = -0.2f * neighbor; 237 | b = a + c; 238 | } 239 | else{ 240 | a = -0.24f * neighbor; // if they are facing almost the same direction (dot product > 0) 241 | b = -a + 0.5f * neighbor; // they're likely more suitable to be in the same flock 242 | } 243 | float test = ((scal * scal * a) + (b * scal) + c); // maximal distance to get them as neighbors regarding their properties - quadratic function 244 | return dist <= test; 245 | } 246 | 247 | bool OutofBounds(Vector3 position){ 248 | if (Mathf.Abs(position.x) >= borderX) 249 | return true; 250 | if (Mathf.Abs(position.z) >= borderZ) 251 | return true; 252 | if (Mathf.Abs(position.y) >= borderY) 253 | return true; 254 | return false; 255 | } 256 | 257 | void SetFish(int i){ // Creation of a fish with random parameters 258 | float speed = Random.Range (0.5f, max_speed); 259 | Vector3 pos = new Vector3 (Random.Range(-borderX, borderX), Random.Range(-borderY, borderY), Random.Range(-borderZ, borderZ)); 260 | Vector3 goal = new Vector3 (Random.Range(-borderX, borderX), Random.Range(-borderY, borderY), Random.Range(-borderZ, borderZ)); 261 | while (pos == goal) 262 | goal = new Vector3 (Random.Range(-borderX, borderX), Random.Range(-borderY, borderY), Random.Range(-borderZ, borderZ)); 263 | Vector3 forward = (goal - pos).normalized; 264 | // rotate to make it face its randomly set direction 265 | Quaternion rotation = Quaternion.Slerp(Quaternion.identity,Quaternion.LookRotation (forward), rotationSpeed * Time.deltaTime); 266 | // initialize the read/write buffer with the state of the fish 267 | states [0][i] = new fishState (speed, pos, forward, rotation); 268 | states [1][i] = new fishState (states [0][i]); 269 | } 270 | 271 | void Init(){ 272 | getInput (); 273 | SetScene (); 274 | InitShader (); 275 | SetDelegates (); 276 | 277 | int threadsPgroup = 1024/4; // Shader parameter, number of threads per group, to get the number of groups 278 | nbGroups = Mathf.CeilToInt (numFishes / (float)threadsPgroup); 279 | 280 | states = new fishState[2][]; 281 | states [0] = new fishState[numFishes]; 282 | states [1] = new fishState[numFishes]; 283 | 284 | nbmat = Mathf.CeilToInt (numFishes / (float)instanceLimit); // Number of matrices which need to be created to draw all the instances 285 | left = numFishes - ((nbmat - 1) * instanceLimit); // Number of instances in the last matrix, as it could be less than 1023 286 | fishArray = new Matrix4x4[nbmat][]; // Creation of the matrices 287 | for (int i = 0; i < nbmat; i++) { 288 | if (i != nbmat - 1) 289 | fishArray [i] = new Matrix4x4[instanceLimit]; 290 | else 291 | fishArray [i] = new Matrix4x4[left]; 292 | } 293 | } 294 | 295 | // Initialization of the data which needs to be sent to the compute shader 296 | // For GPU Application 297 | void InitShader(){ 298 | kernel = shader.FindKernel ("Calc"); 299 | shader.SetVector ("settings", new Vector3 (numFishes, numRocks, distNeighbor)); 300 | shader.SetVector ("borders", new Vector3 (borderX, borderY, borderZ)); 301 | shader.SetVector("velocities", new Vector2(avoid_velocity,direction_velocity)); 302 | shader.SetVector ("speeds", new Vector3 (max_speed,rotationSpeed,outOfBoundsSpeed)); 303 | if (numRocks > 0) { // if we want rocks, setting and filling the rock buffer at start-up, it won't ever change 304 | ComputeBuffer bRocks = new ComputeBuffer (numRocks, System.Runtime.InteropServices.Marshal.SizeOf (obstacles[0])); 305 | shader.SetBuffer(kernel, "Rocks", bRocks); 306 | bRocks.SetData (obstacles); 307 | } 308 | } 309 | 310 | // Considering the desired mode : CPU MT/ST or GPU 311 | // Set the delegates to execute the right function 312 | // During each frame update 313 | void SetDelegates(){ 314 | if (appMode == 2) { 315 | updaterMode = new UpdaterMode (GPU); 316 | updater = new Updater (Update_GPU); 317 | } else { 318 | updater = new Updater (Update_CPU); 319 | if (appMode==1) 320 | updaterMode = new UpdaterMode (MT); 321 | else 322 | updaterMode = new UpdaterMode (ST); 323 | } 324 | } 325 | 326 | // Selected mode : CPU - Multithread 327 | // Application of the flocking algorithm 328 | void MT(int i, int max){ 329 | if(i==max) 330 | Parallel.For (0, left, delegate (int id) {Calc (id + i * instanceLimit);}); 331 | else 332 | Parallel.For (0, instanceLimit, delegate (int id) {Calc (id + i * instanceLimit);}); 333 | } 334 | 335 | // Selected mode : GPU 336 | // Update the fish state after the flocking algorithm being executed in the GPU 337 | void GPU(int i, int max){ 338 | if(i==max) 339 | Parallel.For (0, left, delegate (int id) {UpdateStates (id + i*instanceLimit);}); 340 | else 341 | Parallel.For(0,instanceLimit,delegate (int id){UpdateStates(id + i*instanceLimit);}); 342 | } 343 | 344 | // Selected mode : CPU - Singlethread 345 | // Application of the flocking algorithm 346 | void ST(int i, int max){ 347 | if(i==max) 348 | for (int j = 0; j < left; j++) {Calc (j + i*instanceLimit);} 349 | else 350 | for (int j = 0; j < instanceLimit; j++) {Calc (j + i*instanceLimit);} 351 | } 352 | 353 | void Update_GPU(){ // Update of the scene if the selected mode is GPU 354 | deltaTime = Time.deltaTime; 355 | idx = 0; 356 | 357 | shader.SetFloat ("deltaTime", deltaTime); 358 | RunShader (); 359 | int max = nbmat - 1; 360 | for (int j = 0; j < nbmat; j++) { // To draw the fish, several calls have to be done because of the max number of instances per draw (1023) 361 | updaterMode (j, max); 362 | Graphics.DrawMeshInstanced (mesh, 0, material, fishArray[j]); 363 | idx++; 364 | } 365 | tmp = read; // swap between read and write buffers 366 | read = write; 367 | write = tmp; 368 | 369 | } 370 | // Update of the scene if the selected mode is CPU 371 | // Select then between single thread or multi thread 372 | void Update_CPU(){ 373 | deltaTime = Time.deltaTime; 374 | idx = 0; 375 | int max = nbmat - 1; 376 | for (int j = 0; j < nbmat; j++) { // To draw the fish, several calls have to be done because of the max number of instances per draw (1023) 377 | updaterMode (j, max); 378 | Graphics.DrawMeshInstanced (mesh, 0, material, fishArray[j]); 379 | idx++; 380 | } 381 | tmp = read; // swap between read and write buffers 382 | read = write; 383 | write = tmp; 384 | } 385 | 386 | // Initializing the scene along with the borders which are different considering the axes 387 | // Also considering the selected mode, "water"-like or tank 388 | void SetScene(){ 389 | borderX = tankHeight * length; 390 | borderY = tankHeight/2; 391 | borderZ = tankHeight * depth; 392 | 393 | Transform rockT = rock.transform; 394 | obstacles = new s_Rock[numRocks]; 395 | rocks = new GameObject[numRocks]; 396 | for (int i = 0; i < numRocks; i++) { 397 | float scale = Random.Range (0.15f, 1.00f) * tankHeight; 398 | Vector3 position = new Vector3 (Random.Range (-borderX, borderX), -borderY + scale*Random.Range(0,0.2f), Random.Range (-borderZ, borderZ)); 399 | obstacles[i].position = position; 400 | obstacles[i].radius = scale / 2; 401 | rock.transform.localScale = new Vector3 (scale, scale, scale); 402 | rocks [i] = (GameObject)Instantiate (rock, position, Quaternion.identity); 403 | } 404 | 405 | if (!mode) { 406 | water.SetActive (false); 407 | projector.SetActive (false); 408 | terrain.enabled = false; 409 | tank.transform.position = Vector3.zero; 410 | tank.transform.localScale = new Vector3 (2.2f* borderX, 2.2f * borderY, 2.2f* borderZ); 411 | ground.transform.position = new Vector3 (0, - 1.1f * borderY, 0); 412 | ground.transform.localScale = new Vector3 (2.2f* borderX, 0.0001f, 2.2f* borderZ); 413 | 414 | } else { 415 | terrain.transform.position = new Vector3 (-200, -borderY * 1.1f, -200); 416 | water.transform.position = new Vector3 (0, borderY * 1.15f, 0); 417 | Underwater.limit = -terrain.transform.position.y; 418 | Underwater.mode = true; 419 | ground.SetActive (false); 420 | tank.SetActive (false); 421 | } 422 | } 423 | 424 | // Getting parameters of the application through command line arguments 425 | // if some parameters are less than 0, set them to 0 426 | // in the same case, other will stick to their default value 427 | // shown under parameters of the application (l54) 428 | void getInput(){ 429 | string[] args = System.Environment.GetCommandLineArgs (); 430 | string input; 431 | for(int i=0; i 0) 442 | tankHeight = float.Parse(input); 443 | } 444 | if(args[i] == "-n"){ 445 | input = args[i+1]; 446 | if (float.Parse (input) < 0) 447 | distNeighbor = 0; 448 | else 449 | distNeighbor = float.Parse(input); 450 | } 451 | if (args [i] == "-m") { 452 | input = args[i+1]; 453 | if (System.Convert.ToInt16 (input) == 0) 454 | appMode = 0; 455 | else if (System.Convert.ToInt16 (input) == 1) 456 | appMode = 1; 457 | } 458 | if (args [i] == "-r") { 459 | input = args[i+1]; 460 | if (System.Convert.ToInt16 (input) < 1) 461 | numRocks = 0; 462 | else 463 | numRocks = System.Convert.ToInt16(input); 464 | } 465 | if (args [i] == "-s") 466 | mode = false; 467 | } 468 | } 469 | } 470 | -------------------------------------------------------------------------------- /moveCam.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | // Move the camera of the scene, 6 | // With controls similar of the FPS-ones 7 | // Move forward/backward and strafe left/right with pressing the directional keys 8 | // Rotate the camera and going up/down with the mouse 9 | 10 | // A rotation matrix is created 11 | // to always strafe following the perpendicular directions 12 | // to where the camera is pointing at 13 | 14 | public class moveCam : MonoBehaviour { 15 | float speed = 0.1f; 16 | float sensitivity = 0.4f; 17 | float moveUD, moveLR, rotX, rotY; 18 | Matrix4x4 Perp; // Rotation matrix 19 | 20 | void Start () { 21 | Perp = Matrix4x4.zero; 22 | Perp.m11 = 1; 23 | Perp.m00 = Mathf.Cos(90*Mathf.PI/180); 24 | Perp.m22 = Mathf.Cos(90*Mathf.PI/180); 25 | Perp.m02 = Mathf.Sin (90 * Mathf.PI / 180); 26 | Perp.m20 = -Mathf.Sin (90 * Mathf.PI / 180); 27 | // Initializing the camera to make it pointing at the scene 28 | transform.position = new Vector3 (0, main.tankHeight/2 , -main.tankHeight* 8 / 3); 29 | transform.rotation = Quaternion.Euler (new Vector3(10, 2, 0)); 30 | } 31 | 32 | // Update is called once per frame 33 | void Update () { 34 | moveUD = Input.GetAxis ("Vertical") * speed; // getting the user control inputs 35 | moveLR = Input.GetAxis ("Horizontal") * speed; 36 | rotX = Input.GetAxis ("Mouse X") * sensitivity; 37 | rotY = Input.GetAxis ("Mouse Y") * sensitivity; 38 | 39 | transform.Rotate (0,rotX,0); // rotating the camera 40 | Vector3 ortho = Perp * transform.forward; // getting the perpendicular direction (strafe) 41 | Vector3 movement = new Vector3(moveLR * ortho.x + moveUD*transform.forward.x, 42 | Mathf.Sin (rotY) * sensitivity, 43 | moveLR * ortho.z + moveUD*transform.forward.z); 44 | transform.position += movement; // translating the camera 45 | } 46 | } 47 | --------------------------------------------------------------------------------