-2

This is not related to a Minecraft clone

This question as been moved to CodeReview, as suggested. https://codereview.stackexchange.com/questions/62562/voxel-world-optimization#62562

I am currently writing a Turn-based Strategy Game like Final Fantasy Tactics. The game is played on floating Voxel-style islands, which are generated from heightmap images(will soon be dynamically generated using system.drawing). I already wrote the world generator(15 minute mock up) and the Voxel world optimizer. My only issue is that the Voxel World optimization takes around 900ms to complete. I wanted to show you guys some code and see if someone could assist me in dropping the opt-time down to around 100-200ms.

Here is my current Heightmap(The System is very crude at this point):

HeightMap Mockup

And here is a brief Rendering of said Heightmap:

HeightMap Rendering

The World Optimizer that I have already written takes into consideration when faces cannot be seen, and then if that can be asserted, hides said faces(The land masses are hollow despite having voxels present). Again, I just need someone to help me optimize the optimizer so that It can work faster.

Here is my world optimizer; I will provide any other source code if required.

using GameProject.Game.Framework.Geometry;
using System.Collections.Generic;
using OpenTK;

namespace GameProject.Game.Framework.Generators {
    public enum OptimizationType {
        FullOptimization
    }
    public static class WorldOptimizer {
        public static void OptimizeVoxelWorld( List<Voxel> world , OptimizationType optimizationType ) {
            switch( optimizationType ) {
                case OptimizationType.FullOptimization:
                    DoFullOptimization( world );
                    break;
            }
        }

        private static void DoFullOptimization( List<Voxel> world ) {
            /**
             * Loop Through The Voxel Collection and collect
             * potential neighbors.
             */
            foreach( Voxel currentVoxel in world ) {
                Vector3 backNeighbor = currentVoxel.Location;
                backNeighbor.X += 2.0f;
                Vector3 frontNeighbor = currentVoxel.Location;
                frontNeighbor.X -= 2.0f;
                Vector3 leftNeighbor = currentVoxel.Location;
                leftNeighbor.Z -= 2.0f;
                Vector3 rightNeighbor = currentVoxel.Location;
                rightNeighbor.Z += 2.0f;
                Vector3 topNeighbor = currentVoxel.Location;
                topNeighbor.Y += 2.0f;
                Vector3 bottomNeighbor = currentVoxel.Location;
                bottomNeighbor.Y -= 2.0f;

                /**
                 * This is the part that needs to be fixed.
                 * Basically we loop back through the collection
                 * AGAIN for every voxel. This means that we
                 * check every voxel at least twice, if not up
                 * to six times...I think.
                 */
                foreach( Voxel voxel in world ) {
                    if( voxel != currentVoxel ) {
                        if( voxel.Location == backNeighbor ) {
                            currentVoxel.ShowBackFace = false;
                        } else if( voxel.Location == frontNeighbor ) {
                            currentVoxel.ShowFrontFace = false;
                        } else if( voxel.Location == leftNeighbor ) {
                            currentVoxel.ShowLeftFace = false;
                        } else if( voxel.Location == rightNeighbor ) {
                            currentVoxel.ShowRightFace = false;
                        } else if( voxel.Location == topNeighbor ) {
                            currentVoxel.ShowTopFace = false;
                        } else if( voxel.Location == bottomNeighbor ) {
                            currentVoxel.ShowBottomFace = false;
                        }
                    }
                }
            }
        }

        /**
         * Add this feature later with bitwise flags and other sorts of glorious sugar.
         */
        private static void DoFullControlOptimization(List<Voxel> world) {

        }
    }
}

Suggestion #1; Use Parallel.ForEach Loop (Drops time down from 900ms to 260-300ms)

using System.Threading.Tasks;
using GameProject.Game.Framework.Geometry;
using System.Collections.Generic;
using OpenTK;

namespace GameProject.Game.Framework.Generators {
    public enum OptimizationType {
        FullOptimization
    }
    public static class WorldOptimizer {
        public static void OptimizeVoxelWorld( List<Voxel> world , OptimizationType optimizationType ) {
            switch( optimizationType ) {
                case OptimizationType.FullOptimization:
                    DoFullOptimization( world );
                    break;
            }
        }

        private static void DoFullOptimization( List<Voxel> world ) {
            /**
             * Loop Through The Voxel Collection and collect
             * potential neighbors.
             */

            // Parallel.ForEach drops Opt-Time down to 260-300ms!
            // Was 900ms with regular for-each loop.
            Parallel.ForEach( world , currentVoxel => {
                Vector3 backNeighbor = currentVoxel.Location;
                backNeighbor.X += 2.0f;
                Vector3 frontNeighbor = currentVoxel.Location;
                frontNeighbor.X -= 2.0f;
                Vector3 leftNeighbor = currentVoxel.Location;
                leftNeighbor.Z -= 2.0f;
                Vector3 rightNeighbor = currentVoxel.Location;
                rightNeighbor.Z += 2.0f;
                Vector3 topNeighbor = currentVoxel.Location;
                topNeighbor.Y += 2.0f;
                Vector3 bottomNeighbor = currentVoxel.Location;
                bottomNeighbor.Y -= 2.0f;

                /**
                 * This is the part that needs to be fixed.
                 * Basically we loop back through the collection
                 * AGAIN for every voxel. This means that we
                 * check every voxel at least twice, if not up
                 * to six times...I think.
                 */
                foreach( Voxel voxel in world ) {
                    if( voxel != currentVoxel ) {
                        if( voxel.Location == backNeighbor ) {
                            currentVoxel.ShowBackFace = false;
                        } else if( voxel.Location == frontNeighbor ) {
                            currentVoxel.ShowFrontFace = false;
                        } else if( voxel.Location == leftNeighbor ) {
                            currentVoxel.ShowLeftFace = false;
                        } else if( voxel.Location == rightNeighbor ) {
                            currentVoxel.ShowRightFace = false;
                        } else if( voxel.Location == topNeighbor ) {
                            currentVoxel.ShowTopFace = false;
                        } else if( voxel.Location == bottomNeighbor ) {
                            currentVoxel.ShowBottomFace = false;
                        }
                    }
                }
            } );
        }

        /**
         * Add this feature later with bitwise flags and other sorts of glorious sugar.
         */
        private static void DoFullControlOptimization(List<Voxel> world) {

        }
    }
}
Krythic
  • 4,184
  • 5
  • 26
  • 67
  • 4
    Probably better off on CodeReview. – tnw Sep 10 '14 at 18:48
  • If no one can answer this I will jump over to CodeReview. – Krythic Sep 10 '14 at 18:51
  • 1
    if you make your data structures consider spatial relation of voxels, then this gets a lot easier, as you don't need to search fo neighbors. Also consider parallelizing with `Parallel.ForEach` (or any other threading method) – thumbmunkeys Sep 10 '14 at 18:52
  • I assume Parallel.ForEach does some kind of streaming? I have never used it before but I will look into some literature now. – Krythic Sep 10 '14 at 18:55
  • I just implemented Parallel.ForEach and it dropped the Opt-Time down to about 300ms. Very nice! If anyone has any other suggestions I would love to hear your ideas! – Krythic Sep 10 '14 at 19:07
  • do you have to check every other voxel or just the ones next to it? and are your voxels stored in order i.e. vx at 0,0,0 is the first element in the array? – RadioSpace Sep 10 '14 at 19:17
  • I get the current voxel, check north, south, east, west, above, and below. I fully support project-structure changes if you can suggest an interesting enough one. – Krythic Sep 10 '14 at 19:21
  • Affirmative, Voxel(0,0,0) is at index 0 in the List. – Krythic Sep 10 '14 at 19:24
  • so you could in theory just loop six times for each voxel to check what is next to it. in stead of each voxel checking every other voxel. – RadioSpace Sep 10 '14 at 19:24
  • in 2D if I want to get at xy i use `vox[x+(y * (width - 1))]` i am sure this can be done in 3D as well. then you ditch the second foreach. and usea `for` loop that loops 6 times – RadioSpace Sep 10 '14 at 19:27
  • Apparently there was some confusion. Each voxel iteration checks its own neighbors. Then the next iteration checks its own neighbors. – Krythic Sep 10 '14 at 19:27
  • your second foreach loop is `foreach( Voxel voxel in world )` which would look at every other voxel – RadioSpace Sep 10 '14 at 19:29
  • Uhhh....wait, yeah, I think I see what you're saying. It's redundant to check them all that way. Hmmm... – Krythic Sep 10 '14 at 19:31

1 Answers1

0

the only thing to optimize beside you Rasterizer is

             /**
             * This is the part that needs to be fixed.
             * Basically we loop back through the collection
             * AGAIN for every voxel. This means that we
             * check every voxel at least twice, if not up
             * to six times...I think.
             */
            foreach( Voxel voxel in world ) {
                if( voxel != currentVoxel ) {

is checking all other voxels when you only need to check the ones next to current voxel only check the six nextdoor

RadioSpace
  • 953
  • 7
  • 15
  • Yes, someone already mentioned this; I am currently on the prowl for a potential way around this. Would you have any designs in mind? – Krythic Sep 10 '14 at 20:28
  • I will have to look at this after work (in about 2 hrs) – RadioSpace Sep 10 '14 at 20:50
  • Alright, I am currently looking at literature regarding R-Trees and Octrees. – Krythic Sep 10 '14 at 22:02
  • The number of Voxels would theotretically be dynamic. I would certainly say no more than 100*10*100, though. This would give, at most, a hundred voxels along x and z, while y is locked to a maximum of 10 voxels tall. – Krythic Sep 11 '14 at 06:01
  • @Krynn well with what i have it is doing 64*64*64 in about 50 ms 128*128*128 in 5 seconds it would be done but I got all nit picky about bounds checking so you don't index out side of the array. I'll post what i got tonight. if you are interested – RadioSpace Sep 11 '14 at 19:38
  • Always, but can you post it to the Code Review Link? This has officially been moved to there. =) – Krythic Sep 11 '14 at 20:30