private async Task UpdateWisps(float time, float deltaTime)
    {
        await new WaitForBackgroundThread();

        for (int i = 0; i < forces.Length; i++)
        {
            Force force = forces[i];
            force.IsIntersecting = false;
            forces[i]            = force;
        }

        // Update wisp forces
        totalHeatTarget = 0;
        for (int i = 0; i < numWisps; i++)
        {
            Wisp w1 = wisps[i];

            for (int forceIndex = 0; forceIndex < forces.Length; forceIndex++)
            {
                Force force            = forces[forceIndex];
                float touchingDist     = force.Radius + w1.Radius;
                float prevPointDistSqr = (w1.Point - force.PrevPoint).sqrMagnitude;
                float pointDistSqr     = (w1.Point - force.Point).sqrMagnitude;
                if (pointDistSqr < (touchingDist * touchingDist) || prevPointDistSqr < (touchingDist * touchingDist))
                {
                    force.IsIntersecting = true;
                    w1.Heat            = Mathf.Clamp01(w1.Heat + (deltaTime * heatGainSpeed));
                    w1.Velocity       -= (force.Velocity * w1.Heat);
                    forces[forceIndex] = force;
                }
            }

            w1.Radius = AddNoise(w1.TargetRadius, ambientNoise, time, i);

            Vector3 point = w1.Point;
            point      += (w1.Velocity * deltaTime);
            w1.Velocity = Vector3.Lerp(w1.Velocity, w1.Velocity * velocityDampen, deltaTime);
            point       = Vector3.Lerp(point, w1.TargetPoint, seekStrength * deltaTime);
            point       = AddNoise(point, NoiseSetting.Lerp(ambientNoise, agitatedNoise, w1.Heat), time, i);
            w1.Point    = point;

            w1.Heat          = Mathf.Clamp01(w1.Heat - (deltaTime * heatDissipateSpeed));
            totalHeatTarget += w1.Heat;

            wisps[i] = w1;
        }

        await Task.Yield();

        // Generate quads
        for (int i = 0; i < numWisps; i++)
        {
            Wisp        wisp = wisps[i];
            Quad        quad = quads[i];
            RenderOrder r    = renderOrder[i];

            wisp.Depth = (wisp.Point - cameraPos).sqrMagnitude;

            Color color = baseWispColor.Evaluate(Mathf.Repeat((time + (i * offsetMultiplier)) * colorCycleSpeed, 1f));
            color += heatWispColor.Evaluate(wisp.Heat);
            color  = Color.Lerp(color, transitionColor, transitionColor.a);

            wisps[i] = wisp;
            int tileNum = wisp.TileOffset + currentTileNum;
            if (tileNum >= numTiles)
            {
                tileNum -= numTiles;
            }

            quad     = Quad.FromWisp(color, wisp.Point, wisp.Radius + (wisp.Heat * heatRadiusMultiplier), cameraUp, cameraRht, tileOffsets[tileNum], tileScale);
            quads[i] = quad;

            r.Index        = i;
            r.Depth        = wisp.Depth;
            renderOrder[i] = r;
        }

        renderOrder.Sort(delegate(RenderOrder r1, RenderOrder r2) { return(r2.Depth.CompareTo(r1.Depth)); });
    }
    public override void Initialize(Vector3 surfacePosition)
    {
        base.Initialize(surfacePosition);

        ISceneTransitionService transitionService;

        if (MixedRealityServiceRegistry.TryGetService <ISceneTransitionService>(out transitionService))
        {
            transitionService.OnTransitionStarted += OnTransitionStarted;
        }

        tileScale   = 1f / numColumns;
        tileOffsets = new Vector2[numTiles];

        int tileNum = 0;

        for (int row = 0; row < numRows; row++)
        {
            for (int column = 0; column < numColumns; column++)
            {
                Vector2 uv = Vector2.zero;
                uv.x = tileScale * column;
                uv.y = (tileScale * -row) - tileScale;
                tileOffsets[tileNum] = uv;

                tileNum++;
                if (tileNum >= numTiles)
                {
                    break;
                }
            }

            if (tileNum >= numTiles)
            {
                break;
            }
        }

        noise = new FastSimplexNoise();

        transitionColor = Color.black;

        forces      = new Force[fingers.Length];
        fingertips  = new Fingertip[fingers.Length];
        wisps       = new Wisp[numWisps];
        quads       = new Quad[numWisps];
        finalQuads  = new Quad[numWisps];
        renderOrder = new List <RenderOrder>(numWisps);

        for (int i = 0; i < numWisps; i++)
        {
            Wisp wisp = new Wisp();
            wisp.TargetPoint  = SurfacePosition + (Random.insideUnitSphere * SurfaceRadius);
            wisp.Point        = wisp.TargetPoint;
            wisp.Velocity     = Random.insideUnitSphere * initialVelocity;
            wisp.TargetRadius = Mathf.Lerp(wispSize.x, wispSize.y, (float)noise.Evaluate(wisp.Point.x, wisp.Point.y, wisp.Point.z));
            wisp.Radius       = wisp.TargetRadius;
            wisp.TileOffset   = Random.Range(0, numTiles);
            wisps[i]          = wisp;

            Quad quad = Quad.FromWisp(baseWispColor.Evaluate(Random.Range(0f, 1f)), wisp.Point, wisp.Radius, Vector3.up, Vector3.right, tileOffsets[wisp.TileOffset], tileScale);
            quad.Color    = transitionColor;
            quads[i]      = quad;
            finalQuads[i] = quads[i];

            RenderOrder r = new RenderOrder();
            renderOrder.Add(r);
        }

        for (int i = 0; i < fingers.Length; i++)
        {
            Transform finger    = fingers[i];
            Fingertip fingertip = fingertips[i];
            fingertip.Block     = new MaterialPropertyBlock();
            fingertip.Trail     = finger.GetComponentInChildren <TrailRenderer>();
            fingertip.Mesh      = finger.GetComponentInChildren <MeshRenderer>();
            fingertip.PingAudio = finger.GetComponentInChildren <AudioSource>();
            fingertips[i]       = fingertip;
        }

        Camera.onPostRender += PostRender;

        updateWisps = true;
        Task task = UpdateWispsTask();
    }