fenomas / noa

Experimental voxel game engine.
MIT License
616 stars 91 forks source link

Rotate blocks (0, 90, 180, 270 degrees) #33

Closed Nesh108 closed 4 years ago

Nesh108 commented 7 years ago

Hey @andyhall,

Is there any way to be able to rotate a specific block (either before or after being placed) by 0, 90, 180, 270 degrees (x, y or z)?

The idea would be to reuse blocks without having to re-create them X times just to add different rotations.

fenomas commented 7 years ago

Hey - this is not in the engine at present. I had thought about it, and there is room in the internals to assign several bits of "variation" per voxel like this, but I haven't thought out all the details.

For example, I'm not sure how the client would specify voxel variations at chunk-creation time. The technically straightforward way would be to have the client pack variations into the high bits of the int values, same as the engine does, but ideally the client wasn't supposed to need to know implementation details like that. But the alternatives wouldn't be very clean either.

I think it could be wrangled, but it would take some thinking and at the moment it's not super-high on my list, sorry! In my content, I'm basically only using block rotations for "custom blocks" (where the voxel has a non-block shape), and for those I apply the rotation in the custom block handlers at block creation.

EliteAsian123 commented 5 years ago

I found a way to make static properties like for example the hardness for a block, but it'll be cool if we had dynamic variation, like the direction of a block. This variation will be very handy if added.

Heath123 commented 4 years ago

I think the way Minecraft does it now from 1.13 onwards is to register each possible variation of each block as a "blockstate". Maybe you could edit constants.js to use the the variation as extra block ID bits and register each like a separate block? That seems to be the way Minecraft does it now.

fenomas commented 4 years ago

Yes, that's the way something like this would properly be done - by using 2 bits in the voxel ID as state data, and then having the mesher look at that state when building terrain. However it's complicated how this could be implemented API-wise: the game client gives the engine plain voxel IDs, and the engine does meshing shortly afterwards, so there would need to be some new way of letting the client pass in block state flags during the interim. Anyway this is possible but I don't have plans to do it very soon!

Heath123 commented 4 years ago

Minecraft 1.13+ registers each possible rotation/state as a separate ID, so there can be any number of states and no redundant bits, which can already be done in noa. Minecraft 1.12 and below had a fixed 4 bits of data for each block, like noa has now.

fenomas commented 4 years ago

Ah sorry, I misunderstood you before. That's really useful to know, thanks! I don't suppose you know why they changed the format?

(To be clear, noa doesn't do anything with the extra bits of voxel state, it just sets aside space for them to be used someday. But it sounds like I should forget that and increase the maximum voxel ID instead.)

Patbox commented 4 years ago

They run out of free block ids if I remember correctly.

Heath123 commented 4 years ago

Yep, doing it that way is more compact so it frees up IDs. It also lets them use more than 16 combinations, which they probably needed to store waterlogging data for Update Aquatic (you need to store water data and block data).

Heath123 commented 4 years ago

https://minecraft.gamepedia.com/Block_states#Daylight_Detector for example has over 16 possible states

Heath123 commented 4 years ago

If you did want to move to a blockstate system, I think you should have a way of defining a JS object with values for variation (like the ones on the wiki page), and noa should register all of the combinations, and also you should be able to set fixed values (like "is solid") that no extra states are created for (that's just my opinion though based on how I've seen that Minecraft does it).

Heath123 commented 4 years ago

But that's quite a big change so it might be better for me to try doing that in a fork or making a wrapper or something (I think it should be optional at least, even if it does end up in noa)

fenomas commented 4 years ago

@Patbox @Heath123 Thanks for all the info, it's really helpful.

Personally I think it's definitely best to just add those 4 bits to the voxel ID, which would raise the limit on unique voxels to 8191.

That said, I imagine minecraft probably has more IDs than that nowadays? An added option would be to start using Uint32Array for voxel data, effectively allowing unlimited voxel IDs. I haven't tried it, but I'd imagine this would have little effect on CPU performance, but noticeably increase memory usage for medium to large draw distances.

Heath123 commented 4 years ago

@Patbox @Heath123 Thanks for all the info, it's really helpful.

Personally I think it's definitely best to just add those 4 bits to the voxel ID, which would raise the limit on unique voxels to 8191.

That said, I imagine minecraft probably has more IDs than that nowadays? An added option would be to start using Uint32Array for voxel data, effectively allowing unlimited voxel IDs. I haven't tried it, but I'd imagine this would have little effect on CPU performance, but noticeably increase memory usage for medium to large draw distances.

I think Minecraft has 4096 blockstates available at the moment (they're not all used). For what I'm using it for I'd need to eventually store lighting data too, but that only needs to be done for non-solid blocks and there are probably enough spare states for that (or I could use a separate array).

Heath123 commented 4 years ago

There's information about how Minecraft does it here: https://wiki.vg/Chunk_Format#Global_and_section_palettes This is a list of block states in 1.15.2: https://pokechu22.github.io/Burger/1.15.2.html

Heath123 commented 4 years ago

Never mind, there are 11,337 in Minecraft (wow)

Heath123 commented 4 years ago

Would registering that many blocks cause lag?

Heath123 commented 4 years ago

I don't really know too much about JS and optimising it, but can one of the solidity, opacity or object marker bits be sacrificed and looked up based on the ID? That could get the voxel IDs up to 16,384 which is probably enough for most things because it's the same as Minecraft's block states. Is that feasible?

fenomas commented 4 years ago

Hi, this is a really good idea. So good in fact, that I'm hammering a bit trying out just getting rid of the packed bit flags, and storing voxel IDs as a plain uint16, and doing lookups everywhere the bit flag was previously used. That would allow 65k voxel IDs, and also simplify several bits of the engine somewhat. The performance hit, at first blush, seems to be about 10% slower terrain meshing, and negligible elsewhere.

10% slower meshing isn't ideal, but meshing is fast enough that it shouldn't really cause FPS drops, just lower overall throughput on world generation. I'm going to hammer a bit more but I may just check this in and hope for the best.

fenomas commented 4 years ago

Hi, thanks for the input here - I went ahead and merged this into #develop.

Changes:

Impacts:

That should be it, no public APIs changed, and no other performance impacts I found. LMK if you have issues, thanks!