private void SetSwarmerTransformForRandomSetup(
        ref SwarmShaderSwarmerState inoutSwarmerState)
    {
        inoutSwarmerState.Position =
            Vector3.Scale(new Vector3(3.0f, 0.5f, 3.0f), UnityEngine.Random.insideUnitSphere);

        Vector3 randomForward = UnityEngine.Random.onUnitSphere;
        Vector3 randomUp      = UnityEngine.Random.onUnitSphere;

        Vector3.OrthoNormalize(ref randomForward, ref randomUp);

        inoutSwarmerState.LocalForward = randomForward;
        inoutSwarmerState.LocalUp      = randomUp;
    }
    private void SetSwarmerTransformForPinwheelTiledFloor(
        int swarmerIndex,
        ref SwarmShaderSwarmerState inoutSwarmerState)
    {
        int swarmersPerTile = 6;

        // NOTE: The pinwheels-clusters tile as a hexagon.
        int tileIndex    = (swarmerIndex / swarmersPerTile);
        int patternIndex = (swarmerIndex % swarmersPerTile);

        int tilingStride    = Mathf.CeilToInt(Mathf.Sqrt(SwarmerCount / (float)swarmersPerTile));
        int tileRowIndex    = ((tileIndex / tilingStride) - (tilingStride / 2));
        int tileColumnIndex = ((tileIndex % tilingStride) - (tilingStride / 2));

        Vector3 swarmerCenterToRightWingtip = (SwarmerModelScale * swarmerModel.SwarmerCenterToRightWingtip);

        Matrix4x4 patternTransform = Matrix4x4.identity;

        patternTransform = (
            Matrix4x4.TRS(
                (-1.0f * swarmerCenterToRightWingtip),
                Quaternion.identity,
                Vector3.one) *
            patternTransform);

        patternTransform = (
            Matrix4x4.TRS(
                Vector3.zero,
                Quaternion.AngleAxis((90.0f + (60.0f * patternIndex)), Vector3.up),
                Vector3.one) *
            patternTransform);

        Vector3 tileSize = new Vector3(
            (6.0f * (swarmerCenterToRightWingtip.x * Mathf.Sin(60.0f * Mathf.Deg2Rad))),
            0.0f,
            (3.0f * swarmerCenterToRightWingtip.x));

        Vector3 tilePosition =
            Vector3.Scale(
                tileSize,
                new Vector3(
                    (tileColumnIndex / 2.0f),
                    0.0f,
                    tileRowIndex + (0.5f * (tileColumnIndex % 2))));

        inoutSwarmerState.Position     = (tilePosition + patternTransform.MultiplyPoint(Vector3.zero));
        inoutSwarmerState.LocalForward = patternTransform.MultiplyVector(Vector3.forward);
        inoutSwarmerState.LocalUp      = patternTransform.MultiplyVector(Vector3.up);
    }
    private bool TryAllocateBuffers()
    {
        bool result = false;

        if (!SystemInfo.supportsComputeShaders)
        {
            Debug.LogError("Compute shaders are not supported on this machine. Is DX11 or later installed?");
        }
        else if ((BehaviorComputeShader != null) &&
                 (CommonSwarmComputeShader != null))
        {
            advanceSwarmersKernel =
                BehaviorComputeShader.FindKernel("kernel_advance_swarmer_states");

            kernelForExtractSwarmerPositions =
                CommonSwarmComputeShader.FindKernel("kernel_extract_swarmer_positions");

            if (forcefieldsBufferQueue == null)
            {
                forcefieldsBufferQueue =
                    new Queue <TypedComputeBuffer <SwarmShaderForcefieldState> >(ForcefieldsComputeBufferCount);

                while (forcefieldsBufferQueue.Count < ForcefieldsComputeBufferCount)
                {
                    forcefieldsBufferQueue.Enqueue(
                        new TypedComputeBuffer <SwarmShaderForcefieldState>(MaxForcefieldCount));
                }

                // NOTE: There's no need to immediately initialize the buffers, since they will be populated per-frame.
            }

            if (swarmerStateBuffers.IsInitialized == false)
            {
                var initialSwarmers = new List <SwarmShaderSwarmerState>(SwarmerCount);

                for (int swarmerIndex = 0; swarmerIndex < SwarmerCount; ++swarmerIndex)
                {
                    var newSwarmerState = new SwarmShaderSwarmerState();

                    SetSwarmerTransformForHexLaceTiledFloor(swarmerIndex, ref newSwarmerState);
                    //SetSwarmerTransformForPinwheelTiledFloor(swarmerIndex, ref newSwarmerState);
                    //SetSwarmerTransformForRandomSetup(ref newSwarmerState);
                    //SetSwarmerTransformForTripletTiledFloor(swarmerIndex, ref newSwarmerState);

                    newSwarmerState.Speed = SwarmerSpeedIdle;

                    initialSwarmers.Add(newSwarmerState);
                }

                swarmerStateBuffers.TryAllocateComputeBuffersWithValues(initialSwarmers.ToArray());
            }

            if ((advanceSwarmersKernel != -1) &&
                (kernelForExtractSwarmerPositions != -1) &&
                (forcefieldsBufferQueue != null) &&
                swarmerStateBuffers.IsInitialized)
            {
                result = true;
            }
            else
            {
                // Abort any partial-allocations.
                ReleaseBuffers();
            }
        }

        if (DebugEnabled)
        {
            Debug.LogFormat("Compute buffer allocation attempted. [Success={0}]", result);
        }

        return(result);
    }
    private void SetSwarmerTransformForTripletTiledFloor(
        int swarmerIndex,
        ref SwarmShaderSwarmerState inoutSwarmerState)
    {
        int swarmersPerTile = 6;

        // NOTE: For simplicity the tiles are pairs of triplets (forming a parallelogram).
        int tileIndex    = (swarmerIndex / swarmersPerTile);
        int patternIndex = (swarmerIndex % swarmersPerTile);

        int tilingStride    = Mathf.CeilToInt(Mathf.Sqrt(SwarmerCount / (float)swarmersPerTile));
        int tileRowIndex    = ((tileIndex / tilingStride) - (tilingStride / 2));
        int tileColumnIndex = ((tileIndex % tilingStride) - (tilingStride / 2));

        Vector3 swarmerCenterToRightWingtip = (SwarmerModelScale * swarmerModel.SwarmerCenterToRightWingtip);

        Matrix4x4 patternTransform = Matrix4x4.identity;

        if (patternIndex < 3)
        {
            patternTransform = (
                Matrix4x4.TRS(
                    (-1.0f * swarmerCenterToRightWingtip),
                    Quaternion.identity,
                    Vector3.one) *
                patternTransform);

            patternTransform = (
                Matrix4x4.TRS(
                    Vector3.zero,
                    Quaternion.AngleAxis((90.0f + (120.0f * patternIndex)), Vector3.up),
                    Vector3.one) *
                patternTransform);
        }
        else
        {
            Vector3 swarmerCenterToLeftWingtip = swarmerCenterToRightWingtip;
            swarmerCenterToLeftWingtip.x *= -1.0f;

            patternTransform = (
                Matrix4x4.TRS(
                    (-1.0f * swarmerCenterToLeftWingtip),
                    Quaternion.identity,
                    Vector3.one) *
                patternTransform);

            patternTransform = (
                Matrix4x4.TRS(
                    Vector3.zero,
                    Quaternion.AngleAxis((-90.0f + (120.0f * patternIndex)), Vector3.up),
                    Vector3.one) *
                patternTransform);
        }

        Vector3 tileSize = new Vector3(
            (6.0f * (swarmerCenterToRightWingtip.x * Mathf.Sin(60.0f * Mathf.Deg2Rad))),
            0.0f,
            (3.0f * swarmerCenterToRightWingtip.x));

        Vector3 tilePosition =
            Vector3.Scale(
                tileSize,
                new Vector3(
                    (tileColumnIndex / 2.0f),
                    0.0f,
                    tileRowIndex + (0.5f * (tileColumnIndex % 2))));

        inoutSwarmerState.Position     = (tilePosition + patternTransform.MultiplyPoint(Vector3.zero));
        inoutSwarmerState.LocalForward = patternTransform.MultiplyVector(Vector3.forward);
        inoutSwarmerState.LocalUp      = patternTransform.MultiplyVector(Vector3.up);
    }
    private void SetSwarmerTransformForHexLaceTiledFloor(
        int swarmerIndex,
        ref SwarmShaderSwarmerState inoutSwarmerState)
    {
        int swarmersPerTile = 6;

        // NOTE: The lace-pattern tiles as a hexagon.
        int tileIndex    = (swarmerIndex / swarmersPerTile);
        int patternIndex = (swarmerIndex % swarmersPerTile);

        int tilingStride    = Mathf.CeilToInt(Mathf.Sqrt(SwarmerCount / (float)swarmersPerTile));
        int tileRowIndex    = ((tileIndex / tilingStride) - (tilingStride / 2));
        int tileColumnIndex = ((tileIndex % tilingStride) - (tilingStride / 2));

        Vector3 swarmerCenterToRightWingtip = (SwarmerModelScale * swarmerModel.SwarmerCenterToRightWingtip);

        Matrix4x4 patternTransform = Matrix4x4.identity;

        // Place the swarmer along the X+ axis, facing Z+.
        {
            patternTransform = (
                Matrix4x4.TRS(
                    (
                        (-1.0f * swarmerCenterToRightWingtip) +
                        ((2 * swarmerCenterToRightWingtip.x) * Vector3.right)
                    ),
                    Quaternion.identity,
                    Vector3.one) *
                patternTransform);
        }

        // Rotate the swarmer into position around the tile's center.
        {
            Vector3 originToTileCenter =
                new Vector3(
                    swarmerCenterToRightWingtip.x,
                    0.0f,
                    (2 * (swarmerCenterToRightWingtip.x * Mathf.Sin(60.0f * Mathf.Deg2Rad))));

            patternTransform = (
                Matrix4x4.TRS(
                    (-1.0f * originToTileCenter),
                    Quaternion.identity,
                    Vector3.one) *
                patternTransform);

            patternTransform = (
                Matrix4x4.TRS(
                    Vector3.zero,
                    Quaternion.AngleAxis((60.0f * patternIndex), Vector3.up),
                    Vector3.one) *
                patternTransform);

            patternTransform = (
                Matrix4x4.TRS(
                    originToTileCenter,
                    Quaternion.identity,
                    Vector3.one) *
                patternTransform);
        }

        Vector3 tileSize = new Vector3(
            (6.0f * swarmerCenterToRightWingtip.x),
            0.0f,
            (4.0f * (swarmerCenterToRightWingtip.x * Mathf.Sin(60.0f * Mathf.Deg2Rad))));

        Vector3 tilePosition =
            Vector3.Scale(
                tileSize,
                new Vector3(
                    (tileColumnIndex / 2.0f),
                    0.0f,
                    tileRowIndex + (0.5f * (tileColumnIndex % 2))));

        inoutSwarmerState.Position     = (tilePosition + patternTransform.MultiplyPoint(Vector3.zero));
        inoutSwarmerState.LocalForward = patternTransform.MultiplyVector(Vector3.forward);
        inoutSwarmerState.LocalUp      = patternTransform.MultiplyVector(((patternIndex % 2) == 0) ? Vector3.up : Vector3.down);
    }