Minecraft name:
Sloimay
Whatâs a thing you have made which demonstrates sufficient engineering knowledge?:
I made a âterrain generatorâ using redstone.
I also made a 5hz Conics Graphing Calculator, and a 5hz Image-rotator, to name a few, but I wonât talk about those in this application.
What engineering work went into designing this device?:
I am going to talk about what engineering work went into designing the device, so I wonât explain in deep details which algorithms are used past just perlin noise and whatâs needed for redstone.
The first intuition I had for such a project, would be to have a function f(x, y) which returns the height of the terrain at that XY column, and then print the terrain with a 3d printer using flying machines.
F(x, z) = height sounded familiar with noise functions, so I looked up how to compute perlin noise (a type of smooth random for people that donât know) on YouTube and found plenty of documentation, it was exactly what I needed.
Hereâs what I learnt:
To setup perlin noise in 2d (2 coords in, a height out), here are the steps:
- Chunk your 2d plane in even squares
- Assign random normalized 2d vectors to each corner of those chunks
To pool perlin noise at a certain point in the space here what you need to do: - You get in which chunk the X,Y coordinates are in
- Draw 4 vectors (in green) from the corners to the point
- Take the dot product between each green vector and the red random corner vector associated with it to get a value per corner of the chunk.
- Now bi-cosine interpolate the 4 values in the corner with the coordinates within the chunk of X,Y, you have your final perlin noise value. (You can imagine bi-cosine interpolation as âtake the 4 corners, and smooth their values out to create smooth heights throughout the whole chunkâ)
After learning about perlin noise, I quickly coded it up in python to see if itâd work and rendered a world using my python package: MCSchematic (mcschematic ¡ PyPI) (shameless plug)
Now if this was a bit hard to follow, Figure 3 is almost what the algorithm is, just modified a bit so that itâs easier to do with redstone.
Good segway to: âwhat changed from software to redstone implementationâ?:
- The bi-cosine interpolation at the end has been replaced with a less pretty but more efficient algorithm: bilinear interpolation (Bilinear interpolation - Wikipedia)
Bi-cosine interpolation requires 3 cosine interpolation calculations to be computer, whereas bi-linear interpolation needs 3 linear interpolations to be computer, which is way easier to do with redstone ( linear_interpolation(a, b, t) = a + t*(b-a) ); as linear interpolation (lerp) needs a multiplication and addition, whereas cosine interpolation needs a cosine unit, and multiple multiplications. - Another thing I changed so that it was easier to do with redstone, are the generation of the random corner vectors of each chunk. Instead of storing 2 16-bit values per corner, I opted for an easier solution (which is also how perlin noise is usually implemented) which would be to only have 16 possible normalized vectors per corner and store a 4bit number into each corner, and decode which number corresponds to which of the 16 vectors using a LUT.
I also coded that new version up in python to see how it would look like. This implementation didnât look too different from the original algorithm. Prettiness was lost due to the switch from bi-cosine to bilinear interpolation but honestly, itâs redstone, so it looked good enough for me XD. Iâm glad I took that decision.
It was time to make it with redstone, so I defined my constraints a bit. I opted for a 64x32x64 world area, so the world height would only be 5 bits. The calculations required fractional math as well so I needed precision after radix points, so I opted for the 16bit 8.8 fixed point format, for decent precision before and after the radix point, while it being expandable to 2^3 times bigger (5 bits to 8 bits after radix) calculations and printer area size.
I first started with making the adders and subtractors, then made a 16bit sequential multiplier using 2t looping CSAs (FEARLESS_'s one, what a weird magnificent thing). After that I was able to make a âvector dot productâ circuit, and a âlinear interpolationâ circuit. Next was the âbilinear interpolationâ machine which is basically 3 âlerpâ machines put next to one another.
The computation components are finished, the last step was to make the âcorner vector pool-erâ which will make us able to pool the corner vectors of the chunk the coordinates we want the height of are in. It was done with a special type of 8bit RAM which can only be written to using shift registers and a data stream, and read randomly. That type of RAM was a good fit with my build, as we can split a byte into 2 nibbles, which is very convenient. The âdata stream writingâ was also better suited, as the corner vectors of the chunks are randomly generated (using hopper-dropper RNG), order doesnât matter, so I do not need random-writing, only sequential writing.
Now all we now needed to do was to assemble the components together to create what Figure 3 is saying. And boom, perlin noise compooper :DDDDD (I made my components in a very user-friendly drag-and-drop way, which made control-wire hell very quick to go through.)
Now letâs talk about 3D Printing a little bit:
The printer is a 3D printer that prints XZ columns per XZ columns over an area of 64x64. Each column is 32 blocks tall. The printer will first build the column at 0,0, then 0,1, then 0,2 etc⌠until 0,62, 0,63 when it actually moves by 1 in the X direction and starts printing the second Z-row of the 2d plane, 1,0, 1,1, etcâŚ
We input the height we want our column to be, the column-maker on the side builds the column (can be replaced with any blocks using more fancy redstone) then the flying machines pushes the column over to the desired X,Z location, using a first âZâ flying machine which pushes the column over to the correct Z-row of the world, and then an âXâ flying machine which will push the column over to the correct X coordinate of the world.
If it was kinda hard to understand I could always show it working over on screen-share (or on Nanoâs server that he let me build use).
The main problem of this part was working with flying machines and having to make 10k backups every minute in case something broke LOL, I definitely learnt how to deal with flying machine control logic and even though that was a pain, Iâm glad I did it.
Little addendum:
The flying machines were made for the project by MythicalPingu, but the control logic was up to me as well as the column generation. I ainât dipping my toes into slimestone LMFAO
I then connected the perlin noise compooper connected to the 3D printer by inputting the height it outputs to the column maker, which was our goal!
Some more final information:
- All perlin noise computations are done in fixed point 16bit 8.8 format
- It takes 5h on an overpowered server with /tick warp to render a world (so about 2 weeks in real time)
- The column generation uses concrete powder as âinkâ that needs to be refilled manually (canât generate those out of thin air) so instead of having a player having the refill them everytime a column is printer, I used a command block to set a new concrete powder block everytime one is used.
- It takes about uhhh, 2000 ticks to print a single column XD
Image/s and/or video/s of the device: