simplestarの技術ブログ

目的を書いて、思想と試行、結果と考察、そして具体的な手段を記録します。

Unity:CubeWalkGameのキューブのテクスチャ座標と回転

# まえがき

これらの記事シリーズの続きです。
今回は作業メモになっているので、開発者以外が読むと辛い文章かも…

simplestar-tech.hatenablog.com
simplestar-tech.hatenablog.com


シリーズ名は CubeWalkGame として、前回はチャンクをまたぐキューブ情報の取得を確認しました。
前回はマクロ視点でしたが、今回はミクロ視点ということで、キューブ単体のテクスチャ座標と回転表現をやります。

## 無回転でテクスチャ貼り

テクスチャ座標はこれなんだけど、どうやって貼り付け?

new Vector2[]{
            // Top
            new Vector2 (0, 1),
            new Vector2 (1, 1),
            new Vector2 (1, 0),
            // Bottom
            new Vector2 (1, 0),
            new Vector2 (0, 0),
            new Vector2 (0, 1),
            // Right up
            new Vector2 (1, 1),
            new Vector2 (2, 1),
            new Vector2 (2, 0),
            // Right down
            new Vector2 (2, 0),
            new Vector2 (1, 0),
            new Vector2 (1, 1),
            // Forward up
            new Vector2 (2, 1),
            new Vector2 (3, 1),
            new Vector2 (3, 0),
            // Forward down
            new Vector2 (3, 0),
            new Vector2 (2, 0),
            new Vector2 (2, 1),
            // Cross up
            new Vector2 (3, 1),
            new Vector2 (4.4142f, 1),
            new Vector2 (4.4142f, 0),
            // Cross down
            new Vector2 (4.4142f, 0),
            new Vector2 (3, 0),
            new Vector2 (3, 1),
        }

ひとまず頂点位置と同じようにセットしてみます。

マジックコードを発見しましたが、コレなんでしょう?

            const float mtf = 232f / 4096f;
            float mtoffu = col / 4.0f;
            float mtoffv = mtf * (16 - row % 17) % 17 + 0.0371f;


mesh.uv[vertCount * 2 + 0] = uvSrc[i * 2 + 0] * mtf + mtoffu;
mesh.uv[vertCount * 2 + 1] = uvSrc[i * 2 + 1] * mtf + mtoffv;

こちらの level 4 との対となるマジックコードでした。
github.com

f:id:simplestar_tech:20190616123849p:plain
コードを復元するように書き込むとこの通り

ロジックの肝心な箇所はこちら

                            int row = 10;
                            int col = 0;
                            const float mtf = 232f / 4096f;
                            float mtoffu = col / 4.0f;
                            float mtoffv = mtf * (16 - row % 17) % 17 + 0.0371f;
                            for (int vertexIndex = 0; vertexIndex < vertexCount * 2; vertexIndex += 2)
                            {
                                var uvXIndex = uvOffset + vertexIndex;
                                meshData.pUVVector2[uvXIndex + 0] *= mtf;
                                meshData.pUVVector2[uvXIndex + 0] += mtoffu;
                                meshData.pUVVector2[uvXIndex + 1] *= mtf;
                                meshData.pUVVector2[uvXIndex + 1] += mtoffv;
                            }
                            verticesOffset += vertexCount * 3;
                            uvOffset += vertexCount * 2;

いずれテクスチャの row, col の値は ChunkData から取らないといけませんね。

## 頂点を回転

この記事で試しておかなければならないこと、それが mathematics でその場で頂点回転できないか?というもの
キューブの頂点座標は原点中心で定義しているので、90度または パイ/2 ラジアンで回転する quaternion とかあれば良いのですが

ECS で使う Mathematics を確認します。

こういうときは過去記事を引っ張ってきて、関連リンクを読み漁ります。
simplestar-tech.hatenablog.com

そして発見!

using static Unity.Mathematics.math;

        /// <summary>Returns the result of rotating a vector by a unit quaternion.</summary>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static float3 rotate(quaternion q, float3 v)
        {
            float3 t = 2 * cross(q.value.xyz, v);
            return v + q.value.w * t + cross(q.value.xyz, t);
        }

仮で、すべての頂点を X 軸で 90 度回転させてみます。

コードはこんな感じかな?

                            for (int vertexIndex = 0; vertexIndex < vertexCount * 3; vertexIndex += 3)
                            {
                                var positionXIndex = verticesOffset + vertexIndex;
                                var pPosition = meshData.pVerticesVector3 + positionXIndex;

                                var q = Unity.Mathematics.quaternion.RotateX(PI / 2);
                                var p = float3(pPosition[0], pPosition[1], pPosition[2]);
                                var r = rotate(q, p);

                                pPosition[0] = r.x + x * ChunkManager.CubeSide;
                                pPosition[1] = r.y + y * ChunkManager.CubeSide;
                                pPosition[2] = r.z + z * ChunkManager.CubeSide;
                            }

変化は?

f:id:simplestar_tech:20190616140407p:plain
x軸で時計回りに90度回転しました

期待通りですね!

回転ごとに周辺チャンクの参照先を変えなければならないので、ここが頑張りどころです。

## 回転タイプを byte 値で判別

ここは強引にこう定義してみました。

internal enum CubeRotationType : byte
{
    Top000 = 0,
    Top090,
    Top180,
    Top270,

    Bottom000,
    Bottom090,
    Bottom180,
    Bottom270,

    Forward000,
    Forward090,
    Forward180,
    Forward270,

    Back000,
    Back090,
    Back180,
    Back270,

    Right000,
    Right090,
    Right180,
    Right270,

    Left000,
    Left090,
    Left180,
    Left270,

    Max
}

以前の回転結果図を参考に決めてみたけど、これでうまくいくか

   /// <summary>
    /// byte の回転タイプから Quaternion を作って返す
    /// </summary>
    static Unity.Mathematics.quaternion GetRotationQuaternion(CubeRotationType rotationType)
    {
        // @TODO: インデックスアクセスに
        var rotationQuaternion = Unity.Mathematics.quaternion.identity;
        switch (rotationType)
        {
            case CubeRotationType.Top000:
                break;
            case CubeRotationType.Top090:
                rotationQuaternion = Unity.Mathematics.quaternion.RotateY(PI / 2);
                break;
            case CubeRotationType.Top180:
                rotationQuaternion = Unity.Mathematics.quaternion.RotateY(PI);
                break;
            case CubeRotationType.Top270:
                rotationQuaternion = Unity.Mathematics.quaternion.RotateY(PI * 3 / 2);
                break;
            case CubeRotationType.Bottom000:
                rotationQuaternion = Unity.Mathematics.quaternion.RotateX(PI);
                break;
            case CubeRotationType.Bottom090:
                rotationQuaternion = mul(Unity.Mathematics.quaternion.RotateX(PI), Unity.Mathematics.quaternion.RotateY(PI / 2));
                break;
            case CubeRotationType.Bottom180:
                rotationQuaternion = mul(Unity.Mathematics.quaternion.RotateX(PI), Unity.Mathematics.quaternion.RotateY(PI));
                break;
            case CubeRotationType.Bottom270:
                rotationQuaternion = mul(Unity.Mathematics.quaternion.RotateX(PI), Unity.Mathematics.quaternion.RotateY(PI * 3 / 2));
                break;
            case CubeRotationType.Forward000:
                rotationQuaternion = mul(Unity.Mathematics.quaternion.RotateX(PI / 2), Unity.Mathematics.quaternion.RotateY(PI));
                break;
            case CubeRotationType.Forward090:
                rotationQuaternion = mul(Unity.Mathematics.quaternion.RotateX(PI / 2), Unity.Mathematics.quaternion.RotateY(PI * 3 / 2));
                break;
            case CubeRotationType.Forward180:
                rotationQuaternion = mul(Unity.Mathematics.quaternion.RotateX(PI / 2), Unity.Mathematics.quaternion.RotateY(0));
                break;
            case CubeRotationType.Forward270:
                rotationQuaternion = mul(Unity.Mathematics.quaternion.RotateX(PI / 2), Unity.Mathematics.quaternion.RotateY(PI / 2));
                break;
            case CubeRotationType.Back000:
                rotationQuaternion = Unity.Mathematics.quaternion.RotateX(-PI / 2);
                break;
            case CubeRotationType.Back090:
                rotationQuaternion = mul(Unity.Mathematics.quaternion.RotateX(-PI / 2), Unity.Mathematics.quaternion.RotateY(PI / 2));
                break;
            case CubeRotationType.Back180:
                rotationQuaternion = mul(Unity.Mathematics.quaternion.RotateX(-PI / 2), Unity.Mathematics.quaternion.RotateY(PI));
                break;
            case CubeRotationType.Back270:
                rotationQuaternion = mul(Unity.Mathematics.quaternion.RotateX(-PI / 2), Unity.Mathematics.quaternion.RotateY(PI * 3 / 2));
                break;
            case CubeRotationType.Right000:
                rotationQuaternion = mul(Unity.Mathematics.quaternion.RotateZ(-PI / 2), Unity.Mathematics.quaternion.RotateY(PI * 3 / 2));
                break;
            case CubeRotationType.Right090:
                rotationQuaternion = mul(Unity.Mathematics.quaternion.RotateZ(-PI / 2), Unity.Mathematics.quaternion.RotateY(0));
                break;
            case CubeRotationType.Right180:
                rotationQuaternion = mul(Unity.Mathematics.quaternion.RotateZ(-PI / 2), Unity.Mathematics.quaternion.RotateY(PI / 2));
                break;
            case CubeRotationType.Right270:
                rotationQuaternion = mul(Unity.Mathematics.quaternion.RotateZ(-PI / 2), Unity.Mathematics.quaternion.RotateY(PI));
                break;
            case CubeRotationType.Left000:
                rotationQuaternion = mul(Unity.Mathematics.quaternion.RotateZ(PI / 2), Unity.Mathematics.quaternion.RotateY(PI / 2));
                break;
            case CubeRotationType.Left090:
                rotationQuaternion = mul(Unity.Mathematics.quaternion.RotateZ(PI / 2), Unity.Mathematics.quaternion.RotateY(PI));
                break;
            case CubeRotationType.Left180:
                rotationQuaternion = mul(Unity.Mathematics.quaternion.RotateZ(PI / 2), Unity.Mathematics.quaternion.RotateY(PI * 3 / 2));
                break;
            case CubeRotationType.Left270:
                rotationQuaternion = Unity.Mathematics.quaternion.RotateZ(PI / 2);
                break;
            case CubeRotationType.Max:
                break;
            default:
                break;
        }
        return rotationQuaternion;
    }

次に周辺ブロックを見るためのインデックスを回転タイプから求めます。

いっぱい switch 文を書いていて気づく、enum が巨大な連番だったら switch 文の分岐書くより
配列のインデックスアクセスにした方が、結果取得早いしプログラミングも楽です。(読解し辛いのでコメント必須ですが)

前回のポインターポインターを活用することにして、次の通り定義してみました。

        #region 回転タイプと面タイプごとの方向
        var top = int3(0, 1, 0);
        var bottom = int3(0, -1, 0);
        var right = int3(1, 0, 0);
        var left = int3(-1, 0, 0);
        var forward = int3(0, 0, 1);
        var back = int3(0, 0, -1);
        this.ppRotationFaceDirections = (int3**)(UnsafeUtility.Malloc(sizeof(int3*) * (int)CubeRotationType.Max, sizeof(int3*), Allocator.Persistent));
        #region Top
        this.ppRotationFaceDirections[(int)CubeRotationType.Top000] = (int3*)(this.rotationFaceDirectionArray[(int)CubeRotationType.Top000] = new NativeArray<int3>(
            new int3[] {
                top,
                bottom,
                right,
                forward,
                bottom,
                top,
                left,
                back }, Allocator.Persistent)).GetUnsafePtr();
        this.ppRotationFaceDirections[(int)CubeRotationType.Top090] = (int3*)(this.rotationFaceDirectionArray[(int)CubeRotationType.Top090] = new NativeArray<int3>(
            new int3[] {
                top,
                bottom,
                back,
                right,
                bottom,
                top,
                forward,
                left }, Allocator.Persistent)).GetUnsafePtr();
        this.ppRotationFaceDirections[(int)CubeRotationType.Top180] = (int3*)(this.rotationFaceDirectionArray[(int)CubeRotationType.Top180] = new NativeArray<int3>(
            new int3[] {
                top,
                bottom,
                left,
                back,
                bottom,
                top,
                right,
                forward }, Allocator.Persistent)).GetUnsafePtr();
        this.ppRotationFaceDirections[(int)CubeRotationType.Top270] = (int3*)(this.rotationFaceDirectionArray[(int)CubeRotationType.Top270] = new NativeArray<int3>(
            new int3[] {
                top,
                bottom,
                forward,
                left,
                bottom,
                top,
                back,
                right }, Allocator.Persistent)).GetUnsafePtr();
        #endregion
        #region Bottom
        this.ppRotationFaceDirections[(int)CubeRotationType.Bottom000] = (int3*)(this.rotationFaceDirectionArray[(int)CubeRotationType.Bottom000] = new NativeArray<int3>(
            new int3[] {
                bottom,
                top,
                right,
                back,
                top,
                bottom,
                left,
                forward }, Allocator.Persistent)).GetUnsafePtr();
        this.ppRotationFaceDirections[(int)CubeRotationType.Bottom090] = (int3*)(this.rotationFaceDirectionArray[(int)CubeRotationType.Bottom090] = new NativeArray<int3>(
            new int3[] {
                bottom,
                top,
                forward,
                right,
                top,
                bottom,
                back,
                left }, Allocator.Persistent)).GetUnsafePtr();
        this.ppRotationFaceDirections[(int)CubeRotationType.Bottom180] = (int3*)(this.rotationFaceDirectionArray[(int)CubeRotationType.Bottom180] = new NativeArray<int3>(
            new int3[] {
                bottom,
                top,
                left,
                forward,
                top,
                bottom,
                right,
                back }, Allocator.Persistent)).GetUnsafePtr();
        this.ppRotationFaceDirections[(int)CubeRotationType.Bottom270] = (int3*)(this.rotationFaceDirectionArray[(int)CubeRotationType.Bottom270] = new NativeArray<int3>(
            new int3[] {
                bottom,
                top,
                back,
                left,
                top,
                bottom,
                forward,
                right }, Allocator.Persistent)).GetUnsafePtr();
        #endregion
        #region Forward
        this.ppRotationFaceDirections[(int)CubeRotationType.Forward000] = (int3*)(this.rotationFaceDirectionArray[(int)CubeRotationType.Forward000] = new NativeArray<int3>(
            new int3[] {
                forward,
                back,
                left,
                top,
                back,
                forward,
                right,
                bottom }, Allocator.Persistent)).GetUnsafePtr();
        this.ppRotationFaceDirections[(int)CubeRotationType.Forward090] = (int3*)(this.rotationFaceDirectionArray[(int)CubeRotationType.Forward090] = new NativeArray<int3>(
            new int3[] {
                forward,
                back,
                bottom,
                left,
                back,
                forward,
                top,
                right }, Allocator.Persistent)).GetUnsafePtr();
        this.ppRotationFaceDirections[(int)CubeRotationType.Forward180] = (int3*)(this.rotationFaceDirectionArray[(int)CubeRotationType.Forward180] = new NativeArray<int3>(
            new int3[] {
                forward,
                back,
                right,
                bottom,
                back,
                forward,
                left,
                top }, Allocator.Persistent)).GetUnsafePtr();
        this.ppRotationFaceDirections[(int)CubeRotationType.Forward270] = (int3*)(this.rotationFaceDirectionArray[(int)CubeRotationType.Forward270] = new NativeArray<int3>(
            new int3[] {
                forward,
                back,
                top,
                right,
                back,
                forward,
                bottom,
                left }, Allocator.Persistent)).GetUnsafePtr();
        #endregion
        #region Back
        this.ppRotationFaceDirections[(int)CubeRotationType.Back000] = (int3*)(this.rotationFaceDirectionArray[(int)CubeRotationType.Back000] = new NativeArray<int3>(
            new int3[] {
                back,
                forward,
                right,
                top,
                forward,
                back,
                left,
                bottom }, Allocator.Persistent)).GetUnsafePtr();
        this.ppRotationFaceDirections[(int)CubeRotationType.Back090] = (int3*)(this.rotationFaceDirectionArray[(int)CubeRotationType.Back090] = new NativeArray<int3>(
            new int3[] {
                back,
                forward,
                bottom,
                right,
                forward,
                back,
                top,
                left }, Allocator.Persistent)).GetUnsafePtr();
        this.ppRotationFaceDirections[(int)CubeRotationType.Back180] = (int3*)(this.rotationFaceDirectionArray[(int)CubeRotationType.Back180] = new NativeArray<int3>(
            new int3[] {
                back,
                forward,
                left,
                bottom,
                forward,
                back,
                right,
                top }, Allocator.Persistent)).GetUnsafePtr();
        this.ppRotationFaceDirections[(int)CubeRotationType.Back270] = (int3*)(this.rotationFaceDirectionArray[(int)CubeRotationType.Back270] = new NativeArray<int3>(
            new int3[] {
                back,
                forward,
                top,
                left,
                forward,
                back,
                bottom,
                right }, Allocator.Persistent)).GetUnsafePtr();
        #endregion
        #region Right
        this.ppRotationFaceDirections[(int)CubeRotationType.Right000] = (int3*)(this.rotationFaceDirectionArray[(int)CubeRotationType.Right000] = new NativeArray<int3>(
            new int3[] {
                right,
                left,
                forward,
                top,
                left,
                right,
                back,
                bottom }, Allocator.Persistent)).GetUnsafePtr();
        this.ppRotationFaceDirections[(int)CubeRotationType.Right090] = (int3*)(this.rotationFaceDirectionArray[(int)CubeRotationType.Right090] = new NativeArray<int3>(
            new int3[] {
                right,
                left,
                bottom,
                forward,
                left,
                right,
                top,
                back }, Allocator.Persistent)).GetUnsafePtr();
        this.ppRotationFaceDirections[(int)CubeRotationType.Right180] = (int3*)(this.rotationFaceDirectionArray[(int)CubeRotationType.Right180] = new NativeArray<int3>(
            new int3[] {
                right,
                left,
                back,
                bottom,
                left,
                right,
                forward,
                top }, Allocator.Persistent)).GetUnsafePtr();
        this.ppRotationFaceDirections[(int)CubeRotationType.Right270] = (int3*)(this.rotationFaceDirectionArray[(int)CubeRotationType.Right270] = new NativeArray<int3>(
            new int3[] {
                right,
                left,
                top,
                back,
                left,
                right,
                bottom,
                forward }, Allocator.Persistent)).GetUnsafePtr();
        #endregion
        #region Left
        this.ppRotationFaceDirections[(int)CubeRotationType.Left000] = (int3*)(this.rotationFaceDirectionArray[(int)CubeRotationType.Left000] = new NativeArray<int3>(
            new int3[] {
                left,
                right,
                back,
                top,
                right,
                left,
                forward,
                bottom }, Allocator.Persistent)).GetUnsafePtr();
        this.ppRotationFaceDirections[(int)CubeRotationType.Left090] = (int3*)(this.rotationFaceDirectionArray[(int)CubeRotationType.Left090] = new NativeArray<int3>(
            new int3[] {
                left,
                right,
                bottom,
                back,
                right,
                left,
                top,
                forward }, Allocator.Persistent)).GetUnsafePtr();
        this.ppRotationFaceDirections[(int)CubeRotationType.Left180] = (int3*)(this.rotationFaceDirectionArray[(int)CubeRotationType.Left180] = new NativeArray<int3>(
            new int3[] {
                left,
                right,
                forward,
                bottom,
                right,
                left,
                back,
                top }, Allocator.Persistent)).GetUnsafePtr();
        this.ppRotationFaceDirections[(int)CubeRotationType.Left270] = (int3*)(this.rotationFaceDirectionArray[(int)CubeRotationType.Left270] = new NativeArray<int3>(
            new int3[] {
                left,
                right,
                top,
                forward,
                right,
                left,
                bottom,
                back }, Allocator.Persistent)).GetUnsafePtr();
        #endregion
        #endregion
    }

    protected override void OnDestroy()
    {
        for (int cubeRotationIndex = 0; cubeRotationIndex < (int)CubeRotationType.Max; cubeRotationIndex++)
        {
            this.rotationFaceDirectionArray[cubeRotationIndex].Dispose();
        }
        UnsafeUtility.Free(this.ppRotationFaceDirections, Allocator.Persistent);

こちらのポインターポインターを参照することで、キューブの回転タイプから正しい方向の周囲のキューブ情報の取得が行えることが確認できました。

頂点情報を作成するコードをリファクタリングして、同じようなコードをまとめた結果がこちら

    /// <summary>
    /// 頂点バッファに頂点データをコピー書き込み
    /// </summary>
    unsafe struct CopyWriteVerticesJob : IJobParallelFor
    {
        [NativeDisableUnsafePtrRestriction] [ReadOnly] internal int3** ppRotationFaceDirection;
        [NativeDisableUnsafePtrRestriction] [ReadOnly] internal float* pVerticesSource;
        [NativeDisableUnsafePtrRestriction] [ReadOnly] internal float* pUVSource;
        [ReadOnly] internal int sourceCount;
        internal NativeArray<ChunkMeshData> meshDataArray;

        [BurstCompile]
        public void Execute(int entityIndex)
        {
            var meshData = this.meshDataArray[entityIndex];
            this.verticesOffset = 0;
            this.uvOffset = 0;
            for (var x = 0; x < ChunkManager.ChunkSizeX; x++)
            {
                for (var z = 0; z < ChunkManager.ChunkSizeZ; z++)
                {
                    for (var y = 0; y < ChunkManager.ChunkSizeY; y++)
                    {
                        var pData = GetDataPtr(meshData, x, y, z);
                        if (1 == pData[(int)ChunkDataType.Category])
                        {
                            var rotationType = (CubeRotationType)pData[(int)ChunkDataType.Rotation];

                            this.sourceOffset = 0;
                            this.MakeFace(meshData, x, y, z, rotationType, CubeFaceType.TopA);
                            this.MakeFace(meshData, x, y, z, rotationType, CubeFaceType.BottomA);
                            this.MakeFace(meshData, x, y, z, rotationType, CubeFaceType.RightA);
                            this.MakeFace(meshData, x, y, z, rotationType, CubeFaceType.ForwardA);
                            this.MakeFace(meshData, x, y, z, rotationType, CubeFaceType.CrossA);

                            this.MakeFace(meshData, x, y, z, rotationType, CubeFaceType.BottomB);
                            this.MakeFace(meshData, x, y, z, rotationType, CubeFaceType.TopB);
                            this.MakeFace(meshData, x, y, z, rotationType, CubeFaceType.LeftB);
                            this.MakeFace(meshData, x, y, z, rotationType, CubeFaceType.BackB);
                            this.MakeFace(meshData, x, y, z, rotationType, CubeFaceType.CrossB);
                        }
                    }
                }
            }
        }

        private int MakeFace(ChunkMeshData meshData, int x, int y, int z,
            CubeRotationType rotationType, CubeFaceType faceType)
        {
            int faceCount = 0;
            int dataSideIndex = -1;
            int crossFlag = 0;
            #region 面タイプごとの変数を決定
            switch (faceType)
            {
                case CubeFaceType.TopA:                    
                case CubeFaceType.BottomA:
                    {
                        dataSideIndex = (int)ChunkDataType.SideA;
                        faceCount = 1;
                    }
                    break;
                case CubeFaceType.BottomB:
                case CubeFaceType.TopB:
                    {
                        dataSideIndex = (int)ChunkDataType.SideB;
                        faceCount = 1;
                    }
                    break;
                case CubeFaceType.RightA:
                case CubeFaceType.ForwardA:
                    {
                        dataSideIndex = (int)ChunkDataType.SideA;
                        faceCount = 2;
                    }
                    break;
                case CubeFaceType.LeftB:
                case CubeFaceType.BackB:
                    {
                        dataSideIndex = (int)ChunkDataType.SideB;
                        faceCount = 2;
                    }
                    break;
                case CubeFaceType.CrossA:
                    {
                        dataSideIndex = (int)ChunkDataType.SideA;
                        faceCount = 2;
                        crossFlag = 1;
                    }
                    break;
                case CubeFaceType.CrossB:
                    {
                        dataSideIndex = (int)ChunkDataType.SideB;
                        faceCount = 2;
                        crossFlag = 1;
                    }
                    break;
                default:
                    break;
            }
            #endregion

            var vertexCount = 0;
            #region 周囲チャンクデータをチェックして、面作成が必要なときのみコピー
            var sideCubeOffset = crossFlag == 0 ? this.ppRotationFaceDirection[(int)rotationType][(int)faceType] : int3(0, 0, 0);
            var pData = GetDataPtr(meshData, x + sideCubeOffset.x, y + sideCubeOffset.y, z + sideCubeOffset.z);
            if (!(null != pData && 1 == pData[(int)ChunkDataType.Category] && 1 == pData[dataSideIndex]))
            {
                UnsafeUtility.MemCpy(meshData.pVerticesVector3 + this.verticesOffset, this.pVerticesSource + this.sourceOffset * 9, size: 9 * sizeof(float) * faceCount);
                UnsafeUtility.MemCpy(meshData.pUVVector2 + this.uvOffset, this.pUVSource + this.sourceOffset * 6, size: 6 * sizeof(float) * faceCount);
                vertexCount = faceCount * 3;
            }
            this.sourceOffset += faceCount;
            #endregion

            #region コピーしたものを回転タイプとチャンク内インデックスから、正しい頂点位置へ移動
            for (int vertexIndex = 0; vertexIndex < vertexCount * 3; vertexIndex += 3)
            {
                // コピーした頂点情報を取得
                var pPosition = meshData.pVerticesVector3 + this.verticesOffset + vertexIndex;
                // 回転タイプによる回転
                var quaternion = GetRotationQuaternion(rotationType);
                var position = float3(pPosition[0], pPosition[1], pPosition[2]);
                var resultPosition = rotate(quaternion, position);
                // チャンク内オフセットを足して格納
                pPosition[0] = resultPosition.x + x * ChunkManager.CubeSide;
                pPosition[1] = resultPosition.y + y * ChunkManager.CubeSide;
                pPosition[2] = resultPosition.z + z * ChunkManager.CubeSide;
            }
            this.verticesOffset += vertexCount * 3;
            #endregion

            #region UV 設定
            int row = 10; // データから決めるべき
            int col = 0;
            const float mtf = 232f / 4096f;
            float mtoffu = col / 4.0f;
            float mtoffv = mtf * (16 - row % 17) % 17 + 0.0371f;
            for (int vertexIndex = 0; vertexIndex < vertexCount * 2; vertexIndex += 2)
            {
                var pUV = meshData.pUVVector2 + this.uvOffset + vertexIndex;
                pUV[0] = pUV[0] * mtf + mtoffu;
                pUV[1] = pUV[1] * mtf + mtoffv;
            }
            this.uvOffset += vertexCount * 2;
            #endregion

            return vertexCount;
        }

        int sourceOffset;
        int verticesOffset;
        int uvOffset;
    }

このジョブの関数の面を描くべきかどうかの判定は再利用して頂点カウントのジョブ側でも使いたいところ
切り出し方を考えてみると

具体的には vertexCount を計算する部分を共通化できるといい

できた。

言葉にし難いけど、System は以下の通り、だいぶコンパクトにたたむことができた

using System.Collections.Generic;
using Unity.Burst;
using Unity.Collections;
using Unity.Collections.LowLevel.Unsafe;
using Unity.Entities;
using Unity.Jobs;
using Unity.Mathematics;
using UnityEngine;
using static Unity.Mathematics.math;

internal unsafe class CreateMeshSystem : ComponentSystem
{
    /// <summary>
    /// チャンク情報から頂点数をカウント
    /// </summary>
    unsafe struct CountVerticesJob : IJobParallelFor
    {
        [NativeDisableUnsafePtrRestriction] [ReadOnly] internal int3** ppRotationFaceDirection;
        internal NativeArray<ChunkMeshData> meshDataArray;
        internal int sourceCount;

        [BurstCompile]
        public void Execute(int entityIndex)
        {
            var meshData = this.meshDataArray[entityIndex];
            meshData.vertexCount = 0;
            for (var x = 0; x < ChunkManager.ChunkSizeX; x++)
            {
                for (var z = 0; z < ChunkManager.ChunkSizeZ; z++)
                {
                    for (var y = 0; y < ChunkManager.ChunkSizeY; y++)
                    {
                        var pData = GetDataPtr(meshData, x, y, z);
                        if (1 == pData[(int)ChunkDataType.Category]) // @debug 1 とは?
                        {
                            var rotationType = (CubeRotationType)pData[(int)ChunkDataType.Rotation];
                            meshData.vertexCount += CountFace(meshData, x, y, z, rotationType, this.ppRotationFaceDirection, CubeFaceType.TopA).activeVertexCount;
                            meshData.vertexCount += CountFace(meshData, x, y, z, rotationType, this.ppRotationFaceDirection, CubeFaceType.BottomA).activeVertexCount;
                            meshData.vertexCount += CountFace(meshData, x, y, z, rotationType, this.ppRotationFaceDirection, CubeFaceType.RightA).activeVertexCount;
                            meshData.vertexCount += CountFace(meshData, x, y, z, rotationType, this.ppRotationFaceDirection, CubeFaceType.ForwardA).activeVertexCount;
                            meshData.vertexCount += CountFace(meshData, x, y, z, rotationType, this.ppRotationFaceDirection, CubeFaceType.CrossA).activeVertexCount;
                            meshData.vertexCount += CountFace(meshData, x, y, z, rotationType, this.ppRotationFaceDirection, CubeFaceType.BottomB).activeVertexCount;
                            meshData.vertexCount += CountFace(meshData, x, y, z, rotationType, this.ppRotationFaceDirection, CubeFaceType.TopB).activeVertexCount;
                            meshData.vertexCount += CountFace(meshData, x, y, z, rotationType, this.ppRotationFaceDirection, CubeFaceType.LeftB).activeVertexCount;
                            meshData.vertexCount += CountFace(meshData, x, y, z, rotationType, this.ppRotationFaceDirection, CubeFaceType.BackB).activeVertexCount;
                            meshData.vertexCount += CountFace(meshData, x, y, z, rotationType, this.ppRotationFaceDirection, CubeFaceType.CrossB).activeVertexCount;
                        }
                    }
                }
            }
            this.meshDataArray[entityIndex] = meshData;
        }
    }

    /// <summary>
    /// 頂点バッファに頂点データをコピー書き込み
    /// </summary>
    unsafe struct CopyWriteVerticesJob : IJobParallelFor
    {
        [NativeDisableUnsafePtrRestriction] [ReadOnly] internal int3** ppRotationFaceDirection;
        [NativeDisableUnsafePtrRestriction] [ReadOnly] internal float* pVerticesSource;
        [NativeDisableUnsafePtrRestriction] [ReadOnly] internal float* pUVSource;
        internal NativeArray<ChunkMeshData> meshDataArray;

        [BurstCompile]
        public void Execute(int entityIndex)
        {
            var meshData = this.meshDataArray[entityIndex];
            this.verticesOffset = 0;
            this.uvOffset = 0;
            for (var x = 0; x < ChunkManager.ChunkSizeX; x++)
            {
                for (var z = 0; z < ChunkManager.ChunkSizeZ; z++)
                {
                    for (var y = 0; y < ChunkManager.ChunkSizeY; y++)
                    {
                        var pData = GetDataPtr(meshData, x, y, z);
                        if (1 == pData[(int)ChunkDataType.Category]) // @debug 1 とは?
                        {
                            var rotationType = (CubeRotationType)pData[(int)ChunkDataType.Rotation];

                            this.sourceFaceOffset = 0;
                            this.CalculateMeshData(meshData, x, y, z, rotationType, CountFace(meshData, x, y, z, rotationType, this.ppRotationFaceDirection, CubeFaceType.TopA));
                            this.CalculateMeshData(meshData, x, y, z, rotationType, CountFace(meshData, x, y, z, rotationType, this.ppRotationFaceDirection, CubeFaceType.BottomA));
                            this.CalculateMeshData(meshData, x, y, z, rotationType, CountFace(meshData, x, y, z, rotationType, this.ppRotationFaceDirection, CubeFaceType.RightA));
                            this.CalculateMeshData(meshData, x, y, z, rotationType, CountFace(meshData, x, y, z, rotationType, this.ppRotationFaceDirection, CubeFaceType.ForwardA));
                            this.CalculateMeshData(meshData, x, y, z, rotationType, CountFace(meshData, x, y, z, rotationType, this.ppRotationFaceDirection, CubeFaceType.CrossA));
                            this.CalculateMeshData(meshData, x, y, z, rotationType, CountFace(meshData, x, y, z, rotationType, this.ppRotationFaceDirection, CubeFaceType.BottomB));
                            this.CalculateMeshData(meshData, x, y, z, rotationType, CountFace(meshData, x, y, z, rotationType, this.ppRotationFaceDirection, CubeFaceType.TopB));
                            this.CalculateMeshData(meshData, x, y, z, rotationType, CountFace(meshData, x, y, z, rotationType, this.ppRotationFaceDirection, CubeFaceType.LeftB));
                            this.CalculateMeshData(meshData, x, y, z, rotationType, CountFace(meshData, x, y, z, rotationType, this.ppRotationFaceDirection, CubeFaceType.BackB));
                            this.CalculateMeshData(meshData, x, y, z, rotationType, CountFace(meshData, x, y, z, rotationType, this.ppRotationFaceDirection, CubeFaceType.CrossB));
                        }
                    }
                }
            }
        }

        /// <summary>
        /// メッシュ情報をコピー&計算
        /// </summary>
        private void CalculateMeshData(ChunkMeshData meshData, int x, int y, int z, CubeRotationType rotationType, CountData countData)
        {
            if (0 < countData.activeVertexCount)
            {
                // 作成対象の頂点データをコピー
                UnsafeUtility.MemCpy(meshData.pVerticesVector3 + this.verticesOffset, this.pVerticesSource + 9 * this.sourceFaceOffset, size: 3 * sizeof(float) * countData.activeVertexCount);
                UnsafeUtility.MemCpy(meshData.pUVVector2 + this.uvOffset, this.pUVSource + 6 * this.sourceFaceOffset, size: 2 * sizeof(float) * countData.activeVertexCount);

                #region コピーしたものを回転タイプとチャンク内インデックスから、正しい頂点位置へ移動
                for (int vertexIndex = 0; vertexIndex < countData.activeVertexCount * 3; vertexIndex += 3)
                {
                    // コピーした頂点情報を取得
                    var pPosition = meshData.pVerticesVector3 + this.verticesOffset + vertexIndex;
                    // 回転タイプによる回転
                    var quaternion = GetRotationQuaternion(rotationType);
                    var position = float3(pPosition[0], pPosition[1], pPosition[2]);
                    var resultPosition = rotate(quaternion, position);
                    // チャンク内オフセットを足して格納
                    pPosition[0] = resultPosition.x + x * ChunkManager.CubeSide;
                    pPosition[1] = resultPosition.y + y * ChunkManager.CubeSide;
                    pPosition[2] = resultPosition.z + z * ChunkManager.CubeSide;
                }
                this.verticesOffset += countData.activeVertexCount * 3;
                #endregion

                #region UV 設定
                int row = 10; // @debug データから決めるべき あと変数名キモ
                int col = 0;
                const float mtf = 232f / 4096f;
                float mtoffu = col / 4.0f;
                float mtoffv = mtf * (16 - row % 17) % 17 + 0.0371f;
                for (int vertexIndex = 0; vertexIndex < countData.activeVertexCount * 2; vertexIndex += 2)
                {
                    var pUV = meshData.pUVVector2 + this.uvOffset + vertexIndex;
                    pUV[0] = pUV[0] * mtf + mtoffu;
                    pUV[1] = pUV[1] * mtf + mtoffv;
                }
                this.uvOffset += countData.activeVertexCount * 2;
                #endregion
            }
            // 作成しなくても面タイプごとの面数オフセットを加算
            this.sourceFaceOffset += countData.reservedFaceOffset;
        }

        int sourceFaceOffset;
        int verticesOffset;
        int uvOffset;
    }

    /// <summary>
    /// 回転タイプと面タイプとチャンク内の座標から、作成するべき頂点数をカウント
    /// </summary>
    static CountData CountFace(ChunkMeshData meshData, int x, int y, int z,
            CubeRotationType rotationType, int3** ppRotationFaceDirection, CubeFaceType faceType)
    {
        int faceCount = 0;
        int dataSideIndex = -1;
        int crossFlag = 0;
        #region 面タイプごとの変数を決定
        switch (faceType)
        {
            case CubeFaceType.TopA:
            case CubeFaceType.BottomA:
                {
                    dataSideIndex = (int)ChunkDataType.SideA;
                    faceCount = 1;
                }
                break;
            case CubeFaceType.BottomB:
            case CubeFaceType.TopB:
                {
                    dataSideIndex = (int)ChunkDataType.SideB;
                    faceCount = 1;
                }
                break;
            case CubeFaceType.RightA:
            case CubeFaceType.ForwardA:
                {
                    dataSideIndex = (int)ChunkDataType.SideA;
                    faceCount = 2;
                }
                break;
            case CubeFaceType.LeftB:
            case CubeFaceType.BackB:
                {
                    dataSideIndex = (int)ChunkDataType.SideB;
                    faceCount = 2;
                }
                break;
            case CubeFaceType.CrossA:
                {
                    dataSideIndex = (int)ChunkDataType.SideB;
                    faceCount = 2;
                    crossFlag = 1;
                }
                break;
            case CubeFaceType.CrossB:
                {
                    dataSideIndex = (int)ChunkDataType.SideA;
                    faceCount = 2;
                    crossFlag = 1;
                }
                break;
            default:
                break;
        }
        #endregion

        #region 周囲チャンクデータをチェックして、面作成が必要なときのみコピー
        var vertexCount = 0;
        var sideCubeOffset = crossFlag == 0 ? ppRotationFaceDirection[(int)rotationType][(int)faceType] : int3(0, 0, 0);
        var pData = GetDataPtr(meshData, x + sideCubeOffset.x, y + sideCubeOffset.y, z + sideCubeOffset.z);
        if (!(null != pData && 1 == pData[(int)ChunkDataType.Category] && 1 == pData[dataSideIndex])) // @debug 1 とは? 相手の回転も見ないと駄目なのでは?
        {
            vertexCount = faceCount * 3;
        }
        #endregion
        return new CountData { activeVertexCount = vertexCount, reservedFaceOffset = faceCount };
    }

    static byte* GetDataPtr(ChunkMeshData meshData, int x, int y, int z)
    {
        if (0 <= x && ChunkManager.ChunkSizeX > x
            && 0 <= y && ChunkManager.ChunkSizeY > y
            && 0 <= z && ChunkManager.ChunkSizeZ > z)
        {
            #region チャンク内のデータを返す
            var byteMax = (byte.MaxValue + 1);
            int chunkIndex = meshData.chunkKeyX * byteMax * byteMax + meshData.chunkKeyZ * byteMax + meshData.chunkKeyY;
            var pChunkData = meshData.ppChunkData[chunkIndex];
            var dataIndex = x * ChunkManager.ChunkSizeZ * ChunkManager.ChunkSizeY + z * ChunkManager.ChunkSizeY + y;
            return (byte*)(pChunkData + dataIndex);
            #endregion
        }
        else
        {
            #region チャンク越えカウント
            var overChunkCountX = x / ChunkManager.ChunkSizeX;
            if (0 > x) overChunkCountX -= 1;
            var overChunkCountY = y / ChunkManager.ChunkSizeY;
            if (0 > y) overChunkCountY -= 1;
            var overChunkCountZ = z / ChunkManager.ChunkSizeZ;
            if (0 > z) overChunkCountZ -= 1;
            #endregion
            #region byte オーバーフローによる値ループ
            var chunkKeyX = (byte)(meshData.chunkKeyX + overChunkCountX);
            var chunkKeyY = (byte)(meshData.chunkKeyY + overChunkCountY);
            var chunkKeyZ = (byte)(meshData.chunkKeyZ + overChunkCountZ);
            #endregion
            #region チャンクの特定
            var byteMax = (byte.MaxValue + 1);
            int chunkIndex = chunkKeyX * byteMax * byteMax + chunkKeyZ * byteMax + chunkKeyY;
            var pChunkData = meshData.ppChunkData[chunkIndex];
            #endregion
            if (null != pChunkData)
            {
                #region チャンク内のデータインデックスへ変換
                x -= overChunkCountX * ChunkManager.ChunkSizeX;
                y -= overChunkCountY * ChunkManager.ChunkSizeY;
                z -= overChunkCountZ * ChunkManager.ChunkSizeZ;
                var dataIndex = x * ChunkManager.ChunkSizeZ * ChunkManager.ChunkSizeY + z * ChunkManager.ChunkSizeY + y;
                #endregion
                return (byte*)(pChunkData + dataIndex);
            }

        }
        return null;
    }

   /// <summary>
    /// byte の回転タイプから Quaternion を作って返す
    /// </summary>
    static Unity.Mathematics.quaternion GetRotationQuaternion(CubeRotationType rotationType)
    {
        // @TODO: インデックスアクセスに
        var rotationQuaternion = Unity.Mathematics.quaternion.identity;
        switch (rotationType)
        {
            case CubeRotationType.Top000:
                break;
            case CubeRotationType.Top090:
                rotationQuaternion = Unity.Mathematics.quaternion.RotateY(PI / 2);
                break;
            case CubeRotationType.Top180:
                rotationQuaternion = Unity.Mathematics.quaternion.RotateY(PI);
                break;
            case CubeRotationType.Top270:
                rotationQuaternion = Unity.Mathematics.quaternion.RotateY(PI * 3 / 2);
                break;
            case CubeRotationType.Bottom000:
                rotationQuaternion = Unity.Mathematics.quaternion.RotateX(PI);
                break;
            case CubeRotationType.Bottom090:
                rotationQuaternion = mul(Unity.Mathematics.quaternion.RotateX(PI), Unity.Mathematics.quaternion.RotateY(PI / 2));
                break;
            case CubeRotationType.Bottom180:
                rotationQuaternion = mul(Unity.Mathematics.quaternion.RotateX(PI), Unity.Mathematics.quaternion.RotateY(PI));
                break;
            case CubeRotationType.Bottom270:
                rotationQuaternion = mul(Unity.Mathematics.quaternion.RotateX(PI), Unity.Mathematics.quaternion.RotateY(PI * 3 / 2));
                break;
            case CubeRotationType.Forward000:
                rotationQuaternion = mul(Unity.Mathematics.quaternion.RotateX(PI / 2), Unity.Mathematics.quaternion.RotateY(PI));
                break;
            case CubeRotationType.Forward090:
                rotationQuaternion = mul(Unity.Mathematics.quaternion.RotateX(PI / 2), Unity.Mathematics.quaternion.RotateY(PI * 3 / 2));
                break;
            case CubeRotationType.Forward180:
                rotationQuaternion = mul(Unity.Mathematics.quaternion.RotateX(PI / 2), Unity.Mathematics.quaternion.RotateY(0));
                break;
            case CubeRotationType.Forward270:
                rotationQuaternion = mul(Unity.Mathematics.quaternion.RotateX(PI / 2), Unity.Mathematics.quaternion.RotateY(PI / 2));
                break;
            case CubeRotationType.Back000:
                rotationQuaternion = Unity.Mathematics.quaternion.RotateX(-PI / 2);
                break;
            case CubeRotationType.Back090:
                rotationQuaternion = mul(Unity.Mathematics.quaternion.RotateX(-PI / 2), Unity.Mathematics.quaternion.RotateY(PI / 2));
                break;
            case CubeRotationType.Back180:
                rotationQuaternion = mul(Unity.Mathematics.quaternion.RotateX(-PI / 2), Unity.Mathematics.quaternion.RotateY(PI));
                break;
            case CubeRotationType.Back270:
                rotationQuaternion = mul(Unity.Mathematics.quaternion.RotateX(-PI / 2), Unity.Mathematics.quaternion.RotateY(PI * 3 / 2));
                break;
            case CubeRotationType.Right000:
                rotationQuaternion = mul(Unity.Mathematics.quaternion.RotateZ(-PI / 2), Unity.Mathematics.quaternion.RotateY(PI * 3 / 2));
                break;
            case CubeRotationType.Right090:
                rotationQuaternion = mul(Unity.Mathematics.quaternion.RotateZ(-PI / 2), Unity.Mathematics.quaternion.RotateY(0));
                break;
            case CubeRotationType.Right180:
                rotationQuaternion = mul(Unity.Mathematics.quaternion.RotateZ(-PI / 2), Unity.Mathematics.quaternion.RotateY(PI / 2));
                break;
            case CubeRotationType.Right270:
                rotationQuaternion = mul(Unity.Mathematics.quaternion.RotateZ(-PI / 2), Unity.Mathematics.quaternion.RotateY(PI));
                break;
            case CubeRotationType.Left000:
                rotationQuaternion = mul(Unity.Mathematics.quaternion.RotateZ(PI / 2), Unity.Mathematics.quaternion.RotateY(PI / 2));
                break;
            case CubeRotationType.Left090:
                rotationQuaternion = mul(Unity.Mathematics.quaternion.RotateZ(PI / 2), Unity.Mathematics.quaternion.RotateY(PI));
                break;
            case CubeRotationType.Left180:
                rotationQuaternion = mul(Unity.Mathematics.quaternion.RotateZ(PI / 2), Unity.Mathematics.quaternion.RotateY(PI * 3 / 2));
                break;
            case CubeRotationType.Left270:
                rotationQuaternion = Unity.Mathematics.quaternion.RotateZ(PI / 2);
                break;
            case CubeRotationType.Max:
                break;
            default:
                break;
        }
        return rotationQuaternion;
    }

    protected override unsafe void OnCreate()
    {
        base.OnCreate();

        this.query = GetEntityQuery(new EntityQueryDesc
        {
            All = new[] { ComponentType.ReadOnly<CreateMeshMarker>(),
                ComponentType.ReadWrite<ChunkMeshData>() },
        });

        #region モデルの形状を定義→NativeArray確保
        // 省略
        #endregion
    }

    protected override void OnDestroy()
    {
        for (int cubeRotationIndex = 0; cubeRotationIndex < (int)CubeRotationType.Max; cubeRotationIndex++)
        {
            this.rotationFaceDirectionArray[cubeRotationIndex].Dispose();
        }
        UnsafeUtility.Free(this.ppRotationFaceDirection, Allocator.Persistent);

        this.nativeUVSource.Dispose();
        this.nativeVerticesSource.Dispose();

        base.OnDestroy();
    }

    protected unsafe override void OnUpdate()
    {
        #region NativeArray 確保
        var entities = this.query.ToEntityArray(Allocator.TempJob);
        var meshDataArray = this.query.ToComponentDataArray<ChunkMeshData>(Allocator.TempJob);
        #endregion

        #region メッシュの頂点数をカウント
        var countVerticesJob = new CountVerticesJob
        {
            ppRotationFaceDirection = this.ppRotationFaceDirection,
            sourceCount = this.nativeVerticesSource.Length,
            meshDataArray = meshDataArray
        };
        var countJobHandle = countVerticesJob.Schedule(arrayLength: meshDataArray.Length, innerloopBatchCount: 1);
        countJobHandle.Complete();
        #endregion

        #region カウント数→頂点バッファを確保→バッファポインタを ComponentData に代入
        this.entityMeshDataList.Clear();
        for (int entityIndex = 0; entityIndex < entities.Length; entityIndex++)
        {
            var meshData = meshDataArray[entityIndex];
            var vertexCount = meshData.vertexCount;
            var entityMeshData = new EntityMeshData {
                nativeVertices = new NativeArray<Vector3>(vertexCount, Allocator.TempJob),
                nativeUV = new NativeArray<Vector2>(vertexCount, Allocator.TempJob),
            };
            this.entityMeshDataList.Add(entityMeshData);
            meshData.pVerticesVector3 = (float*)entityMeshData.nativeVertices.GetUnsafePtr();
            meshData.pUVVector2 = (float*)entityMeshData.nativeUV.GetUnsafePtr();
            meshDataArray[entityIndex] = meshData;
        }
        #endregion

        #region 頂点バッファに頂点データをコピー
        var copyVerticesJob = new CopyWriteVerticesJob
        {
            ppRotationFaceDirection = this.ppRotationFaceDirection,
            pVerticesSource = this.pVerticesSource,
            pUVSource = this.pUVSource,
            meshDataArray = meshDataArray
        };
        var copyJobHandle = copyVerticesJob.Schedule(arrayLength: meshDataArray.Length, innerloopBatchCount: 1);
        copyJobHandle.Complete();
        #endregion

        #region 頂点バッファ→マネージド配列→メッシュ作成→メッシュ法線・接線の計算
        for (int entityIndex = 0; entityIndex < entities.Length; entityIndex++)
        {
            var entity = entities[entityIndex];
            var entityMeshData = this.entityMeshDataList[entityIndex];
            var vertices = entityMeshData.nativeVertices.ToArray();
            var uv = entityMeshData.nativeUV.ToArray();

            var mesh = new Mesh();
            mesh.Clear();
            mesh.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32;
            mesh.vertices = vertices;
            mesh.uv = uv;
            int[] triangles = new int[vertices.Length];
            for (int vertexIndex = 0; vertexIndex < vertices.Length; vertexIndex++)
            {
                triangles[vertexIndex] = vertexIndex;
            }
            mesh.SetIndices(triangles, MeshTopology.Triangles, submesh: 0, calculateBounds: true);
            mesh.RecalculateNormals();
            mesh.RecalculateTangents();
            var meshFilter = EntityManager.GetComponentObject<MeshFilter>(entity);
            meshFilter.mesh = mesh;

            entityMeshData.nativeUV.Dispose();
            entityMeshData.nativeVertices.Dispose();
        }
        this.entityMeshDataList.Clear();
        #endregion

        #region entity から marker の除去
        for (int entityIndex = 0; entityIndex < entities.Length; entityIndex++)
        {
            var entity = entities[entityIndex];
            EntityManager.RemoveComponent(entity, ComponentType.ReadOnly<CreateMeshMarker>());
        }
        #endregion

        #region NativeArray 開放
        meshDataArray.Dispose();
        entities.Dispose();
        #endregion
    }

    EntityQuery query;
    
    NativeArray<float> nativeVerticesSource;
    float* pVerticesSource;
    NativeArray<float> nativeUVSource;
    float* pUVSource;

    List<EntityMeshData> entityMeshDataList = new List<EntityMeshData>();

    NativeArray<int3>[] rotationFaceDirectionArray = new NativeArray<int3>[(int)CubeRotationType.Max];
    int3** ppRotationFaceDirection;


    class EntityMeshData
    {
        public NativeArray<Vector3> nativeVertices;
        public NativeArray<Vector2> nativeUV;
    }

    struct CountData
    {
        public int activeVertexCount;
        public int reservedFaceOffset;
    }
}

internal enum ChunkDataType : int
{
    Category = 0,
    Rotation,
    SideA,
    SideB,
    Max
}

internal enum CubeFaceType : byte
{
    TopA = 0,
    BottomA,
    RightA,
    ForwardA,
    BottomB,
    TopB,
    LeftB,
    BackB,
    CrossA,
    CrossB,
    Max,
}

internal enum CubeRotationType : byte
{
    Top000 = 0,
    Top090,
    Top180,
    Top270,

    Bottom000,
    Bottom090,
    Bottom180,
    Bottom270,

    Forward000,
    Forward090,
    Forward180,
    Forward270,

    Back000,
    Back090,
    Back180,
    Back270,

    Right000,
    Right090,
    Right180,
    Right270,

    Left000,
    Left090,
    Left180,
    Left270,

    Max
}

## 動作確認

回転対応ができたので random に CubeRotationType を ChunkData に与えて、結果のメッシュを確認します。
余計な面が大きなキューブの内側に入っていなければ OK

結果はどうか?

残念 回転タイプ 0~7 までは問題なかったが
8から問題あり

11 まで修正(180度回転ずらせばよかった)

15 まではすでに正しかった。
16は、こちらも不具合あったが 180度回転ずらして修正を確認

19までOK
20から、すでに正しかった。

これでもう一度ランダムに回転を与えて結果を確認します。

f:id:simplestar_tech:20190616223015p:plain
正しく設定できました。

上記コードの方、正しいものに修正しておきました。