Next Broadcast
3d:18h:43m
Africa/Abidjan

Voxel landscaping in the browser

Mark Brand [ignism] GameDev DK30 Fall 2021 8 8

Description

Earlier this year I picked up react-three-fiber as my side-project playground, a react renderer for threejs. You can build/prototype stuff extremely fast, compared to vanilla threejs. Last couple of months I’ve been working a minecraft/townscaper crossover, but I ran into performance issues because I had a very naive approach (coding wise). It brought the project to a halt, since I had to start from scratch. I am trying to kickstart it again by making it a DK30 project.


DK30 complete! Try the final version for this DK30 here:

Voxel Landscaping


I will keep posting significant updates in the dk30 discord, but you can also follow me on twitter and instagram

Recent Updates

REFLECTION

30 days are over, it’s been a ride, a bumby ride. I want to spend this post reflecting on my process. I want to start to say that although the DK30 is finished, this project is not and I won’t stop working on it. I’m glad to say that since two weeks ago the project was in limbo. So let’s start there, why did I stop after making quite some progress?

As I started this DK30 I had a lot of energy and motivation. Everything was possible and I was quite sure I would be capable of tackling the big picture I sketched in my mind. I couldn’t wait to begin, started early and even made so much unexpected progress that my weekly planning was unusable. So I crossed that out and hacked along while also writing a new planning for each week.

But then again I finished my new planning again early and it was around that point that the project came to a halt for almost a week. The motivation wasn’t there any more and I didn’t know what to work on… even though there was lots of stuff to do! But that became apparent when I realised, there was too much stuff to do and the project suddenly became this huge monster. And I just floated around it, dreaming. When I made that realisation I was also able to put my feet back on the ground and cut the monster into smaller minions I could easily defeat: I made new goals for small problems that I would be able to solve in one working session. This worked out great and made me motivated again to work on it, that motivation hasn’t left me since.

So what did I learn from this? Now that the 30 days are over I need to keep writing goals to keep this project alive. To do that I am going to keep a Project Board on github. Where I can organize my todo-lists and put in perspective bigger (future) goals and smaller (immediate) goals.

All in all, I am quite proud on the state of the project at the moment. It’s hard to be proud of something you made yourself. It is easy to see its shortcomings, since in your head the project can be so much more. But I am going to embrace that energy and try to do this another month, and then another, and another…

17.11.21

DK30 Completed!

The project is far from done but I am really happy with the progress I made during this DK30. Try the final version for this DK30 here:

Voxel Landscaping

Just before the finish line I implemented a rudimentary export/import system with json files. Which can easiliy crash the world since it doesn’t check for file compatibility but hey. I also cooked up some quick soundseffects and tweaked the UI.

15.11.21

Was hesitant to post a demo of my previous update since it had so many bugs. But I fixed them all, so me start by giving the link this time:

DEMO 15.11.21

Faces weren’t rendered where there definitly should be some.

Turned out I needed some extra geometry, because I inset the faces that have no neighbours, than ad bricks on top of it. But when there are cross-neighbours there was a gap in where the 2 diagonal blocks meet, so I added additional neighbour checks and added geometry where needed. Just a simple plane does just fine.

Vertices for the generated bricks were out of place

This was a weird glitch, and I was able to solve it by just making all the blocks bigger. So there seems to be some floating point inconsistency going on. Not entirely sure about that, but since upscaling geometry solved it, I’ll leave it for now.

Empty faces when removing a block

So the “world” is divided in clusters of blocks. Everytime you place/remove a block only that specific cluster is rerenderd. I simply forgot to update neighbouring clusters when adding/removing blocks on the cluster border.

I added a simple UI to show and select available materials.

I also tweaked some coloring (again), but I will keep doing so probably, especially when adding more materials. A “Grass” tile is next on my list.

12.11.21

Got the update on the brick algorithm. I stumbled upon Binary Space Partinioning, used in a dungeon generator. The idea is that you define an area, and split that area (horizontally or vertically). Then recursively repeat that so you end up with smaller random areas.

I applied this to the side faces of my brick material block, like so:

When using this on a wall of blocks the patterns are much more interesting and non-repeating:

As you might have noticed, I also played around with the color of the materials and the sky. However I feel like I’m still not where I would like it to be.

This time I won’t upload the demo cause I broke A LOT of stuff while implementing this. It is super glitchy and unfinished so that will be for next week. Polish everything and make it presentable.

11.11.21

I worked on a brick material. Trying to come up with an algorithm that let me immitate the designs I made in Blender in the previous post.

For each side I check if it has a direct neighbour, if it doesn’t I generate a plane on a simple 4x4 grid. I then extrude a random face which is 2x2 on the grid. Afterwards I extrude randomly some more 1x1 faces, which results in this block:

In my Blender designs I added some noise to the original 4x4 plane, but right now this seemed good enough to implement. So I converted all my functions from the example block to work with my world generator:

The bad news is: I’m actually not really happy with the result. One block looks okay, but having more of them together there doesn’t seem to be enough variation. The good news: I already stumbled on an algorithm which I want to try next before wrapping up this DK30.

Click around in the new Demo here:

DEMO 11.11.21

  • select rock material: press ‘1’ key
  • select brick material: press ‘2’ key
  • add block: left mouse button
  • remove block: right mouse button

05.11.21

Been a while since last update. I tackled the obvious stuff and got a demo working of what I had in mind, and then… nothing. Suddenly I lost sight of what my goal was. Or rather, the goal became more abstract, so the project got a bit into limbo. Sure I fiddled here and there, but no real progress for about a week. Then it struck me that the follow up steps I had in mind were too big and I just needed to break it down again into smaller problems, you know, like I always do as a developer?

So my next end-goal became: Add another material to build with, cluster type as named in my code. I then could break it up in subgoals:

  • design the new material
  • add the option to switch materials
  • prepare the main store and all functions to handle different cluster types
  • finaly, add the new cluster type

So far that helped a lot! I made the decision to add brick walls and spun up a quick design:

But the rewriting of functions, so that my program know the difference between materials, took me longer than I thought. When I changed one function another broke. So I can’t show a real brick wall just yet, but I can show at least that my program handles multiple materials now:

21.10.21

Who wants to build some sky castles? Or, well, the rocky landscape to imagine a skycastle could be build at.

Today I fought again some noise glitches. Still some faces didn’t seem to be rendered. But I think I got them all now, it’s hard to be 100% sure, since it’s all generated on the fly and I can’t test for every outcome.

I also worked more towards how I want to have the landscape look. I never imagined it as a floating plane in the sky, but more like Monument Valley. To achieve that look I need to implement vertex colors (instead of mesh colors), so that’s on my todo list tomorrow.

Play the latest demo here:

DEMO 21.10.21

20.10.21

Started with adding the materialization of the cubes. this worked, however I learned that THREE.js has trouble when working with small floating numbers: It just doesn’t render the faces. I don’t feel like fixing this if I can just fix it by upscaling the block size.

So I thought, let’s upscale everything. Thinking I had it all worked out nicely with sizes of blocks and clusters set up in my store. This turned out to be not the case, since I used the size of a cluster for both the size in world units, and blocks along a axis. This works ofcourse when I use a block size of 1, hover it fails horribly when I change that size.

So I needed to set some steps back again. And go over every function where I used the cluster size variable wrongly. to celebrate I made the background a nice gradient :)

Now back to achieve that rocky feel, so far I got this look:

Which leaves me today with still some glitches around the edge:

This is probably due to my noise input positions are wrongly calculated. Which result in a small deviation.

However, that would conclude today. I am proud of the progress I am making, have fun poking around with the new demo here:

DEMO 20.10.21

18.10.21

Updating the mesh generator to only render the faces we see did solve the shadow problem. However, as always, it gave me a new glitch. Now shadows leaked light:

It has something to do with my shadow bias, but after googling and hacking for a whole day I made no progress on it. Then I got an idea: To make it easier for to pinpoint whether a block is there or not I render invisible blocks inside the generated mesh. I use these blocks to test for raycasted intersections with the mouse. But visible or not, I can still let them cast a shadow, and it did solve my problem. I may run into performance issues later on, but we will see. I ease to fall into a over-optimization trap, if it’s already working as intended.

Besides that I switched frameworks, instead of React I am using NextJS now, to quickly build and deploy demo’s, which I can show here.

Here’s the first:

DEMO 18.10.21

16.10.21

After getting a basic working prototype I went ahead and made some super simple sketches in Blender on how to achieve a “rocky” feel with some simple vertex translations:

Using noise on the translations I can generate some variations in it. I am pretty happy with the result after implementing it in my code:

Progress! But as I was about to implement it into cluster generator, I did some tests with an actual material (instead of the meshNormalMaterial I used). And I noticed weird shadow glitches:

These glitches are probably there because I just place boxes on top of each other, with faces you don’t even see. So as always, 2 steps forward, 1 step back. I need to optimize that cluster generator and only draw the faces that are actually visible, this would also make it possible to just have one single cluster mesh, which will probable be great performance wise.

14.10.21

The original starting dat would be next monday, but as a freelance webdeveloper you sometimes end up with more spare time then you thought you would. Work projects have been cancelled so I already sank a lot of time into my voxel engine and tackled come of the basic stuff.

Right now I have an interactive working prototype, with simple generated cubes that have their corners cut off if they have no neighbours!

I may need to make a new planning.

Estimated Timeframe

Oct 14th - Nov 15th

Week 1 Goal

Fundament

Get a solid working prototype, with simple cubes. You should be able to move the camera and add and remove blocks. Maybe I clean up my previously done work, but probably start from scratch.

Week 2 Goal

Develop the prototype into a system with clustered blocks of cubes. By creating this in the browser I have limited power and therefor need a smart way to render the blocks. I have been struggling with vertex-shaders, but these are quite new to me and I ran into the problem of the cubes correctly being deformed, but the shadows it casted were still just cubes. This is actually where I stopped my initial work, but since then I have a new idea how to do it differently. I am going to generate each cube, then merge all cubes of a cluster into a single mesh. Now the renderer only has to rerender a single mesh when it changes.

I finished this already in week 1, didn’t expect that but I need to set new goals for weeks 2, 3 and 4.

Materialization

Having a working prototype with simple cubes, I now want to focus on giving the cubes more character by adding more variation into the mesh generation.

Week 3 Goal

Create a “Rock” mesh type. The idea of this voxel engine is that cube-meshes get generated at runtime, based on their neighbours. Not unlike marching cubes work, but with more variation/complexity. To begin I have an idea how to do this with a rocky terrain but creating dents by moving vertices and chopping of parts of the cube.

Environment

Instead of just playing with cubes, give the ‘player’ the feel of them actually landscaping a world. Achieved by adding a skybox, and maybe make the limitations of the groundplane more natural instead of the invisible wall. Our world can be a little island for instance?

Week 4 Goal

Review

Instead of hacking along, I want to take this week and review my progress. what did I achieve? What did I learn from it? And most importantly, what’s next? Can I schedule another a planning for the next 30 days?

Tags

  • voxels
  • landscaping
  • townscaper
  • browser
  • react
  • threejs