Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

After playing minecraft...

Discussion in 'General Discussion' started by jc_lvngstn, Oct 8, 2010.

Thread Status:
Not open for further replies.
  1. jc_lvngstn

    jc_lvngstn

    Joined:
    Jul 19, 2006
    Posts:
    1,508
    First, I thoroughly enjoyed the game. It's amazing how something so simple on the surface can be so much fun :)

    But, in playing it, I found myself wondering about terrain engine of sorts he used. Does he just draw a 3 dimensional array of cubes, say around 1000x1000x128? Or do you think he uses some sort of raycasting to only draw the visible ones?

    I find the idea of a terrain that isn't just a heightmap to be neat, even a fairly "low resolution" on built up of cubes. Any thoughts how this could be implemented in Unity?
     
  2. Ntero

    Ntero

    Joined:
    Apr 29, 2010
    Posts:
    1,436
    When using a parralel aligned cubes you only ever need to render 3 faces at a time. it is the 3 faces who's Dot(player.forward, face.normal) < 0.

    From what I hear he has an array of faces in the 6 cardinal directions, and then calls render on the 3 required faces for that frame at any particular frame. I'm unsure of anything more technical within to handle culling and stacked cubes, but I've heard the 3 face rule for at least the basic first pass culling.
     
  3. dogzerx2

    dogzerx2

    Joined:
    Dec 27, 2009
    Posts:
    3,964
    is minecraft in any way optimized? It doesn't run as fast as you'd expect... considering the graphics
     
  4. Tinus

    Tinus

    Joined:
    Apr 6, 2009
    Posts:
    437
    The game also only draws cubes visible to the player. You can notice this clearly when you accidentally clip the camera through a block; it looks like a huge void behind it, with only caves being rendered in the distance below. So I guess one thing he does is to only render blocks that are directly adjacent to air or water, where the player has the potential to see it. This test is probably done in a single pass for initial terrain generation, and then incrementally for blocks around a block that experiences a state change.

    Does that help?
     
  5. jc_lvngstn

    jc_lvngstn

    Joined:
    Jul 19, 2006
    Posts:
    1,508

    Yes, it sure does. I think your guess about only rendering blocks that are directly adjacent to air or water is a HUGE tip to me. And it makes perfect sense...I only wish I'd though to of that myself :)
    +1 to Awesomeness for you there

    Now...about storage...think it's just a huge 3 dimensional array of blocks?
     
  6. Ntero

    Ntero

    Joined:
    Apr 29, 2010
    Posts:
    1,436
    I thought I recalled hearing it's a huge array of references to block types. So each type of block is numbered or indexed, and then it only stores the texture and block parameters once in the static index list. Although I'm unsure of how that works for Blocks like Water. An array of shorts is much smaller than an array of block structs.
     
  7. LamentConfig

    LamentConfig

    Joined:
    Sep 28, 2010
    Posts:
    292
    I really want to play minecraft - buuuut - from what I've heard about it, it would kill any productivity I have with my current project :)
     
    Seneral likes this.
  8. Dreamora

    Dreamora

    Joined:
    Apr 5, 2008
    Posts:
    26,601
    its interesting how he gets the file that small cause technically a 1024x1024 block file eats 1mb per layer, even if its only using 1 byte per block
    but the inf format should be documented on its wiki I think
     
  9. BIG-BUG

    BIG-BUG

    Joined:
    Mar 29, 2009
    Posts:
    457
    As I saw in a video the landscape is practically infinite, isn't it? I guess the map around the player is generated in realtime and only changed blocks / chunks are applied and stored. This concept works over network too.
     
  10. Tinus

    Tinus

    Joined:
    Apr 6, 2009
    Posts:
    437
    I haven't seen the file format, but with run-length-encoding (like you do with characters in a text file) alone I think you can compress it down a whole lot. The distribution of block types is not noisy, instead you get blocks of the same type clustering together because of the procedural algorithms. Just consider the earth block, there's huge continuous stretches of that type everywhere. In your fileformat you do something like: "from here, repeat the earth block 34 times". One byte for the block type, another for the amount of repeats. :)

    For water I think he probably stores the level of water in a block next to the block type. It seems to act pretty discreet, with water per block only having 8 or 16 different levels of height. I would guess flow direction and such do not have to be stored, they can be logically derived from the current state of the waterblocks.
     
    Last edited: Oct 8, 2010
    flaminghairball likes this.
  11. jackshiels

    jackshiels

    Joined:
    Feb 29, 2008
    Posts:
    357
    I lub this game... so much time wasted.

    AFAIK the map goes 128 blocks down.
     
  12. Dreamora

    Dreamora

    Joined:
    Apr 5, 2008
    Posts:
    26,601
    the amount of torches you need for that haha ...
    already digging around at 20 below ground means you have cut down a forest and ripped appart a whole store of coal ;)
     
  13. beezir

    beezir

    Joined:
    Apr 3, 2009
    Posts:
    133
    I played around with it a bit in unity, and the way I did it was 10x10x10 chunks of blocks, rendering only the faces exposed to empty space. Random, stable noise let the terrain go infinitely in all directions (including down). Performance was pretty good for a 4-5 chunk view distance (so 1,000,000+ possible cubes in memory, but only a fraction of those actually adding polygons to the world). I was using a single byte to store the block type, but you could still reduce that significantly. Regardless, with that view distance and storage size, it's still only 1mb of data for a decent size area. If you stored the random seed, the actual data file could be pretty minimal - just save the cubes that have changed and use the noise algorithm to determine the others.
     
    Last edited: Oct 9, 2010
    Deleted User and Zimbres like this.
  14. CaptainKiyaku

    CaptainKiyaku

    Joined:
    Feb 8, 2009
    Posts:
    324
    I've tried something like this in Unity too.
    NOTE: SophieH was so kind to give me her voxel script, that made this happen!

    I modified her script to only use byte arrays and added some other changes. But my world can only be 2048x2048x128 right now, if it goes bigger, you would get an memory exception error so this is something i want to work on.

    I've got the idea to only store chunks (16x16x16 blocks in a group) that got changed, instead of saving each block into that array. But it got too slow finding the right chunk among millions in one frame. So i dunno how to solve this to create a much bigger world. I would welcome any suggestion.

    So what the script does is to render only faces if it is on the surface. It groups every 16x16x16 block into one chunk to save drawcalls as well and i use a big atlas map for the textures (the texture i use is from Doku's RPG Texture package for minecraft).

    You can try an older version. I have a newer one with much better controls, only one cube to place/destroy, and better block detection. But i haven't uploaded it cause im still working on increasing the world, somehow.



    Unity Minecraft

    NOTE: It will be laggy at the beginning to generate the level, later on i will do that in a loading screen. So when you try placing cubes at the beginning, it takes a few seconds to display them, cause ive limited my current version so it can only update < 10 chunks at a time.

    The physic and collision is crappy too, just added something simple so i can play around with it.

    Controls:
    - WASD: Movement
    - Left click: Place current block type
    - Right click: remove block
    - R: toggle between blocks (no visual indicator yet)
    - F: Create a big sphere of the current block type
    - B: Bomb away some blocks (this is fun 8D)

    The red transparent cube shows the field you would remove.
    The blue transparent cube shows the field where you place a new block.
     
  15. jkreijkamp

    jkreijkamp

    Joined:
    Dec 5, 2007
    Posts:
    129
    @Kiyaku
    Quite a lot of fun to play around with! I start to understand the MineCraft addiction, note to myself: stay away from minecraft.
     
  16. jc_lvngstn

    jc_lvngstn

    Joined:
    Jul 19, 2006
    Posts:
    1,508
    Kiyaku,
    I wasn't sure what visual range Minecraft uses...but I put a sphere at one corner of your map, and back up to the middle of the map. I felt like that was a good view distance...so maybe minecraft uses something more along the lines of 1024x1024x128, instead? Not sure, I guess I could hop on and try to estimate it.

    I then measured how many blocks I could cross in Minecraft, in 5 seconds...roughtly 25. I then found a flat piece of terrain (or as close as I could, without digging one out) and picked a tree that was JUST on the edge of visibility. I then measured how much time it took to reach that tree.
    It took right about 45 seconds. So 45/5 = 9, 9*25 = 225.
    So, this means the the viewing distance is roughly 225 blocks forward. This is so small, I'm thinking about double checking my timing or something.
    But, if this is the case, I could easily go ahead and say...maybe it is really 28 blocks for every 5 seconds of walking, easily giving you the nice round number of 255.
    Meaning that the total drawing distance around the player is 512x512x128.

    That's a LOT smaller than 2048x2048x128 :) 33mb instead of 536mb!!
    I'm assuming one byte per block.

    (I ran a second test, and got a forward distance of 336...but that was a bad test, I had some hilly terrain. I'm just too lazy to dig a straight path right now. But even then, that would put a map of 672x672x128.)


    As far as chunks, I think that is definitely a great idea.
    I noticed that when new terrain moves into view, it appears in chunks. All i had to do was find a tree that was on the edge of visibility, then move forward until the next "chunk" came into view with another landmark that was on the edge of visibility, and measure the blocks between them.
    Roughly 16...so I'm going to say that as the player moves forward, new terrain is added in 16x16x16 chunks. So with 33mb of blocks to draw in total (33, 554, 432) divided into chunks of 16x16x16...that comes to only having to manage 8192 chunks of blocks in memory.

    So your chunk array definition would be something like
    byte[,,] chunk = new byte[16,16,16]; (4096 blocks or bytes)

    your "world" or display array definition would be
    Chunk[,,] world = new Chunk[32,32,8];

    A bit more manageable, I think.
    Entire chunks could be marked as drawable or not, simply based on whether or not they only contain air or have no blocks adjacent to air or water (not possible to be seen). This would be a huge time saver. It would a simple thing to calculate this whenever a new chunk becomes visible.

    I'm still working on how lighting is calculated for the blocks.

    So, I have some questions for you:
    You mentioned only drawing faces if the face was on the surface...meaning its next to an empty block, I assume. If this is so, what is a "Cube"? Is it just a prefab with 6 planes that you can hide or show?

    After looking at your screenshot, it appears that you generate a cube for each visible square on the map, possibly only drawing certain sides which are visible, but still using a cube collider.


    Ok, a little more and I'm done...for now :)
    Some thoughts on the types of blocks:
    That array of bytes, for storing blocks...can hold 256 values. That's a LOT of different block types. From looking at the wiki, Minecraft has about 64. That leaves a LOT more room in that byte for extra data.
    Let's say the remaining 64 values are for future block types. Great, that leaves half the byte for other things, like the precalculated lighting for each block.
    So values 0-127 determine block type.
    You could use the rest to determine the lighting amounts for the 6 block sides.

    I'm not saying this is the best approach...but it might be a good way to save memory. Personally, I don't care for this approach unless I have to, as it is somewhat inflexible for new ideas or such. Other the other hand, having 33 million "Block" classes instead of bytes, each with its own storage baggage (bloat if you will), could be pretty rough.

    Maybe what is best is a hybrid approach...empty blocks get 1 byte of storage...the block type. Visible blocks get the full gamut.
     
    Zimbres likes this.
  17. CaptainKiyaku

    CaptainKiyaku

    Joined:
    Feb 8, 2009
    Posts:
    324
    Well the viewing distant might be that long. But then what, how do you generate the next blocks when you keep on walking, moving the fields in that array? But then where do you store the changed tiles so you can save them?

    I actually tried the saving-the-chunks approach before. Instead of saving "blocks", i saved each chunk and i gave the chunk a script that hold the 4096 byte array to store its own blocks. So with this approach i can create worlds that are so much bigger.

    But the problem i encountered is speed. When you change a block in the world, you first have to check what chunk it belongs to. So the only thing that came into my mind, is going through every created chunk and see if the block belongs to that chunk. But this process takes too long, you will feel "lags" if you created too many chunks.

    Also loading a new chunk, and this will answer your next question about "what is a block?", is lagging when the field is too big.
    A "block" is just a virtual position in my array. I don't create cubes, i only create the faces. And when i create a chunk, i check i there would be a block above, left, behind, right, infront or under me. If yes, dont render my faces on this position. So i only render faces that are visible.
    This is really fast using the big byte array to store the block types.

    But when you use the chunk system, i would have to call the "check what chunk has this block" function 16x16x16x6 times for each chunk when recreating the chunk (block got added or removed). 16x16x16 is the number of blocks in that chunk, and then 6 times for each direction. So you would loop 24576 times the "check what chunk has this block" function to create a chunk. This causes a 1-2 second lag (hope i did the math right, its 4am right now lol).

    So this is where my current problem is, trying to create a system that is fast enough to create a huge number of chunks + blocks, and to find a way how to create a "infinite" world like in minecraft. I think the "only save whats changed" approach is the way to go. But dunno how yet.

    But yes i've also changed the block type to a byte array cause i wont need more than 256 different blocks so this is enough lol. However i planned a second array for different purposes like "time", so you can change blocks over time (regrowing, changing skin, etc). You can store that in the same byte array, depending on how many objects you want to create in your world.

    For collision detection i just create a 3x3 grid out of invisible box colliders around the player, and the boxes will only activate its collider if the fields around me are visible (blocktype != 0). Works pretty good so far.
     
  18. jc_lvngstn

    jc_lvngstn

    Joined:
    Jul 19, 2006
    Posts:
    1,508
    I found some more info here, on the level format:
    http://www.minecraftwiki.net/wiki/Alpha_Level_Format
    His "chunks" of blocks are 16x16x128.
    I like how he stores the chunks of data into a breakdown of folders, based on the chunk coordinates. This allows for quick and easy access of the data, no matter where in the world you are loading the chunk from.

    This answers a big question I had...how do you quickly and effeciently
    save and load data for a chunk of data, which might not necessarily be part of a square map, without having to search through the file or load the whole level?


    As far as knowing which chunk a block belongs to...could you just have each block have a reference to its chunk? That way, when you collide or remove a block, you know which chunk it is a part of by looking at its reference to the chunk. Adding a block is simply replacing an existing block of air with some other material.

    Seems like you could do that. It also might be a good idea to have an index of the block within the chunk. I guess it depends on how much it increases memory storage.
     
    Last edited: Oct 9, 2010
  19. jc_lvngstn

    jc_lvngstn

    Joined:
    Jul 19, 2006
    Posts:
    1,508
    Some thoughts about how to add/remove chunks, as you move around the map.
    You could do a linked list type thing, where each chunk has a reference to the chunks to the north/south/east/west of it.

    Or, you could just have an array of chunks for your world (32x32) which would give you a 512x512x128 block world.
    The array [0..31, 0..31) would reference the chunk at those chunk coordinates.
    If the player moves 'east' and you need to "shift" the world west...you could easily load in the east border of chunks from disk (or create them, whatever), shift the array to the left one chunk, and set the eastmost column to the new east chunks. Code could be able to shift these pretty quick...a 32x32 array is nothing. And you are only updating the references.


    I'm throwing together a quick code spike to declare the chunk, block, and world classes, and see what effect different options have on memory usage.
    If you wanted, each chunk could have a column/row value which is updated.

    This is just all in my head. there are probably optimizations that could be made, shortcuts, and so forth.
     
  20. beezir

    beezir

    Joined:
    Apr 3, 2009
    Posts:
    133
    If anyone wants a codebase to play around with, here's mine. Also a webplayer to just mess around with it. Feel free to use it in a free project... on the off chance that you become the next notch, please throw a small chunk of that cash at me. ;)

    Web Player

    Sometimes you start in a deep hole if the random terrain just happened to dig one under the starting point.

    You have to click on the screen to capture the mouse, but it'll respond to all your movements regardless. I just used very slightly modified player scripts from the default stuff. You can left-click to dig, but I never got around to right-click-to-add (it would technically be pretty simple). Camera and all that is default, so it doesn't show from the right height and blocks look bigger than they otherwise would. Code is messy, mostly uncommented, and I take no responsibility for any inappropriate remarks you may come across. It was just for fun. Infinite world in all directions, I've broken the loader a couple times but it usually works just fine. Lighting isn't enabled, fog hides the chunks popping in. Have fun!

    Unity Package Download
     
    Last edited: Oct 9, 2010
  21. jc_lvngstn

    jc_lvngstn

    Joined:
    Jul 19, 2006
    Posts:
    1,508
    Beezir...this is pretty awesome of you. Thank you.
     
  22. CaptainKiyaku

    CaptainKiyaku

    Joined:
    Feb 8, 2009
    Posts:
    324
    wow that's great! Thanks a lot, i will check that out and add it to my current project :D

    I think if someone becomes the next notch with that, you would get a big pile of money hehe.
     
  23. Taintspore

    Taintspore

    Joined:
    Sep 29, 2009
    Posts:
    185
    @Beezir

    Good thing I decided to try doing that after our chat and before you released that or I'd have never learned anything :)

    Neat to see how you did it, especially how to properly seed the Simplex Noise. I was having trouble with it (I even tried changing the bit patterns like you did but I used the Unity random number gen, didn't work out very well) and decided to switch back to Perlin. I ported the perlin section of libnoise and oddly perlin runs for about 80% the time of simplex, I was a little puzzled but I think it might have to do with the reference implementation being made to play nice with electrical engineers' brains.

    Thanks for releasing that to the community, I'm sure there're tonnes of little tricks in there for us to discover.
     
    Last edited: Oct 10, 2010
  24. taumel

    taumel

    Joined:
    Jun 9, 2005
    Posts:
    5,292
    Cool! Although, i'm glad i've managed to escape Minecraft...
     
  25. jc_lvngstn

    jc_lvngstn

    Joined:
    Jul 19, 2006
    Posts:
    1,508
    What method is used to generate the landscape? I see you mentioned Simplex Noise and perlin...is there a specific name for the method used to generate the landscapes?
     
  26. beezir

    beezir

    Joined:
    Apr 3, 2009
    Posts:
    133
    I just used the noise algorithm (simplex in my case) to determine density and a second pass to carve out caverns. Then I generate meshes based on that data using standard unity functions.
     
  27. CaptainKiyaku

    CaptainKiyaku

    Joined:
    Feb 8, 2009
    Posts:
    324
    I don't know much about Simplex Noise so i was wondering if it's possible to keep a "static seed" or whatever so everytime you run the game, it would give you the same level?
     
  28. beezir

    beezir

    Joined:
    Apr 3, 2009
    Posts:
    133
    Yep, that's no problem. Saves a ton on storage space too, since you'd only have to save the seed and any changed blocks. :)
     
  29. jc_lvngstn

    jc_lvngstn

    Joined:
    Jul 19, 2006
    Posts:
    1,508
    So, in my current design, I'm trying to generate the landscape (which should be pretty quick.) I'm currently storing my block DATA (not the visual representation or prefabs) in chunks made up of 16x16x128 blocks. The world "map" is made up of 32x32 chunks, which gives me a total world of 512x512x128 blocks.

    So, creating the world DATA isn't that big a deal, it's real quick.

    However, when I do a pass to initially determine visible blocks, I'm just drawing a prefab block at those locations. As you can expect, creating a new instance of a prefab, heck even a cube primitive, is slower.

    REAL slow. And generates quite a bit of gameobjects for unity. If I drew every block in just ONE chunk...I'd be creating and drawing 32,768 instances of a block in unity. Granted, I'm only drawing the outer blocks because of visibility testing, but still. It's a lot of blocks. Just one side has 2048 blocks. The thing I like about this approach is, it lets me just create a block with a collider and assign materials, instead of having to generate meshes, work with uvs, etc. It's more intuitive to me (easy), but it seems to cost too much to create these in unity.

    So...looking at Beezir's approach of creating a mesh from the visible faces of the blocks in each chunk seems to be a very good approach. It's a little less intuitive for me, but mostly because it means I have to get into the nitty gritty of actually creating the mesh, working with the normals, etc.

    Also, I like the idea of breaking down chunks into 16x16x16 blocks, instead of 16x16x128.

    Any thoughts on this? I think I already know the approach I have to take (Beezir's), just wondering if maybe anyone else had any thoughts or suggestions.

    PS: One reason I wanted to take the prefab/block approach was, I'd like to be able to use irregular objects instead of just blocks. I guess both approaches would work with this, though.
     
    Last edited: Oct 11, 2010
  30. Taintspore

    Taintspore

    Joined:
    Sep 29, 2009
    Posts:
    185
    In my own code I take a similar approach to Beezir, but I store faces in six lists: top, bottom, left, right, front, back. Only the three possibly visible sides are enabled for rendering. I think this might be the approach that Notch took but I'm not certain.

    I've tried different chunk sizes and methods of storing/accessing chunks (ie: linked lists vs List<List<Chunk>>), and I found that the few chunks you have on screen the better. For instance, having a chunk size of 16x16x256 with a world size of 16x16x1 runs much faster than a using chunks of size 16x16x128 with a world size of 16x16x2 and faster by roughly a factor of 2 or 3 than using 16x16x16 chunks on a 16x16x16 world or even a 16x16x8 world.

    I also found that digging on 32x32x128 chunks causes an FPS hiccup and 16x16x128 doesn't, but the 32x32x128 chunks give a higher non digging framerate. So from my experience there are a TON of tradeoffs going on.

    For not so blocky blocks, you could look into Marching Cubes or Marching Tetrahedrons. I've heard they can create more geometry though and with MC you'll have to find fixes for the ambiguous cases.
     
  31. jc_lvngstn

    jc_lvngstn

    Joined:
    Jul 19, 2006
    Posts:
    1,508
    Taintspore, that's interesting about the performance difference with different chunk sizes.
    You mentioned lists...one thing I am doing is using a 32x32x1 array of chunks, for the visible world. Each chunk contains 16x16x128 blocks.

    To locate a particular chunk for any block, I just divide the block x world position (0-511) by 16, that gives the the chunk "X". I do the same with the block Y location to get the chunk Y.
    This means I can access any chunk pretty much instantly, given the block location. When I move into a new chunk boundary, I just do a quick slide in the opposite direction with my main chunk array...and since all am doing is changing references to my chunks, it's pretty quick with a 32x32 array.
    Any thoughts on this approach?

    As far as non cube blocks...I was thinking of more natural landscape formations. For example...a single rock column, taking up only one block, would just have a mesh sort of like a stalagmite. It seems like it would work the same, I would just have to have some soft of post-process on the terrain to intelligently say "Here is a rock corner...just put a corner here" or "here is a rock column, use the stalagmite mesh" instead of just using blocks. Not sure if that makes sense of not. It would definitely take more resources, but it should be interesting to try it out. It might be a neat way to have a more "natural" landscape, but still give the ability to be generated, mined, and expanded on.

    Regardless...I'm thoroughly enjoying this conversation. Thanks for contributing, all.
     
  32. CaptainKiyaku

    CaptainKiyaku

    Joined:
    Feb 8, 2009
    Posts:
    324
    @Beezir, great, i just tried giving the System.Random the same seed once a new level was created and it works perfect, it generates the same level over and over again. So i also added a saving system that only saves changed chunks indeed and applies it to the world after it got generated.

    I guess i will have to change it to a 16x16x128 chunk size too, cause if you increase the blockViewDistance in Beezirs code, it takes quite a long time to generate the world and loading a new row makes your fps drop down to 20 too (depending on your machine).

    And yes, Notch also uses the 16x16x128 version, his world isn't higher so it actually makes sense.

    This is the best thread ever here on the forum hehe. I can't wait to see all the "clones", seems like many people try something like that.

    I just realized though that the minecraft fanbase is really rough to people who try to make simliar games, i just saw it on Cubelands videos, or on the minecraft forum.
    I guess a lot of them don't even know that minecraft is a clone from infiniminer, and notch also mentioned that in one of his very old youtube videos where minecraft was called "the cave game".

    Competition is good, it can make minecraft better and gives people more choices. If the games have different systems, that is great too. I am planning on doing a loto f things different from minecraft. Sure it will have simliar stuff like the materials, gathering, etc, but i already created some fun new systems that puts the game into a different categorie.
     
  33. jc_lvngstn

    jc_lvngstn

    Joined:
    Jul 19, 2006
    Posts:
    1,508
    Honestly, I was a little hesitant about posting my interest in a minecraft clone. Snobbery exists everywhere, and often you'll get see the response "Why don't you make your own game, instead of just copying others?"

    Thing is...I love Minecraft, and I think imitation is the sincerest form of flattery. It's really inspired me to give something similar a shot. Look at the huge Tower Defense genre...at the huge variety there is now. Besides...since Notch isn't working for me to implement the changes *I* want...might as well do it myself :)

    Regardless...I'm still curious about the chunk sizes, specifically the depth. It seems like you should be able to have both infinite Height, Width, AND depth to the maps.
    Anyway...as soon as I get done with "work", I'll get a chance to work on it some more.
     
  34. Taintspore

    Taintspore

    Joined:
    Sep 29, 2009
    Posts:
    185
    That sounds pretty similar to the way I do it at the chunk level. Actually I quantize space the exact same way. I used the face lists on a per chunk basis and I've discovered it's actually slower than just having one mesh per chunk (I was trying to avoid sending the back faces to the gfx card but I underestimated my 8800). I'm guessing it's slower because i was sending more meshes.

    The problem with that is going to be figuring out when you've got a column or not, if you used marching cubes or a derivative it should automate the process.

    In regards to minecraft and cloning. Making a voxel based world doesn't mean you cloned minecraft, otherwise all polygon based (rather than sprite based) RPGs copied, let's say Ultima Underworld. Ignore the high and mighty's, if nothing else it's fun nd you're giving yourself a leg up for when hardware can better handle large numbers of voxels.

    I've got a question about the meshes themselves: I always end up with three vertices per triangle, I've tried using mesh.optimize() but it doesn't seem to work. I looked at beezir's project and it looks like he gets roughly a two to one ratio. I've gone looking for info on creating triangle strips and i could probably do it manually, but in the docs it says optimize is supposed to do that on it's own. There a trick to using it?
     
  35. jc_lvngstn

    jc_lvngstn

    Joined:
    Jul 19, 2006
    Posts:
    1,508
    As far as mesh.optimize(), I'm a bit inexperienced there. I'll likely end up using that approach, eventually, maybe I can be more help after I get that experience.
     
    Last edited: Oct 11, 2010
  36. CaptainKiyaku

    CaptainKiyaku

    Joined:
    Feb 8, 2009
    Posts:
    324
    I was worring about people hating me when i want to create a "minecraft" clone.

    But thinking about it, and i can just repeat myself, minecraft itself is a clone from Infiniminer. Noone complained about it (at least i never saw someone complaining about it), it's just that minecraft got so famous that everybody hates "rip offs".

    If you add nice new features and have more than a classic minecraft mode, people will rethink about it and wont complain so much.

    Noone hated people when they try to do a zelda clone, or super mario or a xyz world war shooter.
     
  37. Dreamora

    Dreamora

    Joined:
    Apr 5, 2008
    Posts:
    26,601
    Nobody speaks against a sandbox game that allows you to build and destroy and using blocks is just the only reasonable way about it.

    after all, infiniminer, minecraft etc are all "clones of Lego" reasonably seen and lego is a clone of old wooden brick based toys.
    so nothing wrong about.

    you will only be hated if it is a 1 : 1 clone instead of something with a similar concept.
    also minecraft kind of tries to even favor that as the development pace of the alpha isn't exactly stunning for a few hundred thousand USD win the past months. it still progresses as if it was a single dev in sparetime ... so there is definitely a massive vacuum present which the first one with the dedication, staff and pace will fill, whoever that is will make some happy happy cash.

    but one has to keep in mind that competing with minecraft isn't exactly trivial as its modding and community are its strength. it will take you potentially too long to compete even anywhere reasonably with it.
     
  38. CaptainKiyaku

    CaptainKiyaku

    Joined:
    Feb 8, 2009
    Posts:
    324
    @dreamora, i kinda agree with your points, i am not a big fan of this "who was first and who made a rip off".
    I think if people try to adopt a game idea, they can create something new and more interesting.

    Also yeah, notch is kinda lazy, selling ~30.000 copies in 24 hours but skips several weeks of updates. He hired people now so hopefully it gets a bit faster.


    @Taintspore, it's interesting you get a better framerate with 16x16x128 chunks. I just changed beezirs code to use that kind of chunks but everytime i need to load new ones, the frames drop to <20. Well it kinda makes sense cause now it has to generate 16x16x128 chunks at once per frame so this slows it down quite a lot.

    Maybe if i change it to 6 arrays as well could solve this problem. But i dunno, maybe i need a different system, hmpf.
     
  39. Taintspore

    Taintspore

    Joined:
    Sep 29, 2009
    Posts:
    185
    @Kiyaku: I'm not sure what is causing the lag, I'll have to look at Beezir's code (those tests were with my own).

    At the moment mine doesn't create new chunks as you wander but mesh generation after a dig op is really fast. Voxels tend to be pretty CPU intensive, especially since we can't do them using cuda or openCL (at least as far as I know).

    I tried increasing the visibility range of Beezir's system from 50 to 200 and I had to kill the process, from my tests with using multiple lists per chunk unity likes it much better when there are fewer chunks.

    Anyway, like i said, when imget a chance I'll take a look and see if I can figure out what is causing the difference in performance.

    It might be the noise function, Beezir's simplex looks the same as mine so I think we got it from the same place and I found that implementation slower than the perlin found in libnoise. I think it's because be that Perlin's reference implementation for Simplex was written for hardware and that the libnoise perlin noise is optimized for software.

    I still haven't managed to get mesh.optimize to work either. I know the algorithm is suboptimal (to make it go faster) but It's can't be this bad, I'm going to try to reorder the way the mesh is constructed to give optimize (lol, just realized that optimize is suboptimal) a better chance at creating triangle strips.
     
    Last edited: Oct 13, 2010
  40. CaptainKiyaku

    CaptainKiyaku

    Joined:
    Feb 8, 2009
    Posts:
    324
    Well i tried to remove the Simplex Noise function, it is a bit faster then but still not really good.
    I also used a higher viewdistance (100), it takes quite some time at the beginning cause he creates all gameobjects for all chunks you can see in that 100x100x100 view and then generate the mesh. After that its faster so its okay if you just load for a while at the beginning to generate the world.

    But when it tries to replace the next row for you (beezir doesnt create new chunks, he just moves the gameobject from the row behind you to the row infront of you and recreate the mesh), it slows down a lot.

    So you use 16x16x128 chunks right? What do you do to create the voxels per chunk, do you have 3 loops for X, Y and Z size and check if there is a cube, if yes, create the mesh?
    That's what beezir does too (and i did that in a previous script too) but that's the point why it's so slow. He also has to check if the current cube is touching another one, if not, you need to create the faces.

    What exactly is your optimize problem? Maybe i can help, it seems to work nice in beezirs exaple too.
     
  41. Taintspore

    Taintspore

    Joined:
    Sep 29, 2009
    Posts:
    185
    Without a way to optimize the voxel representation (say octrees for example) we're stuck with loops. I'm sure there,s a way to use a kernel or mask to change the data quickly but then you need a good way to modify the mesh without remaking the whole thing.

    So yeah, three loops, check neighbours, add triangles to mesh. The mesh generation isn't what's slow with mine, the noise takes longer, possibly the chunk init cost, haven't tried generating a 'new' chunk (but reusing the chunk object yet) so I'm not sure. My code takes between 10 and 25 seconds to generate a 16x16x1 chunk world with 16x16x128 chunks. Can't remember offhand how long for 16x16x256 chunks. If I decrease the depth of each chunk but add more chunk layers the init time is longer and the fps drops. Fps is between 70 and 1000 fps depending on how many chunks are in view, average is between 150 and 200.

    The problem I'm having with optimize is that it doesn't seem to do anything. I create three vertices per triangle (extremely naive mesh construction) and after calling optimize I'm left with the same triangle/vertex ratio. I think it might be that there are no strips to begin with but I'm not sure how to fix that. I tried manually creating proper triangle strips for the top faces but I got an error stating that the numbers of triangles and verts were out of whack.
     
  42. CaptainKiyaku

    CaptainKiyaku

    Joined:
    Feb 8, 2009
    Posts:
    324
    Hm but the code from Beezir just replaces the chunk too and then recreate it's mesh.
    It would be great if we can only change voxels that actually changed, but that's probably too hard and maybe not even necessary.

    So in your example you rebuild the mesh completely too right? (like make a mesh.Clear() and then recreate the faces?)

    Would you mind sharing a webplayer so i could see the loading speed?

    I really don't know how to get it faster then. All that code does is check it's blocktype, and if its not empty, check the neighbours and recreate the mesh. Maybe one of the coroutines to check the neighbours is slow. I dunno, hm.

    About the optimizing, hm i haven't checked if beziers example changes the vertices count after optimizing. Except it creates only 4 vertices for 2 triangles (you need at least 4 for each voxels face anyway to get a proper UV setting in the end).
     
  43. Taintspore

    Taintspore

    Joined:
    Sep 29, 2009
    Posts:
    185
    Bah, I just realized what I was doing wrong on the mesh creation. That's what I get for keeping code around from when I just wanted to get something on screen (first time doing mesh creation manually). I only create four vector3's, but I add two of them twice. Stupid moment :twisted:

    I'll try to stick a webplayer up tomorrow. If I have time I'll put up a few based on chunk size/number.

    I think I might have a memory leak somewhere but I'm not sure, haven't gone looking/or calculated min/max mem usage yet. As a warning, it's very green. Hard to read the terrain so I stuck a head lamp on the player.

    I'll have a better idea about what exactly is causing the load times once I put 'infinite' terrain in.
     
  44. CaptainKiyaku

    CaptainKiyaku

    Joined:
    Feb 8, 2009
    Posts:
    324
    Hehe yeah i do mistakes like that sometimes too. It's also the first time for me to create procedural meshes. Quite interesting.

    Okay cool can't wait for the webplayer. It doesn't matter how it looks like, just would like to see how fast new chunks load for you.
    I am trying to find the part that slows the mesh creation down so much. But so far i couldn't find anything, hm. Profiler says 93% of the slow down goes to the regenerateMesh() function so i guess its just the loop cycle? I dunno, if it runs smooth for you, there must be something wrong.

    If you ever could show your mesh creation part, that would be awesome so maybe i can see a difference.
     
  45. Taintspore

    Taintspore

    Joined:
    Sep 29, 2009
    Posts:
    185
    sure, it's long and horrendous to look at but here it is, each chunk knows it's neighbours (top, bottom, left...)
    AddVertexTriangleIndex just adds the necessary stuff to the mesh lists. It's not named well because it just used to add the vertex and index, but that made everything flat pink so I added normals and such but never changed the name. Time for a refactor.
    Webplayers should hopefully be up tomorrow afternoon sometime.

    Code (csharp):
    1. public void CreateMesh(){
    2.  
    3.         this.InitLists();
    4.  
    5.         for(int x = (int)this.size.x - 1; x >= 0; x--) {
    6.  
    7.             for(int y = (int) this.size.y - 1; y >=0 ; y--) {
    8.  
    9.                 for(int z = (int)this.size.z - 1; z >= 0; z--) {
    10.  
    11.                     //process non-empty voxels
    12.  
    13.                     if(this.data[x,y,z] != 0) {
    14.  
    15.                         float h = 0.5f;
    16.  
    17.                         /**                      
    18.  
    19.                          *         VERTEX NUMBER/NAMES                      
    20.                          *                                                    
    21.                          *                     1(LUF)         2(RUF)
    22.                          *                       *--------------*
    23.                          *                      /|             /|
    24.                          *   y           8(LUB)/ |      7(RUB)/ |
    25.                          *   |                *--------------*  |  
    26.                          *   |                |  |           |  |
    27.                          *   |_____ x         |  |           |  |    
    28.                          *   /                |  |4(LLF)     |  |
    29.                          *  /                 |  *-----------|--*3(RLF)              
    30.                          *   -z               | /            | /    
    31.                          *                    |/             |/        
    32.                          *              5(LLB)*--------------*6(RLB)
    33.                          *                                      
    34.                          *       Unity likes Clockwise vertex winding          
    35.                          *    
    36.                          * */
    37.  
    38.                         Vector3 LUF = new Vector3(x - this.size.x/2 - h, y + h, z - this.size.z/2 + h);
    39.  
    40.                         Vector3 LUB = new Vector3(x - this.size.x/2 - h, y + h, z - this.size.z/2 - h);
    41.  
    42.                         Vector3 LLF = new Vector3(x - this.size.x/2 - h, y - h, z - this.size.z/2 + h);
    43.  
    44.                         Vector3 LLB = new Vector3(x - this.size.x/2 - h, y - h, z - this.size.z/2 - h);
    45.  
    46.                         Vector3 RUF = new Vector3(x - this.size.x/2 + h, y + h, z - this.size.z/2 + h);
    47.  
    48.                         Vector3 RUB = new Vector3(x - this.size.x/2 + h, y + h, z - this.size.z/2 - h);
    49.  
    50.                         Vector3 RLF = new Vector3(x - this.size.x/2 + h, y - h, z - this.size.z/2 + h);
    51.  
    52.                         Vector3 RLB = new Vector3(x - this.size.x/2 + h, y - h, z - this.size.z/2 - h);
    53.  
    54.                        
    55.  
    56.                         Vector2 zeroOne = new Vector2(0,1);
    57.  
    58.                         Vector2 zeroZero = new Vector2(0,0);
    59.  
    60.                         Vector2 oneOne = new Vector2(1,1);
    61.  
    62.                         Vector2 oneZero = new Vector2(1,0);
    63.  
    64.  
    65.                         //left side
    66.  
    67.                         if(x == 0) {
    68.  
    69.                             if(this.left != null  (this.left.GetDensityAt((int) this.size.x - 1, y, z) == 0)) {
    70.  
    71.                                 this.AddVertexAndTriangleIndex(LUB, oneOne, Vector3.left);
    72.  
    73.                                 this.AddVertexAndTriangleIndex(LLF, zeroZero, Vector3.left);
    74.  
    75.                                 this.AddVertexAndTriangleIndex(LUF, zeroOne, Vector3.left);
    76.  
    77.                            
    78.  
    79.                                 this.AddVertexAndTriangleIndex(LLB, oneZero, Vector3.left);
    80.  
    81.                                 this.AddVertexAndTriangleIndex(LLF, zeroZero, Vector3.left);
    82.  
    83.                                 this.AddVertexAndTriangleIndex(LUB, oneOne, Vector3.left);
    84.  
    85.                             }
    86.  
    87.                         }
    88.  
    89.                         else if(this.data[x - 1, y, z] == 0) {
    90.  
    91.                             this.AddVertexAndTriangleIndex(LUB, oneOne, Vector3.left);
    92.  
    93.                             this.AddVertexAndTriangleIndex(LLF, zeroZero, Vector3.left);
    94.  
    95.                             this.AddVertexAndTriangleIndex(LUF, zeroOne, Vector3.left);
    96.  
    97.                        
    98.  
    99.                             this.AddVertexAndTriangleIndex(LLB, oneZero, Vector3.left);
    100.  
    101.                             this.AddVertexAndTriangleIndex(LLF, zeroZero, Vector3.left);
    102.  
    103.                             this.AddVertexAndTriangleIndex(LUB, oneOne, Vector3.left);
    104.  
    105.                         }
    106.                         //right side
    107.  
    108.                         if(x == this.size.x - 1) {
    109.  
    110.                             if(this.right != null  (this.right.GetDensityAt(0, y, z) == 0)) {
    111.  
    112.                                 this.AddVertexAndTriangleIndex(RUB, zeroOne, Vector3.right);
    113.  
    114.                                 this.AddVertexAndTriangleIndex(RUF, oneOne, Vector3.right);
    115.  
    116.                                 this.AddVertexAndTriangleIndex(RLF, oneZero, Vector3.right);
    117.  
    118.                                
    119.  
    120.                                 this.AddVertexAndTriangleIndex(RLF, oneZero, Vector3.right);
    121.  
    122.                                 this.AddVertexAndTriangleIndex(RLB, zeroZero, Vector3.right);
    123.  
    124.                                 this.AddVertexAndTriangleIndex(RUB, zeroOne, Vector3.right);
    125.  
    126.                             }
    127.  
    128.                         }
    129.  
    130.                         else if(this.data[x + 1, y, z] == 0) {
    131.  
    132.                             this.AddVertexAndTriangleIndex(RUB, zeroOne, Vector3.right);
    133.  
    134.                             this.AddVertexAndTriangleIndex(RUF, oneOne, Vector3.right);
    135.  
    136.                             this.AddVertexAndTriangleIndex(RLF, oneZero, Vector3.right);
    137.  
    138.                            
    139.  
    140.                             this.AddVertexAndTriangleIndex(RLF, oneZero, Vector3.right);
    141.  
    142.                             this.AddVertexAndTriangleIndex(RLB, zeroZero, Vector3.right);
    143.  
    144.                             this.AddVertexAndTriangleIndex(RUB, zeroOne, Vector3.right);
    145.  
    146.                         }
    147.  
    148.                        
    149.  
    150.                         //bottom
    151.  
    152.                         if(y == 0) {
    153.  
    154.                             if(this.bottom != null  this.bottom.GetDensityAt(x, (int) this.size.y - 1, z) == 0) {
    155.  
    156.                                 this.AddVertexAndTriangleIndex(LLF, zeroZero, Vector3.down);
    157.  
    158.                                 this.AddVertexAndTriangleIndex(LLB, zeroOne, Vector3.down);
    159.  
    160.                                 this.AddVertexAndTriangleIndex(RLB, oneOne, Vector3.down);
    161.  
    162.                                
    163.  
    164.                                 this.AddVertexAndTriangleIndex(RLB, oneOne, Vector3.down);
    165.  
    166.                                 this.AddVertexAndTriangleIndex(RLF, oneZero, Vector3.down);
    167.  
    168.                                 this.AddVertexAndTriangleIndex(LLF, zeroZero, Vector3.down);
    169.  
    170.                             }
    171.  
    172.                         }
    173.  
    174.                         else if(this.data[x, y - 1, z] == 0) {
    175.  
    176.                             this.AddVertexAndTriangleIndex(LLF, zeroZero, Vector3.down);
    177.  
    178.                             this.AddVertexAndTriangleIndex(LLB, zeroOne, Vector3.down);
    179.  
    180.                             this.AddVertexAndTriangleIndex(RLB, oneOne, Vector3.down);
    181.  
    182.                            
    183.  
    184.                             this.AddVertexAndTriangleIndex(RLB, oneOne, Vector3.down);
    185.  
    186.                             this.AddVertexAndTriangleIndex(RLF, oneZero, Vector3.down);
    187.  
    188.                             this.AddVertexAndTriangleIndex(LLF, zeroZero, Vector3.down);
    189.  
    190.                         }
    191.  
    192.                        
    193.  
    194.                         //top
    195.  
    196.                         if(y == this.size.y - 1) {
    197.  
    198.                             if( this.top == null || this.top.GetDensityAt(x, 0, z) == 0) {
    199.  
    200.                                
    201.  
    202.                                
    203.  
    204.                                 this.AddVertexAndTriangleIndex(LUF, zeroOne, Vector3.up);
    205.  
    206.                                 this.AddVertexAndTriangleIndex(RUF, oneOne, Vector3.up);
    207.  
    208.                                 this.AddVertexAndTriangleIndex(RUB, oneZero, Vector3.up);
    209.  
    210.                                
    211.  
    212.                                
    213.  
    214.                                 this.AddVertexAndTriangleIndex(RUB, oneZero, Vector3.up);
    215.  
    216.                                 this.AddVertexAndTriangleIndex(LUB, zeroZero, Vector3.up);
    217.  
    218.                                 this.AddVertexAndTriangleIndex(LUF, zeroOne, Vector3.up);
    219.  
    220.                             }
    221.  
    222.                         }
    223.  
    224.                         else if (this.data[x, y + 1, z] == 0) {
    225.  
    226.                             this.AddVertexAndTriangleIndex(LUF, zeroOne, Vector3.up);
    227.  
    228.                             this.AddVertexAndTriangleIndex(RUF, oneOne, Vector3.up);
    229.  
    230.                             this.AddVertexAndTriangleIndex(RUB, oneZero, Vector3.up);
    231.  
    232.                            
    233.  
    234.                             this.AddVertexAndTriangleIndex(RUB, oneZero, Vector3.up);
    235.  
    236.                             this.AddVertexAndTriangleIndex(LUB, zeroZero, Vector3.up);
    237.  
    238.                             this.AddVertexAndTriangleIndex(LUF, zeroOne, Vector3.up);
    239.  
    240.                         }
    241.  
    242.                         //Back
    243.  
    244.                         if(z == 0) {
    245.  
    246.                             if(this.back != null  this.back.GetDensityAt(x, y, (int) this.size.z - 1) == 0) {
    247.  
    248.                                 this.AddVertexAndTriangleIndex(LLB, zeroZero, Vector3.back);
    249.  
    250.                                 this.AddVertexAndTriangleIndex(LUB, zeroOne, Vector3.back);
    251.  
    252.                                 this.AddVertexAndTriangleIndex(RUB, oneOne, Vector3.back);
    253.  
    254.                                    
    255.  
    256.                                 this.AddVertexAndTriangleIndex(RUB, oneOne, Vector3.back);
    257.  
    258.                                 this.AddVertexAndTriangleIndex(RLB, oneZero, Vector3.back);
    259.  
    260.                                 this.AddVertexAndTriangleIndex(LLB, zeroZero, Vector3.back);
    261.  
    262.                             }
    263.  
    264.                         }
    265.  
    266.                         else if(this.data[x, y, z - 1] == 0) {
    267.  
    268.                             this.AddVertexAndTriangleIndex(LLB, zeroZero, Vector3.back);
    269.  
    270.                             this.AddVertexAndTriangleIndex(LUB, zeroOne, Vector3.back);
    271.  
    272.                             this.AddVertexAndTriangleIndex(RUB, oneOne, Vector3.back);
    273.  
    274.                                
    275.  
    276.                             this.AddVertexAndTriangleIndex(RUB, oneOne, Vector3.back);
    277.  
    278.                             this.AddVertexAndTriangleIndex(RLB, oneZero, Vector3.back);
    279.  
    280.                             this.AddVertexAndTriangleIndex(LLB, zeroZero, Vector3.back);
    281.  
    282.                         }
    283.  
    284.                        
    285.  
    286.                        
    287.  
    288.                         //FRONT
    289.  
    290.                         if(z == this.size.z - 1) {
    291.  
    292.                             if(this.front != null  this.front.GetDensityAt(x, y, 0) == 0) {
    293.  
    294.                                 this.AddVertexAndTriangleIndex(LUF, oneOne, Vector3.forward);
    295.  
    296.                                 this.AddVertexAndTriangleIndex(LLF, oneZero, Vector3.forward);
    297.  
    298.                                 this.AddVertexAndTriangleIndex(RUF, zeroOne, Vector3.forward);
    299.  
    300.                                    
    301.  
    302.                                 this.AddVertexAndTriangleIndex(LLF, oneZero, Vector3.forward);
    303.  
    304.                                 this.AddVertexAndTriangleIndex(RLF, zeroZero, Vector3.forward);
    305.  
    306.                                 this.AddVertexAndTriangleIndex(RUF, zeroOne, Vector3.forward);
    307.  
    308.                             }
    309.  
    310.                         }
    311.  
    312.                         else if (this.data[x, y, z + 1] == 0) {
    313.  
    314.                             this.AddVertexAndTriangleIndex(LUF, oneOne, Vector3.forward);
    315.  
    316.                             this.AddVertexAndTriangleIndex(LLF, oneZero, Vector3.forward);
    317.  
    318.                             this.AddVertexAndTriangleIndex(RUF, zeroOne, Vector3.forward);
    319.  
    320.                                
    321.  
    322.                             this.AddVertexAndTriangleIndex(LLF, oneZero, Vector3.forward);
    323.  
    324.                             this.AddVertexAndTriangleIndex(RLF, zeroZero, Vector3.forward);
    325.  
    326.                             this.AddVertexAndTriangleIndex(RUF, zeroOne, Vector3.forward);
    327.  
    328.                         }
    329.  
    330.                     }
    331.  
    332.                 }
    333.  
    334.             }
    335.  
    336.         }
    337.  
    338.        
    339.  
    340.         MeshCollider mCollider = this.GetComponent<MeshCollider>();
    341.  
    342.         MeshFilter mFilter = this.GetComponent<MeshFilter>();
    343.  
    344.         mFilter.mesh.Clear();
    345.  
    346.         if(mCollider.sharedMesh != null) {
    347.  
    348.             mCollider.sharedMesh.Clear();
    349.  
    350.             mCollider.sharedMesh = null;   
    351.  
    352.         }
    353.  
    354.  
    355.  
    356.         //Destroy(mFilter.mesh);
    357.  
    358.         //Destroy(mCollider.sharedMesh);
    359.  
    360.        
    361.  
    362.         //mFilter.mesh = new Mesh();
    363.  
    364.        
    365.  
    366.         mFilter.mesh.vertices = this.vertices.ToArray();
    367.  
    368.         mFilter.mesh.uv = this.UVs.ToArray();
    369.  
    370.         mFilter.mesh.normals = this.normals.ToArray();
    371.  
    372.         mFilter.mesh.triangles = this.triangles.ToArray();
    373.  
    374.         mFilter.mesh.RecalculateNormals();
    375.  
    376.         mFilter.mesh.Optimize();
    377.  
    378.         mCollider.sharedMesh = mFilter.mesh;
    379.  
    380.  
    381.  
    382.        
    383.  
    384.     }
    385.  
    386.    
     
  46. Taintspore

    Taintspore

    Joined:
    Sep 29, 2009
    Posts:
    185
    oh man, hit the char limit and the formatting on the code is bad. Tried to fix it but it didn't work, looked fine in preview though...
     
  47. CaptainKiyaku

    CaptainKiyaku

    Joined:
    Feb 8, 2009
    Posts:
    324
    Awesome thanks!
    I love that cube sketch hehe. I will look through and see if i can change the beezir code somehow to make it load fast too.

    Yes it has a space between each line but i think its not really cut off at the end? Well i guess the main part is there anyway.
     
  48. Taintspore

    Taintspore

    Joined:
    Sep 29, 2009
    Posts:
    185
    No problem. It's missing the helper function but all that does is add the verts, tris, normals, and uvs to there respective lists. (ie List<Vector3>, etc) the addition order is wonky but they go in properly, just not, er, nicely. I,m pretty sure it's the chunk init (lists, finding chunk components, noise) that lags the load for me, the meshes generate pretty quickly.

    Lol, everyone that's seen the code likes that thing.
     
  49. CaptainKiyaku

    CaptainKiyaku

    Joined:
    Feb 8, 2009
    Posts:
    324
    Hm i can't see a big difference, i guess the only difference is that you split it into 6 lists. That might be helpful already cause you only have to create half the polygons (only 3 faces) at a time.

    This is my version from beezirs code:

    Code (csharp):
    1.  
    2.  
    3.     void regenerateMesh()
    4.     {
    5.         offset = transform.position;
    6.         Mesh subMesh = this.GetComponent<MeshFilter>().mesh;
    7.         MeshCollider mc = gameObject.GetComponent<MeshCollider>();
    8.        
    9.         List<int> indices = new List<int>();
    10.         List<Vector2> uvs = new List<Vector2>();
    11.         List<Vector3> verts = new List<Vector3>();
    12.         List<Color> colors = new List<Color>();
    13.  
    14.         subMesh.Clear();
    15.  
    16.         int curVert = 0;
    17.         int density = 0;
    18.         int[] faceIDs;
    19.  
    20.         Vector3 curVec = new Vector3(0, 0, 0);
    21.  
    22.         for (int x = 0; x < chunkSize.x; x++)
    23.         {
    24.             for (int y = 0; y < chunkSize.y; y++)
    25.             {
    26.                 for (int z = 0; z < chunkSize.z; z++)
    27.                 {
    28.                     curVec.x = x;
    29.                     curVec.y = y;
    30.                     curVec.z = z;
    31.  
    32.                     density = getTerrainValue(curVec);
    33.  
    34.                     m_tmpdata[(int)curVec.x, (int)curVec.y, (int)curVec.z] = 0;
    35.  
    36.                     if (density == 0 )
    37.                         continue;
    38.  
    39.                     faceIDs = GetBlockID(new Vector3(x, y + 1, z), new Vector3(x, y, z), density);
    40.  
    41.                     curVec.z--;
    42.  
    43.                     // Back
    44.                     if (getTerrainValue(curVec) == 0)
    45.                     {
    46.                         verts.Add(new Vector3(x, y, z));
    47.                         verts.Add(new Vector3(x + 1, y + 1, z));
    48.                         verts.Add(new Vector3(x + 1, y, z));
    49.                         verts.Add(new Vector3(x, y + 1, z));
    50.  
    51.                         addUV(faceIDs[0], ref uvs);
    52.  
    53.                         indices.Add(curVert);
    54.                         indices.Add(curVert + 1);
    55.                         indices.Add(curVert + 2);
    56.  
    57.                         indices.Add(curVert);
    58.                         indices.Add(curVert + 3);
    59.                         indices.Add(curVert + 1);
    60.  
    61.                         curVert += 4;
    62.                     }
    63.  
    64.                     curVec.z += 2;
    65.                    
    66.                     // Front
    67.                     if (getTerrainValue(curVec) == 0)
    68.                     {
    69.                         verts.Add(new Vector3(x, y, z + 1));
    70.                         verts.Add(new Vector3(x + 1, y + 1, z + 1));
    71.                         verts.Add(new Vector3(x + 1, y, z + 1));
    72.                         verts.Add(new Vector3(x, y + 1, z + 1));
    73.  
    74.                         addUV(faceIDs[1], ref uvs);
    75.                        
    76.                         indices.Add(curVert);
    77.                         indices.Add(curVert + 2);
    78.                         indices.Add(curVert + 1);
    79.  
    80.                         indices.Add(curVert);
    81.                         indices.Add(curVert + 1);
    82.                         indices.Add(curVert + 3);
    83.  
    84.                         curVert += 4;
    85.                     }
    86.                     curVec.z = z;
    87.  
    88.                     curVec.y--;
    89.                    
    90.                     // Bottom
    91.                     if (getTerrainValue(curVec) == 0)
    92.                     {
    93.                         verts.Add(new Vector3(x, y, z));
    94.                         verts.Add(new Vector3(x + 1, y, z + 1));
    95.                         verts.Add(new Vector3(x + 1, y, z));
    96.                         verts.Add(new Vector3(x, y, z + 1));
    97.  
    98.                         addUV(faceIDs[2], ref uvs);
    99.  
    100.                         indices.Add(curVert);
    101.                         indices.Add(curVert + 2);
    102.                         indices.Add(curVert + 1);
    103.  
    104.                         indices.Add(curVert);
    105.                         indices.Add(curVert + 1);
    106.                         indices.Add(curVert + 3);
    107.  
    108.                         curVert += 4;
    109.                     }
    110.  
    111.                     curVec.y += 2;
    112.                     //shade=0.5f;
    113.                    
    114.                     // Top
    115.                     if (getTerrainValue(curVec) == 0)
    116.                     {
    117.                         verts.Add(new Vector3(x, y + 1, z));
    118.                         verts.Add(new Vector3(x + 1, y + 1, z + 1));
    119.                         verts.Add(new Vector3(x + 1, y + 1, z));
    120.                         verts.Add(new Vector3(x, y + 1, z + 1));
    121.  
    122.                         addUV(faceIDs[3], ref uvs);
    123.  
    124.                         indices.Add(curVert);
    125.                         indices.Add(curVert + 1);
    126.                         indices.Add(curVert + 2);
    127.  
    128.                         indices.Add(curVert);
    129.                         indices.Add(curVert + 3);
    130.                         indices.Add(curVert + 1);
    131.  
    132.                         curVert += 4;
    133.                     }
    134.  
    135.                     curVec.y = y;
    136.                     //shade = 0.45f;
    137.                     curVec.x--;
    138.                    
    139.                     // Left
    140.                     if (getTerrainValue(curVec) == 0)
    141.                     {
    142.                         verts.Add(new Vector3(x, y, z + 1));
    143.                         verts.Add(new Vector3(x, y + 1, z));
    144.                         verts.Add(new Vector3(x, y, z));
    145.                         verts.Add(new Vector3(x, y + 1, z + 1));
    146.  
    147.                         addUV(faceIDs[4], ref uvs);
    148.  
    149.                         indices.Add(curVert);
    150.                         indices.Add(curVert + 3);
    151.                         indices.Add(curVert + 1);
    152.  
    153.                         indices.Add(curVert);
    154.                         indices.Add(curVert + 1);
    155.                         indices.Add(curVert + 2);
    156.  
    157.                         curVert += 4;
    158.                     }
    159.                     curVec.x += 2;
    160.                    
    161.                     // Right
    162.                     if (getTerrainValue(curVec) == 0)
    163.                     {
    164.                         verts.Add(new Vector3(x + 1, y, z + 1));
    165.                         verts.Add(new Vector3(x + 1, y + 1, z));
    166.                         verts.Add(new Vector3(x + 1, y, z));
    167.                         verts.Add(new Vector3(x + 1, y + 1, z + 1));
    168.  
    169.                         addUV(faceIDs[5], ref uvs);
    170.  
    171.                         indices.Add(curVert);
    172.                         indices.Add(curVert + 1);
    173.                         indices.Add(curVert + 3);
    174.  
    175.                         indices.Add(curVert);
    176.                         indices.Add(curVert + 2);
    177.                         indices.Add(curVert + 1);
    178.  
    179.                         curVert += 4;
    180.                     }
    181.                 }
    182.             }
    183.         }
    184.  
    185.         subMesh.vertices = verts.ToArray();
    186.         subMesh.triangles = indices.ToArray();
    187.         subMesh.uv = uvs.ToArray();
    188.         //subMesh.colors = colors.ToArray();
    189.  
    190.         //subMesh.Optimize();
    191.         subMesh.RecalculateNormals();
    192.        
    193.         //if (mc.sharedMesh == null) mc.sharedMesh = subMesh;
    194.         mc.sharedMesh = new Mesh();
    195.         mc.sharedMesh = subMesh;
    196.         //mc.sharedMesh.Optimize();
    197.     }
    198.  
    addUV just checks the block ID and adds the UV for the tilemap. But that's not slowing down.
    I think i will try to use 6 lists as well and see if that works better. Time to rewrite everything lol.
    Or maybe i can just test it by only creating 3 out of 6 faces, just to see if the loop is better then.
     
  50. Taintspore

    Taintspore

    Joined:
    Sep 29, 2009
    Posts:
    185
    Uh oh, what version did I post?

    Hrmmm, in that one I only have one list for each part of the mesh (one for normals, one for verts, etc). I tried the six lists (one per side direction) but it caused the FPS to drop. I thought that was weird because I had it set to not render sides that were facing away. I still have that code kicking around somewhere, it wasn't that different, just had a list of six lists of vector3's or 2's or ints.

    Still not sure why it performed worse but I have a feeling it has to do with the fact that each side mesh was in a child object.
     
Thread Status:
Not open for further replies.