Пример #1
0
        static void QuickSort( int[] keys, ChunkInfo[] values, int left, int right )
        {
            while( left < right ) {
                int i = left, j = right;
                int pivot = keys[(i + j) / 2];
                // partition the list
                while( i <= j ) {
                    while( pivot > keys[i] ) i++;
                    while( pivot < keys[j] ) j--;

                    if( i <= j ) {
                        int key = keys[i]; keys[i] = keys[j]; keys[j] = key;
                        ChunkInfo value = values[i]; values[i] = values[j]; values[j] = value;
                        i++; j--;
                    }
                }

                // recurse into the smaller subset
                if( j - left <= right - i ) {
                    if( left < j )
                        QuickSort( keys, values, left, j );
                    left = i;
                } else {
                    if( i < right )
                        QuickSort( keys, values, i, right );
                    right = j;
                }
            }
        }
        int UpdateChunksAndVisibility(double deltaTime, ref int chunkUpdats, int adjViewDistSqr)
        {
            ChunkInfo[] chunks = renderer.chunks, render = renderer.renderChunks;
            int         j      = 0;

            for (int i = 0; i < chunks.Length; i++)
            {
                ChunkInfo info = chunks[i];
                if (info.Empty)
                {
                    continue;
                }
                int  distSqr = distances[i];
                bool noData  = info.NormalParts == null && info.TranslucentParts == null;

                bool inRange = distSqr <= adjViewDistSqr;
                if (!noData && distSqr >= adjViewDistSqr + 32 * 16)
                {
                    DeleteChunk(info, true); continue;
                }

                if (noData && inRange && chunkUpdats < chunksTarget)
                {
                    BuildChunk(info, ref chunkUpdats);
                }
                info.Visible = inRange &&
                               game.Culling.SphereInFrustum(info.CentreX, info.CentreY, info.CentreZ, 14);            // 14 ~ sqrt(3 * 8^2)
                if (info.Visible && !info.Empty)
                {
                    render[j] = info; j++;
                }
            }
            return(j);
        }
        void DrawTranslucentPart(ChunkInfo info, ref ChunkPartInfo part, int m)
        {
            gfx.BindVb(part.VbId);
            bool drawLeft   = (inTranslucent || info.DrawLeft) && part.LeftCount > 0;
            bool drawRight  = (inTranslucent || info.DrawRight) && part.RightCount > 0;
            bool drawBottom = (inTranslucent || info.DrawBottom) && part.BottomCount > 0;
            bool drawTop    = (inTranslucent || info.DrawTop) && part.TopCount > 0;
            bool drawFront  = (inTranslucent || info.DrawFront) && part.FrontCount > 0;
            bool drawBack   = (inTranslucent || info.DrawBack) && part.BackCount > 0;

            if (drawLeft && drawRight)
            {
                gfx.DrawIndexedVb_TrisT2fC4b(part.LeftCount + part.RightCount, part.LeftIndex);
                game.Vertices += m * (part.LeftCount + part.RightCount);
            }
            else if (drawLeft)
            {
                gfx.DrawIndexedVb_TrisT2fC4b(part.LeftCount, part.LeftIndex);
                game.Vertices += m * part.LeftCount;
            }
            else if (drawRight)
            {
                gfx.DrawIndexedVb_TrisT2fC4b(part.RightCount, part.RightIndex);
                game.Vertices += m * part.RightCount;
            }

            if (drawFront && drawBack)
            {
                gfx.DrawIndexedVb_TrisT2fC4b(part.FrontCount + part.BackCount, part.FrontIndex);
                game.Vertices += m * (part.FrontCount + part.BackCount);
            }
            else if (drawFront)
            {
                gfx.DrawIndexedVb_TrisT2fC4b(part.FrontCount, part.FrontIndex);
                game.Vertices += m * part.FrontCount;
            }
            else if (drawBack)
            {
                gfx.DrawIndexedVb_TrisT2fC4b(part.BackCount, part.BackIndex);
                game.Vertices += m * part.BackCount;
            }

            if (drawBottom && drawTop)
            {
                gfx.DrawIndexedVb_TrisT2fC4b(part.BottomCount + part.TopCount, part.BottomIndex);
                game.Vertices += m * (part.BottomCount + part.TopCount);
            }
            else if (drawBottom)
            {
                gfx.DrawIndexedVb_TrisT2fC4b(part.BottomCount, part.BottomIndex);
                game.Vertices += m * part.BottomCount;
            }
            else if (drawTop)
            {
                gfx.DrawIndexedVb_TrisT2fC4b(part.TopCount, part.TopIndex);
                game.Vertices += m * part.TopCount;
            }
        }
        void RenderNormalBatch(int batch)
        {
            for (int i = 0; i < chunks.Length; i++)
            {
                ChunkInfo info = chunks[i];
                                #if OCCLUSION
                if (info.NormalParts == null || !info.Visible || info.Occluded)
                {
                    continue;
                }
                                #else
                if (info.NormalParts == null || !info.Visible)
                {
                    continue;
                }
                                #endif

                ChunkPartInfo part = info.NormalParts[batch];
                if (part.IndicesCount == 0)
                {
                    continue;
                }
                usedNormal[batch] = true;
                if (part.IndicesCount > maxIndices)
                {
                    DrawBigPart(info, ref part);
                }
                else
                {
                    DrawPart(info, ref part);
                }

                if (part.SpriteCount > 0)
                {
                    int groupCount = part.SpriteCount / 4;
                    api.FaceCulling = true;
                    if (info.DrawRight || info.DrawFront)
                    {
                        api.DrawIndexedVb_TrisT2fC4b(groupCount, 0);
                    }
                    if (info.DrawLeft || info.DrawBack)
                    {
                        api.DrawIndexedVb_TrisT2fC4b(groupCount, groupCount);
                    }
                    if (info.DrawLeft || info.DrawFront)
                    {
                        api.DrawIndexedVb_TrisT2fC4b(groupCount, groupCount * 2);
                    }
                    if (info.DrawRight || info.DrawBack)
                    {
                        api.DrawIndexedVb_TrisT2fC4b(groupCount, groupCount * 3);
                    }
                    api.FaceCulling = false;
                }
                game.Vertices += part.IndicesCount;
            }
        }
        void Print(string prefix, int cx, int cy, int cz)
        {
            ChunkInfo chunk = unsortedChunks[cx + chunksX * (cy + cz * chunksY)];

            Console.WriteLine("{0}  D {1}: V {2}, O {3}, {4}", prefix, chunk.DistanceFlags,
                              chunk.OccludedFlags, chunk.OcclusionFlags, chunk.Occluded);
            //	Console.WriteLine(chunk.DistanceFlags + " : " + chunk.OccludedFlags + " : " + chunk.OcclusionFlags + " , " + chunk.Occluded);

            Vector3I p = chunkPos;
            int      x1 = chunk.CentreX - 8, x2 = chunk.CentreX + 8;
            int      y1 = chunk.CentreY - 8, y2 = chunk.CentreY + 8;
            int      z1 = chunk.CentreZ - 8, z2 = chunk.CentreZ + 8;
            int      dx = Math.Max(x1 - p.X, Math.Max(0, p.X - x2));
            int      dy = Math.Max(y1 - p.Y, Math.Max(0, p.Y - y2));
            int      dz = Math.Max(z1 - p.Z, Math.Max(0, p.Z - z2));
            int      distX, distY, distZ;

            // X axis collisions
            int dxLeft = Math.Abs(x1 - p.X), dxRight = Math.Abs(x2 - p.X);

            if (dxLeft < dxRight)
            {
                distX = dxLeft * dxLeft + dy * dy + dz * dz;
            }
            else
            {
                distX = dxRight * dxRight + dy * dy + dz * dz;
            }

            // Z axis collisions
            int dxFront = Math.Abs(z1 - p.Z), dxBack = Math.Abs(z2 - p.Z);

            if (dxFront < dxBack)
            {
                distZ = dx * dx + dy * dy + dxFront * dxFront;
            }
            else
            {
                distZ = dx * dx + dy * dy + dxBack * dxBack;
            }

            // Y axis collisions
            int dxBottom = Math.Abs(y1 - p.Y), dxTop = Math.Abs(y2 - p.Y);

            if (dxBottom < dxTop)
            {
                distY = dx * dx + dxBottom * dxBottom + dz * dz;
            }
            else
            {
                distY = dx * dx + dxTop * dxTop + dz * dz;
            }

            int distMin = Math.Min(distX, Math.Min(distY, distZ));

            Console.WriteLine("    M {0} : X {1}, Y {2}, Z {3} ({4}, {5})", distMin, distX, distY, distZ, dxFront, dxBack);
        }
Пример #6
0
 void DeleteChunk(ChunkInfo info, bool decUsed)
 {
     info.Empty = false;
                 #if OCCLUSION
     info.OcclusionFlags = 0;
     info.OccludedFlags  = 0;
                 #endif
     DeleteData(ref info.NormalParts, decUsed);
     DeleteData(ref info.TranslucentParts, decUsed);
 }
        void DrawPart(ChunkInfo info, ref ChunkPartInfo part)
        {
            api.BindVb(part.VbId);
            bool drawLeft   = info.DrawLeft && part.LeftCount > 0;
            bool drawRight  = info.DrawRight && part.RightCount > 0;
            bool drawBottom = info.DrawBottom && part.BottomCount > 0;
            bool drawTop    = info.DrawTop && part.TopCount > 0;
            bool drawFront  = info.DrawFront && part.FrontCount > 0;
            bool drawBack   = info.DrawBack && part.BackCount > 0;

            if (drawLeft && drawRight)
            {
                api.FaceCulling = true;
                api.DrawIndexedVb_TrisT2fC4b(part.LeftCount + part.RightCount, part.LeftIndex);
                api.FaceCulling = false;
            }
            else if (drawLeft)
            {
                api.DrawIndexedVb_TrisT2fC4b(part.LeftCount, part.LeftIndex);
            }
            else if (drawRight)
            {
                api.DrawIndexedVb_TrisT2fC4b(part.RightCount, part.RightIndex);
            }

            if (drawFront && drawBack)
            {
                api.FaceCulling = true;
                api.DrawIndexedVb_TrisT2fC4b(part.FrontCount + part.BackCount, part.FrontIndex);
                api.FaceCulling = false;
            }
            else if (drawFront)
            {
                api.DrawIndexedVb_TrisT2fC4b(part.FrontCount, part.FrontIndex);
            }
            else if (drawBack)
            {
                api.DrawIndexedVb_TrisT2fC4b(part.BackCount, part.BackIndex);
            }

            if (drawBottom && drawTop)
            {
                api.FaceCulling = true;
                api.DrawIndexedVb_TrisT2fC4b(part.BottomCount + part.TopCount, part.BottomIndex);
                api.FaceCulling = false;
            }
            else if (drawBottom)
            {
                api.DrawIndexedVb_TrisT2fC4b(part.BottomCount, part.BottomIndex);
            }
            else if (drawTop)
            {
                api.DrawIndexedVb_TrisT2fC4b(part.TopCount, part.TopIndex);
            }
        }
            public void Enqueue(ChunkInfo item)
            {
                if (Size == array.Length)
                {
                    throw new InvalidOperationException("Queue limit reached");
                }

                array[tail] = item;
                tail        = (tail + 1) % array.Length;
                Size++;
            }
        void DeleteChunk(ChunkInfo info)
        {
            info.Empty = false; info.AllAir = false;
                        #if OCCLUSION
            info.OcclusionFlags = 0;
            info.OccludedFlags  = 0;
                        #endif

            if (info.NormalParts != null)
            {
                ChunkPartInfo[] parts = info.NormalParts;
                for (int i = 0; i < parts.Length; i++)
                {
                    game.Graphics.DeleteVb(ref parts[i].VbId);
                    if (parts[i].VerticesCount == 0)
                    {
                        continue;
                    }
                    renderer.normalPartsCount[i]--;
                }
                info.NormalParts = null;
            }

            if (info.LiquidParts != null)
            {
                ChunkPartInfo[] parts = info.LiquidParts;
                for (int i = 0; i < parts.Length; i++)
                {
                    game.Graphics.DeleteVb(ref parts[i].VbId);
                    if (parts[i].VerticesCount == 0)
                    {
                        continue;
                    }
                    renderer.liquidPartsCount[i]--;
                }
                info.LiquidParts = null;
            }

            if (info.TranslucentParts != null)
            {
                ChunkPartInfo[] parts = info.TranslucentParts;
                for (int i = 0; i < parts.Length; i++)
                {
                    game.Graphics.DeleteVb(ref parts[i].VbId);
                    if (parts[i].VerticesCount == 0)
                    {
                        continue;
                    }
                    renderer.translucentPartsCount[i]--;
                }
                info.TranslucentParts = null;
            }
        }
 void QueueChunk(int cx, int cy, int cz, ChunkQueue queue)
 {
     if (cx >= 0 && cy >= 0 && cz >= 0 && cx < chunksX && cy < chunksY && cz < chunksZ)
     {
         ChunkInfo info = unsortedChunks[cx + chunksX * (cy + cz * chunksY)];
         if (!info.Visited)
         {
             queue.Enqueue(info);
         }
         info.Visited = true;
     }
 }
Пример #11
0
        public static void UpdateSortOrder(Game game, ChunkUpdater updater)
        {
            Vector3 cameraPos = game.CurrentCameraPos;
            //int adjCamX, adjCamZ;
            //game.Camera.GetCamChunk(out adjCamX, out adjCamZ);
            //cameraPos.X -= adjCamX;
            //cameraPos.Z -= adjCamZ;
            Vector3I newChunkPos = Vector3I.Floor(cameraPos);

            newChunkPos.X = (newChunkPos.X & ~0x0F) + 8;
            newChunkPos.Y = (newChunkPos.Y & ~0x0F) + 8;
            newChunkPos.Z = (newChunkPos.Z & ~0x0F) + 8;
            if (newChunkPos == updater.chunkPos)
            {
                return;
            }

            ChunkInfo[] chunks    = game.MapRenderer.chunks;
            int[]       distances = updater.distances;
            Vector3I    pPos      = newChunkPos;

            updater.chunkPos = pPos;

            for (int i = 0; i < chunks.Length; i++)
            {
                ChunkInfo info = chunks[i];

                // Calculate distance to chunk centre
                int dx = info.CentreX - pPos.X, dy = info.CentreY - pPos.Y, dz = info.CentreZ - pPos.Z;
                distances[i] = dx * dx + dy * dy + dz * dz;

                // Can work out distance to chunk faces as offset from distance to chunk centre on each axis.
                int dXMin = dx - 8, dXMax = dx + 8;
                int dYMin = dy - 8, dYMax = dy + 8;
                int dZMin = dz - 8, dZMax = dz + 8;

                // Back face culling: make sure that the chunk is definitely entirely back facing.
                info.DrawLeft   = !(dXMin <= 0 && dXMax <= 0);
                info.DrawRight  = !(dXMin >= 0 && dXMax >= 0);
                info.DrawFront  = !(dZMin <= 0 && dZMax <= 0);
                info.DrawBack   = !(dZMin >= 0 && dZMax >= 0);
                info.DrawBottom = !(dYMin <= 0 && dYMax <= 0);
                info.DrawTop    = !(dYMin >= 0 && dYMax >= 0);
            }

            // NOTE: Over 5x faster compared to normal comparison of IComparer<ChunkInfo>.Compare
            if (distances.Length > 1)
            {
                QuickSort(distances, chunks, 0, chunks.Length - 1);
            }
            updater.ResetUsedFlags();
            //SimpleOcclusionCulling();
        }
        void SimpleOcclusionCulling()           // TODO: still broken
        {
            Vector3  p      = game.LocalPlayer.EyePosition;
            Vector3I mapLoc = Vector3I.Floor(p);

            Utils.Clamp(ref mapLoc.X, 0, game.World.Width - 1);
            Utils.Clamp(ref mapLoc.Y, 0, game.World.Height - 1);
            Utils.Clamp(ref mapLoc.Z, 0, game.World.Length - 1);

            int cx = mapLoc.X >> 4;
            int cy = mapLoc.Y >> 4;
            int cz = mapLoc.Z >> 4;

            ChunkInfo chunkIn = unsortedChunks[cx + chunksX * (cy + cz * chunksY)];
            byte      chunkInOcclusionFlags = chunkIn.OcclusionFlags;

            chunkIn.OcclusionFlags = 0;
            ChunkQueue queue = new ChunkQueue(chunksX * chunksY * chunksZ);

            for (int i = 0; i < chunks.Length; i++)
            {
                ChunkInfo chunk = chunks[i];
                chunk.Visited       = false;
                chunk.Occluded      = false;
                chunk.OccludedFlags = chunk.OcclusionFlags;
                chunk.DistanceFlags = 0;
            }

            chunkIn.Visited = true;
            mapLoc          = Vector3I.Floor(p);
            if (game.World.IsValidPos(mapLoc))
            {
                chunkIn.DistanceFlags = flagX | flagY | flagZ;
            }
            else
            {
                chunkIn.OccludedFlags  = chunkIn.OcclusionFlags = chunkInOcclusionFlags;
                chunkIn.DistanceFlags |= (mapLoc.X < 0 || mapLoc.X >= game.World.Width) ? flagX : (byte)0;
                chunkIn.DistanceFlags |= (mapLoc.Y < 0 || mapLoc.Y >= game.World.Height) ? flagY : (byte)0;
                chunkIn.DistanceFlags |= (mapLoc.Z < 0 || mapLoc.Z >= game.World.Length) ? flagZ : (byte)0;
            }

            Console.WriteLine("SRC    {0}", cx + "," + cy + "," + cz);
            QueueChunk(cx - 1, cy, cz, queue);
            QueueChunk(cx + 1, cy, cz, queue);
            QueueChunk(cx, cy - 1, cz, queue);
            QueueChunk(cx, cy + 1, cz, queue);
            QueueChunk(cx, cy, cz - 1, queue);
            QueueChunk(cx, cy, cz + 1, queue);
            ProcessQueue(queue);
            chunkIn.OcclusionFlags = chunkInOcclusionFlags;
        }
Пример #13
0
        public static void UpdateSortOrder(Game game, ChunkUpdater updater)
        {
            Vector3  cameraPos   = game.CurrentCameraPos;
            Vector3I newChunkPos = Vector3I.Floor(cameraPos);

            newChunkPos.X = (newChunkPos.X & ~0x0F) + 8;
            newChunkPos.Y = (newChunkPos.Y & ~0x0F) + 8;
            newChunkPos.Z = (newChunkPos.Z & ~0x0F) + 8;
            if (newChunkPos == updater.chunkPos)
            {
                return;
            }

            ChunkInfo[] chunks    = game.MapRenderer.chunks;
            int[]       distances = updater.distances;
            updater.chunkPos = newChunkPos;

            for (int i = 0; i < distances.Length; i++)
            {
                ChunkInfo info = chunks[i];
                distances[i] = Utils.DistanceSquared(info.CentreX, info.CentreY, info.CentreZ,
                                                     updater.chunkPos.X, updater.chunkPos.Y, updater.chunkPos.Z);
            }

            // NOTE: Over 5x faster compared to normal comparison of IComparer<ChunkInfo>.Compare
            if (distances.Length > 1)
            {
                QuickSort(distances, chunks, 0, chunks.Length - 1);
            }

            Vector3I pPos = newChunkPos;

            for (int i = 0; i < chunks.Length; i++)
            {
                ChunkInfo info = chunks[i];
                int       dX1 = (info.CentreX - 8) - pPos.X, dX2 = (info.CentreX + 8) - pPos.X;
                int       dY1 = (info.CentreY - 8) - pPos.Y, dY2 = (info.CentreY + 8) - pPos.Y;
                int       dZ1 = (info.CentreZ - 8) - pPos.Z, dZ2 = (info.CentreZ + 8) - pPos.Z;

                // Back face culling: make sure that the chunk is definitely entirely back facing.
                info.DrawLeft   = !(dX1 <= 0 && dX2 <= 0);
                info.DrawRight  = !(dX1 >= 0 && dX2 >= 0);
                info.DrawFront  = !(dZ1 <= 0 && dZ2 <= 0);
                info.DrawBack   = !(dZ1 >= 0 && dZ2 >= 0);
                info.DrawBottom = !(dY1 <= 0 && dY2 <= 0);
                info.DrawTop    = !(dY1 >= 0 && dY2 >= 0);
            }
            updater.RecalcBooleans(false);
            //SimpleOcclusionCulling();
        }
 void OccludeZ( int cx, int cy, int cz, int zOffset, ChunkInfo info )
 {
     cz += zOffset;
     if( cz >= 0 && cz < chunksZ ) {
         ChunkInfo neighbour = unsortedChunks[cx + chunksX * (cy + cz * chunksY)];
         if( (neighbour.OccludedFlags & neighbour.DistanceFlags) != neighbour.DistanceFlags )
             info.Occluded = false;
         else
             info.OccludedFlags |= flagZ;
     } else {
         info.Occluded = false;
     }
     info.DistanceFlags |= flagZ;
 }
 void OccludeY( int cx, int cy, int cz, int yOffset, ChunkInfo info )
 {
     cy += yOffset;
     if( cy >= 0 && cy < chunksY ) {
         ChunkInfo neighbour = unsortedChunks[cx + chunksX * (cy + cz * chunksY)];
         if( (neighbour.OccludedFlags & neighbour.DistanceFlags) != neighbour.DistanceFlags )
             info.Occluded = false;
         else
             info.OccludedFlags |= flagY;
     } else {
         info.Occluded = false;
     }
     info.DistanceFlags |= flagY;
 }
 void OccludeX( int cx, int cy, int cz, int xOffset, ChunkInfo info )
 {
     cx += xOffset;
     if( cx >= 0 && cx < chunksX ) {
         ChunkInfo neighbour = unsortedChunks[cx + chunksX * (cy + cz * chunksY)];
         if( (neighbour.OccludedFlags & neighbour.DistanceFlags) != neighbour.DistanceFlags )
             info.Occluded = false;
         else
             info.OccludedFlags |= flagX;
     } else {
         info.Occluded = false;
     }
     info.DistanceFlags |= flagX;
 }
        void BuildChunk(ChunkInfo info, ref int chunkUpdates)
        {
            game.ChunkUpdates++;
            chunkUpdates++;
            info.PendingDelete = false;
            builder.MakeChunk(info);

            if (info.NormalParts == null && info.LiquidParts == null && info.TranslucentParts == null)
            {
                info.Empty = true;
                return;
            }

            if (info.NormalParts != null)
            {
                ChunkPartInfo[] parts = info.NormalParts;
                for (int i = 0; i < parts.Length; i++)
                {
                    if (parts[i].VerticesCount == 0)
                    {
                        continue;
                    }
                    renderer.normalPartsCount[i]++;
                }
            }
            if (info.LiquidParts != null)
            {
                ChunkPartInfo[] parts = info.LiquidParts;
                for (int i = 0; i < parts.Length; i++)
                {
                    if (parts[i].VerticesCount == 0)
                    {
                        continue;
                    }
                    renderer.liquidPartsCount[i]++;
                }
            }
            if (info.TranslucentParts != null)
            {
                ChunkPartInfo[] parts = info.TranslucentParts;
                for (int i = 0; i < parts.Length; i++)
                {
                    if (parts[i].VerticesCount == 0)
                    {
                        continue;
                    }
                    renderer.translucentPartsCount[i]++;
                }
            }
        }
            public ChunkInfo Dequeue()
            {
                if (Size == 0)
                {
                    throw new InvalidOperationException("No elements left in queue");
                }

                ChunkInfo item = array[head];

                array[head] = null;
                head        = (head + 1) % array.Length;
                Size--;
                return(item);
            }
        void RenderNormalBatch(int batch)
        {
            for (int i = 0; i < renderCount; i++)
            {
                ChunkInfo info = renderChunks[i];
                if (info.NormalParts == null)
                {
                    continue;
                }

                ChunkPartInfo part = info.NormalParts[batch];
                if (part.IndicesCount == 0)
                {
                    continue;
                }
                usedNormal[batch] = true;
                if (part.IndicesCount > maxIndices)
                {
                    DrawBigPart(info, ref part);
                }
                else
                {
                    DrawPart(info, ref part);
                }

                if (part.SpriteCount > 0)
                {
                    int count = part.SpriteCount / 4;                     // 4 per sprite
                    gfx.FaceCulling = true;
                    if (info.DrawRight || info.DrawFront)
                    {
                        gfx.DrawIndexedVb_TrisT2fC4b(count, 0); game.Vertices += count;
                    }
                    if (info.DrawLeft || info.DrawBack)
                    {
                        gfx.DrawIndexedVb_TrisT2fC4b(count, count); game.Vertices += count;
                    }
                    if (info.DrawLeft || info.DrawFront)
                    {
                        gfx.DrawIndexedVb_TrisT2fC4b(count, count * 2); game.Vertices += count;
                    }
                    if (info.DrawRight || info.DrawBack)
                    {
                        gfx.DrawIndexedVb_TrisT2fC4b(count, count * 3); game.Vertices += count;
                    }
                    gfx.FaceCulling = false;
                }
            }
        }
Пример #20
0
        void DrawTranslucentPart(ChunkInfo info, ref ChunkPartInfo part)
        {
            api.BindVb(part.VbId);
            bool drawLeft   = (drawAllFaces || info.DrawLeft) && part.LeftCount > 0;
            bool drawRight  = (drawAllFaces || info.DrawRight) && part.RightCount > 0;
            bool drawBottom = (drawAllFaces || info.DrawBottom) && part.BottomCount > 0;
            bool drawTop    = (drawAllFaces || info.DrawTop) && part.TopCount > 0;
            bool drawFront  = (drawAllFaces || info.DrawFront) && part.FrontCount > 0;
            bool drawBack   = (drawAllFaces || info.DrawBack) && part.BackCount > 0;

            if (drawLeft && drawRight)
            {
                api.DrawIndexedVb_TrisT2fC4b(part.LeftCount + part.RightCount, part.LeftIndex);
            }
            else if (drawLeft)
            {
                api.DrawIndexedVb_TrisT2fC4b(part.LeftCount, part.LeftIndex);
            }
            else if (drawRight)
            {
                api.DrawIndexedVb_TrisT2fC4b(part.RightCount, part.RightIndex);
            }

            if (drawFront && drawBack)
            {
                api.DrawIndexedVb_TrisT2fC4b(part.FrontCount + part.BackCount, part.FrontIndex);
            }
            else if (drawFront)
            {
                api.DrawIndexedVb_TrisT2fC4b(part.FrontCount, part.FrontIndex);
            }
            else if (drawBack)
            {
                api.DrawIndexedVb_TrisT2fC4b(part.BackCount, part.BackIndex);
            }

            if (drawBottom && drawTop)
            {
                api.DrawIndexedVb_TrisT2fC4b(part.BottomCount + part.TopCount, part.BottomIndex);
            }
            else if (drawBottom)
            {
                api.DrawIndexedVb_TrisT2fC4b(part.BottomCount, part.BottomIndex);
            }
            else if (drawTop)
            {
                api.DrawIndexedVb_TrisT2fC4b(part.TopCount, part.TopIndex);
            }
        }
        public void MarkDeleteChunk(int cx, int cy, int cz)
        {
            if (cx < 0 || cy < 0 || cz < 0 ||
                cx >= chunksX || cy >= chunksY || cz >= chunksZ)
            {
                return;
            }

            ChunkInfo info = unsortedChunks[cx + chunksX * (cy + cz * chunksY)];

            //if (info.AllAir) return; // do not recreate chunks completely air
            info.Empty         = false;
            info.PendingDelete = true;
            //info.TranslucentParts = null;
            //info.NormalParts = null;
            //info.LiquidParts = null;
        }
Пример #22
0
        void BuildChunk(ChunkInfo info, ref int chunkUpdates)
        {
            game.ChunkUpdates++;
            builder.GetDrawInfo(info.CentreX - 8, info.CentreY - 8, info.CentreZ - 8,
                                ref info.NormalParts, ref info.TranslucentParts);

            if (info.NormalParts == null && info.TranslucentParts == null)
            {
                info.Empty = true;
            }
            else
            {
                IncrementUsed(info.NormalParts);
                IncrementUsed(info.TranslucentParts);
            }
            chunkUpdates++;
        }
        /// <summary> Marks the given chunk as needing to be deleted. </summary>
        public void RefreshChunk(int cx, int cy, int cz)
        {
            if (cx < 0 || cy < 0 || cz < 0 ||
                cx >= chunksX || cy >= chunksY || cz >= chunksZ)
            {
                return;
            }

            ChunkInfo info = unsortedChunks[cx + chunksX * (cy + cz * chunksY)];

            if (info.AllAir)
            {
                return;                          // do not recreate chunks completely air
            }
            info.Empty         = false;
            info.PendingDelete = true;
        }
        public void RedrawBlock(int x, int y, int z, BlockID block, int oldHeight, int newHeight)
        {
            int cx = x >> 4, cy = y >> 4, cz = z >> 4;

            // Does this chunk now contain air?
            ChunkInfo curInfo = renderer.unsortedChunks[cx + chunksX * (cy + cz * chunksY)];

            curInfo.AllAir &= game.BlockInfo.Draw[block] == DrawType.Gas;

            // NOTE: It's a lot faster to only update the chunks that are affected by the change in shadows,
            // rather than the entire column.
            int newCy = newHeight < 0 ? 0 : newHeight >> 4;
            int oldCy = oldHeight < 0 ? 0 : oldHeight >> 4;
            int minCy = Math.Min(oldCy, newCy), maxCy = Math.Max(oldCy, newCy);

            ResetColumn(cx, cy, cz, minCy, maxCy);
            World world = game.World;

            int bX = x & 0x0F, bY = y & 0x0F, bZ = z & 0x0F;

            if (bX == 0 && cx > 0)
            {
                ResetNeighbour(x - 1, y, z, block, cx - 1, cy, cz, minCy, maxCy);
            }
            if (bY == 0 && cy > 0 && Needs(block, world.GetBlock(x, y - 1, z)))
            {
                ResetChunk(cx, cy - 1, cz);
            }
            if (bZ == 0 && cz > 0)
            {
                ResetNeighbour(x, y, z - 1, block, cx, cy, cz - 1, minCy, maxCy);
            }

            if (bX == 15 && cx < chunksX - 1)
            {
                ResetNeighbour(x + 1, y, z, block, cx + 1, cy, cz, minCy, maxCy);
            }
            if (bY == 15 && cy < chunksY - 1 && Needs(block, world.GetBlock(x, y + 1, z)))
            {
                ResetChunk(cx, cy + 1, cz);
            }
            if (bZ == 15 && cz < chunksZ - 1)
            {
                ResetNeighbour(x, y, z + 1, block, cx, cy, cz + 1, minCy, maxCy);
            }
        }
Пример #25
0
        void DeleteChunk(ChunkInfo info)
        {
            info.Empty = false; info.AllAir = false;
                        #if OCCLUSION
            info.OcclusionFlags = 0;
            info.OccludedFlags  = 0;
                        #endif
                        #if !GL11
            game.Graphics.DeleteVb(ref info.Vb);
                        #endif

            if (info.NormalParts != null)
            {
                ChunkPartInfo[] parts = info.NormalParts;
                for (int i = 0; i < parts.Length; i++)
                {
                    if (parts[i].Offset < 0)
                    {
                        continue;
                    }
                    renderer.normalPartsCount[i]--;
                                        #if GL11
                    game.Graphics.DeleteVb(ref parts[i].Vb);
                                        #endif
                }
                info.NormalParts = null;
            }

            if (info.TranslucentParts != null)
            {
                ChunkPartInfo[] parts = info.TranslucentParts;
                for (int i = 0; i < parts.Length; i++)
                {
                    if (parts[i].Offset < 0)
                    {
                        continue;
                    }
                    renderer.translucentPartsCount[i]--;
                                        #if GL11
                    game.Graphics.DeleteVb(ref parts[i].Vb);
                                        #endif
                }
                info.TranslucentParts = null;
            }
        }
Пример #26
0
        static void QuickSort(int[] keys, ChunkInfo[] values, int left, int right)
        {
            while (left < right)
            {
                int i = left, j = right;
                int pivot = keys[(i + j) / 2];
                // partition the list
                while (i <= j)
                {
                    while (pivot > keys[i])
                    {
                        i++;
                    }
                    while (pivot < keys[j])
                    {
                        j--;
                    }

                    if (i <= j)
                    {
                        int       key   = keys[i]; keys[i] = keys[j]; keys[j] = key;
                        ChunkInfo value = values[i]; values[i] = values[j]; values[j] = value;
                        i++; j--;
                    }
                }

                // recurse into the smaller subset
                if (j - left <= right - i)
                {
                    if (left < j)
                    {
                        QuickSort(keys, values, left, j);
                    }
                    left = i;
                }
                else
                {
                    if (i < right)
                    {
                        QuickSort(keys, values, i, right);
                    }
                    right = j;
                }
            }
        }
        void RenderTranslucentBatch(int batch)
        {
            for (int i = 0; i < renderCount; i++)
            {
                ChunkInfo info = renderChunks[i];
                if (info.TranslucentParts == null)
                {
                    continue;
                }
                ChunkPartInfo part = info.TranslucentParts[batch];

                if (part.IndicesCount == 0)
                {
                    continue;
                }
                DrawTranslucentPart(info, ref part, 1);
            }
        }
        int UpdateChunksStill(ref int chunkUpdates)
        {
            ChunkInfo[] chunks      = renderer.chunks, render = renderer.renderChunks;
            int         j           = 0;
            int         viewDistSqr = AdjustViewDist(game.ViewDistance);
            int         userDistSqr = AdjustViewDist(game.UserViewDistance);

            for (int i = 0; i < chunks.Length; i++)
            {
                ChunkInfo info = chunks[i];
                if (info.Empty)
                {
                    continue;
                }
                int  distSqr = distances[i];
                bool noData  = info.NormalParts == null && info.LiquidParts == null && info.TranslucentParts == null;

                if (!noData && distSqr >= userDistSqr + 32 * 16)
                {
                    DeleteChunk(info); continue;
                }
                noData |= info.PendingDelete;

                if (noData && distSqr <= userDistSqr && chunkUpdates < chunksTarget)
                {
                    DeleteChunk(info);
                    BuildChunk(info, ref chunkUpdates);

                    // only need to update the visibility of chunks in range.
                    info.Visible = distSqr <= viewDistSqr &&
                                   game.Culling.SphereInFrustum(info.CentreX, info.CentreY, info.CentreZ, 14);              // 14 ~ sqrt(3 * 8^2)
                    if (info.Visible && !info.Empty)
                    {
                        render[j] = info; j++;
                    }
                }
                else if (info.Visible)
                {
                    render[j] = info; j++;
                }
            }
            return(j);
        }
 void OccludeZ(int cx, int cy, int cz, int zOffset, ChunkInfo info)
 {
     cz += zOffset;
     if (cz >= 0 && cz < chunksZ)
     {
         ChunkInfo neighbour = unsortedChunks[cx + chunksX * (cy + cz * chunksY)];
         if ((neighbour.OccludedFlags & neighbour.DistanceFlags) != neighbour.DistanceFlags)
         {
             info.Occluded = false;
         }
         else
         {
             info.OccludedFlags |= flagZ;
         }
     }
     else
     {
         info.Occluded = false;
     }
     info.DistanceFlags |= flagZ;
 }
 void OccludeY(int cx, int cy, int cz, int yOffset, ChunkInfo info)
 {
     cy += yOffset;
     if (cy >= 0 && cy < chunksY)
     {
         ChunkInfo neighbour = unsortedChunks[cx + chunksX * (cy + cz * chunksY)];
         if ((neighbour.OccludedFlags & neighbour.DistanceFlags) != neighbour.DistanceFlags)
         {
             info.Occluded = false;
         }
         else
         {
             info.OccludedFlags |= flagY;
         }
     }
     else
     {
         info.Occluded = false;
     }
     info.DistanceFlags |= flagY;
 }
 void OccludeX(int cx, int cy, int cz, int xOffset, ChunkInfo info)
 {
     cx += xOffset;
     if (cx >= 0 && cx < chunksX)
     {
         ChunkInfo neighbour = unsortedChunks[cx + chunksX * (cy + cz * chunksY)];
         if ((neighbour.OccludedFlags & neighbour.DistanceFlags) != neighbour.DistanceFlags)
         {
             info.Occluded = false;
         }
         else
         {
             info.OccludedFlags |= flagX;
         }
     }
     else
     {
         info.Occluded = false;
     }
     info.DistanceFlags |= flagX;
 }
Пример #32
0
        void UpdateChunksAndVisibility(double deltaTime, ref int chunkUpdats, int adjViewDistSqr)
        {
            ChunkInfo[] chunks = renderer.chunks;
            for (int i = 0; i < chunks.Length; i++)
            {
                ChunkInfo info = chunks[i];
                if (info.Empty)
                {
                    continue;
                }
                int  distSqr = distances[i];
                bool inRange = distSqr <= adjViewDistSqr;

                if (info.NormalParts == null && info.TranslucentParts == null)
                {
                    if (inRange && chunkUpdats < chunksTarget)
                    {
                        BuildChunk(info, ref chunkUpdats);
                    }
                }
                info.Visible = inRange &&
                               game.Culling.SphereInFrustum(info.CentreX, info.CentreY, info.CentreZ, 14);            // 14 ~ sqrt(3 * 8^2)
            }
        }
Пример #33
0
        void BuildChunk(ChunkInfo info, ref int chunkUpdates)
        {
            game.ChunkUpdates++;
            builder.GetDrawInfo(info.CentreX - 8, info.CentreY - 8, info.CentreZ - 8,
                                ref info.NormalParts, ref info.TranslucentParts, ref info.AllAir);

            info.PendingDelete = false;
            if (info.NormalParts == null && info.TranslucentParts == null)
            {
                info.Empty = true;
            }
            else
            {
                if (info.NormalParts != null)
                {
                    IncrementUsed(info.NormalParts);
                }
                if (info.TranslucentParts != null)
                {
                    IncrementUsed(info.TranslucentParts);
                }
            }
            chunkUpdates++;
        }
Пример #34
0
        void DrawPart( ChunkInfo info, ref ChunkPartInfo part )
        {
            api.BindVb( part.VbId );
            bool drawLeft = info.DrawLeft && part.LeftCount > 0;
            bool drawRight = info.DrawRight && part.RightCount > 0;
            bool drawBottom = info.DrawBottom && part.BottomCount > 0;
            bool drawTop = info.DrawTop && part.TopCount > 0;
            bool drawFront = info.DrawFront && part.FrontCount > 0;
            bool drawBack = info.DrawBack && part.BackCount > 0;

            if( drawLeft && drawRight ) {
                api.FaceCulling = true;
                api.DrawIndexedVb_TrisT2fC4b( part.LeftCount + part.RightCount, part.LeftIndex );
                api.FaceCulling = false;
            } else if( drawLeft ) {
                api.DrawIndexedVb_TrisT2fC4b( part.LeftCount, part.LeftIndex );
            } else if( drawRight ) {
                api.DrawIndexedVb_TrisT2fC4b( part.RightCount, part.RightIndex );
            }

            if( drawFront && drawBack ) {
                api.FaceCulling = true;
                api.DrawIndexedVb_TrisT2fC4b( part.FrontCount + part.BackCount, part.FrontIndex );
                api.FaceCulling = false;
            } else if( drawFront ) {
                api.DrawIndexedVb_TrisT2fC4b( part.FrontCount, part.FrontIndex );
            } else if( drawBack ) {
                api.DrawIndexedVb_TrisT2fC4b( part.BackCount, part.BackIndex );
            }

            if( drawBottom && drawTop ) {
                api.FaceCulling = true;
                api.DrawIndexedVb_TrisT2fC4b( part.BottomCount + part.TopCount, part.BottomIndex );
                api.FaceCulling = false;
            } else if( drawBottom ) {
                api.DrawIndexedVb_TrisT2fC4b( part.BottomCount, part.BottomIndex );
            } else if( drawTop ) {
                api.DrawIndexedVb_TrisT2fC4b( part.TopCount, part.TopIndex );
            }
        }
        void DrawBigPart(ChunkInfo info, ref ChunkPartInfo part)
        {
            gfx.BindVb(part.VbId);
            bool drawLeft   = info.DrawLeft && part.LeftCount > 0;
            bool drawRight  = info.DrawRight && part.RightCount > 0;
            bool drawBottom = info.DrawBottom && part.BottomCount > 0;
            bool drawTop    = info.DrawTop && part.TopCount > 0;
            bool drawFront  = info.DrawFront && part.FrontCount > 0;
            bool drawBack   = info.DrawBack && part.BackCount > 0;

            if (drawLeft && drawRight)
            {
                gfx.FaceCulling = true;
                gfx.DrawIndexedVb_TrisT2fC4b(part.LeftCount + part.RightCount, part.LeftIndex);
                gfx.FaceCulling = false;
                game.Vertices  += part.LeftCount + part.RightCount;
            }
            else if (drawLeft)
            {
                gfx.DrawIndexedVb_TrisT2fC4b(part.LeftCount, part.LeftIndex);
                game.Vertices += part.LeftCount;
            }
            else if (drawRight)
            {
                gfx.DrawIndexedVb_TrisT2fC4b(part.RightCount, part.RightIndex);
                game.Vertices += part.RightCount;
            }

            if (drawFront && drawBack)
            {
                gfx.FaceCulling = true;
                gfx.DrawIndexedVb_TrisT2fC4b(part.FrontCount + part.BackCount, part.FrontIndex);
                gfx.FaceCulling = false;
                game.Vertices  += part.FrontCount + part.BackCount;
            }
            else if (drawFront)
            {
                gfx.DrawIndexedVb_TrisT2fC4b(part.FrontCount, part.FrontIndex);
                game.Vertices += part.FrontCount;
            }
            else if (drawBack)
            {
                gfx.DrawIndexedVb_TrisT2fC4b(part.BackCount, part.BackIndex);
                game.Vertices += part.BackCount;
            }

            // Special handling for top and bottom as these can go over 65536 vertices and we need to adjust the indices in this case.
            if (drawBottom && drawTop)
            {
                gfx.FaceCulling = true;
                if (part.IndicesCount > maxIndices)
                {
                    int part1Count = maxIndices - part.BottomIndex;
                    gfx.DrawIndexedVb_TrisT2fC4b(part1Count, part.BottomIndex);
                    gfx.DrawIndexedVb_TrisT2fC4b(part.BottomCount + part.TopCount - part1Count, maxVertex, 0);
                }
                else
                {
                    gfx.DrawIndexedVb_TrisT2fC4b(part.BottomCount + part.TopCount, part.BottomIndex);
                }
                gfx.FaceCulling = false;
                game.Vertices  += part.TopCount + part.BottomCount;
            }
            else if (drawBottom)
            {
                int part1Count;
                if (part.IndicesCount > maxIndices &&
                    (part1Count = maxIndices - part.BottomIndex) < part.BottomCount)
                {
                    gfx.DrawIndexedVb_TrisT2fC4b(part1Count, part.BottomIndex);
                    gfx.DrawIndexedVb_TrisT2fC4b(part.BottomCount - part1Count, maxVertex, 0);
                }
                else
                {
                    gfx.DrawIndexedVb_TrisT2fC4b(part.BottomCount, part.BottomIndex);
                }
                game.Vertices += part.BottomCount;
            }
            else if (drawTop)
            {
                int part1Count;
                if (part.IndicesCount > maxIndices &&
                    (part1Count = maxIndices - part.TopIndex) < part.TopCount)
                {
                    gfx.DrawIndexedVb_TrisT2fC4b(part1Count, part.TopIndex);
                    gfx.DrawIndexedVb_TrisT2fC4b(part.TopCount - part1Count, maxVertex, 0);
                }
                else
                {
                    gfx.DrawIndexedVb_TrisT2fC4b(part.TopCount, part.TopIndex);
                }
                game.Vertices += part.TopCount;
            }
        }
Пример #36
0
        void BuildChunk( ChunkInfo info, ref int chunkUpdates )
        {
            game.ChunkUpdates++;
            builder.GetDrawInfo( info.CentreX - 8, info.CentreY - 8, info.CentreZ - 8,
                                ref info.NormalParts, ref info.TranslucentParts );

            if( info.NormalParts == null && info.TranslucentParts == null ) {
                info.Empty = true;
            } else {
                IncrementUsed( info.NormalParts );
                IncrementUsed( info.TranslucentParts );
            }
            chunkUpdates++;
        }
Пример #37
0
 void DeleteChunk( ChunkInfo info, bool decUsed )
 {
     info.Empty = false;
     #if OCCLUSION
     info.OcclusionFlags = 0;
     info.OccludedFlags = 0;
     #endif
     DeleteData( ref info.NormalParts, decUsed );
     DeleteData( ref info.TranslucentParts, decUsed );
 }
Пример #38
0
        void DrawBigPart( ChunkInfo info, ref ChunkPartInfo part )
        {
            api.BindVb( part.VbId );
            bool drawLeft = info.DrawLeft && part.LeftCount > 0;
            bool drawRight = info.DrawRight && part.RightCount > 0;
            bool drawBottom = info.DrawBottom && part.BottomCount > 0;
            bool drawTop = info.DrawTop && part.TopCount > 0;
            bool drawFront = info.DrawFront && part.FrontCount > 0;
            bool drawBack = info.DrawBack && part.BackCount > 0;

            if( drawLeft && drawRight ) {
                api.FaceCulling = true;
                api.DrawIndexedVb_TrisT2fC4b( part.LeftCount + part.RightCount, part.LeftIndex );
                api.FaceCulling = false;
            } else if( drawLeft ) {
                api.DrawIndexedVb_TrisT2fC4b( part.LeftCount, part.LeftIndex );
            } else if( drawRight ) {
                api.DrawIndexedVb_TrisT2fC4b( part.RightCount, part.RightIndex );
            }

            if( drawFront && drawBack ) {
                api.FaceCulling = true;
                api.DrawIndexedVb_TrisT2fC4b( part.FrontCount + part.BackCount, part.FrontIndex );
                api.FaceCulling = false;
            } else if( drawFront ) {
                api.DrawIndexedVb_TrisT2fC4b( part.FrontCount, part.FrontIndex );
            } else if( drawBack ) {
                api.DrawIndexedVb_TrisT2fC4b( part.BackCount, part.BackIndex );
            }

            // Special handling for top and bottom as these can go over 65536 vertices and we need to adjust the indices in this case.
            if( drawBottom && drawTop ) {
                api.FaceCulling = true;
                if( part.IndicesCount > maxIndices ) {
                    int part1Count = maxIndices - part.BottomIndex;
                    api.DrawIndexedVb_TrisT2fC4b( part1Count, part.BottomIndex );
                    api.DrawIndexedVb_TrisT2fC4b( part.BottomCount + part.TopCount - part1Count, maxVertex, 0 );
                } else {
                    api.DrawIndexedVb_TrisT2fC4b( part.BottomCount + part.TopCount, part.BottomIndex );
                }
                api.FaceCulling = false;
            } else if( drawBottom ) {
                int part1Count;
                if( part.IndicesCount > maxIndices &&
                   ( part1Count = maxIndices - part.BottomIndex ) < part.BottomCount ) {
                    api.DrawIndexedVb_TrisT2fC4b( part1Count, part.BottomIndex );
                    api.DrawIndexedVb_TrisT2fC4b( part.BottomCount - part1Count, maxVertex, 0 );
                } else {
                    api.DrawIndexedVb_TrisT2fC4b( part.BottomCount, part.BottomIndex );
                }
            } else if( drawTop ) {
                int part1Count;
                if( part.IndicesCount > maxIndices &&
                   ( part1Count = maxIndices - part.TopIndex ) < part.TopCount ) {
                    api.DrawIndexedVb_TrisT2fC4b( part1Count, part.TopIndex );
                    api.DrawIndexedVb_TrisT2fC4b( part.TopCount - part1Count, maxVertex, 0 );
                } else {
                    api.DrawIndexedVb_TrisT2fC4b( part.TopCount, part.TopIndex );
                }
            }
        }
            public void Enqueue( ChunkInfo item )
            {
                if( Size == array.Length )
                    throw new InvalidOperationException( "Queue limit reached" );

                array[tail] = item;
                tail = (tail + 1) % array.Length;
                Size++;
            }
Пример #40
0
        void DrawTranslucentPart( ChunkInfo info, ref ChunkPartInfo part )
        {
            api.BindVb( part.VbId );
            bool drawLeft = (drawAllFaces || info.DrawLeft) && part.LeftCount > 0;
            bool drawRight = (drawAllFaces || info.DrawRight) && part.RightCount > 0;
            bool drawBottom = (drawAllFaces || info.DrawBottom) && part.BottomCount > 0;
            bool drawTop = (drawAllFaces || info.DrawTop) && part.TopCount > 0;
            bool drawFront = (drawAllFaces || info.DrawFront) && part.FrontCount > 0;
            bool drawBack = (drawAllFaces || info.DrawBack) && part.BackCount > 0;

            if( drawLeft && drawRight ) {
                api.DrawIndexedVb_TrisT2fC4b( part.LeftCount + part.RightCount, part.LeftIndex );
            } else if( drawLeft ) {
                api.DrawIndexedVb_TrisT2fC4b( part.LeftCount, part.LeftIndex );
            } else if( drawRight ) {
                api.DrawIndexedVb_TrisT2fC4b( part.RightCount, part.RightIndex );
            }

            if( drawFront && drawBack ) {
                api.DrawIndexedVb_TrisT2fC4b( part.FrontCount + part.BackCount, part.FrontIndex );
            } else if( drawFront ) {
                api.DrawIndexedVb_TrisT2fC4b( part.FrontCount, part.FrontIndex );
            } else if( drawBack ) {
                api.DrawIndexedVb_TrisT2fC4b( part.BackCount, part.BackIndex );
            }

            if( drawBottom && drawTop ) {
                api.DrawIndexedVb_TrisT2fC4b( part.BottomCount + part.TopCount, part.BottomIndex );
            } else if( drawBottom ) {
                api.DrawIndexedVb_TrisT2fC4b( part.BottomCount, part.BottomIndex );
            } else if( drawTop ) {
                api.DrawIndexedVb_TrisT2fC4b( part.TopCount, part.TopIndex );
            }
        }