The first iteration of a Room Splitter for procedurally generating floorplans
Right now it only outputs the rooms.
Doesn’t follow any logic other than Min and Max Room sizes. with a little bit of randomization around where the split goes.
I want to create some more solid rules.
- Ensure Rooms have Purpose (bedroom, kitchen, living, etc). with support for multi-purpose rooms when the room cannot be split.
(E.g. House with 1 room will be a bedsit (Bedroom / Kitchen /Living merged into one)
- Ensure House fulfills minimum requirements in order.
Bedsit < Studio < 1 Bed Flat < and so on until size is big enough to have specialized rooms.
I think the City Zoning blog post will be of great help for this as you can start splitting the house into zones after it gets big. (Sleeping Areas / Living / Recreation / Utilitarian areas, etc). And create a ruleset to classify and then choose how to generate the content.
It took me a while to update this, but I can subdivide a space into a series of interconnected rooms.
Right now the Door Sizes are fixed, but I have built-in the ability to randomize the passage width, add a frame or not, so this should create more interesting layouts.
I’m not sure about the believability of the logic though, because it can generate some awkward layouts.
I want to logic to go to a hierarchical structure as mentioned before, so the room splitting will be a bit more deterministic (layouts less random), but I’ll have to figure out how to keep the variety going. I’m reading a couple of white papers on these that are interesting.
But overall I’m happy I managed to get a series of interconnected rooms.
Yeah, it’s starting to look convincing. Buildings will normally have other constraints like supporting columns, stair-wells, service risers, lift shafts, etc, so it’s bound to look a bit ‘random’ with full freedom to divide the space up. This is a great rabbit-hole to be going down though. I’m sure you’ll find yourself thinking of ever more tweaks you can do to improve the logic.
Added random twists, and now I’m getting a better distribution.
I need to add randomness to the decision of splitting a room. right now I have Must Split / Can Split, and it will do it. And it always splits the room across the longest axis.
Not sure how you’re doing it, but I added a special operation a while ago to help with room splitting:
Numerical.MergeRanges - Combine arbitrary list of numerical ranges into a list of non-overlapping ordered ranges
Basically to help find all the places along a room that don’t intersect a door. It’s quite specialised, but useful as it’s a number-crunching process that is very complicated to implement as a procedure. You would use it by building a list of pairs of start/end of all the doorways along the room in one axis, it will output a similar set of values but each segment will be non-overlapping and they will be in order. This makes it easier to scan for ‘gaps’ that a split can safely happen in.
I added this when I wanted to add vertical shafts to my dungeons, and since these are areas (like doorways) that can’t be split (but in both axes) it was going to make the logic I already had horribly messy, in-fact I don’t think I could get my head around it. Anyway, I wrote this operator to do the heavy lifting instead.
What I did was start with a Main Room, add doors on each side at random places. Then get the right, middle and left frames, and start splitting them.
That’s why you’ll always see one or two long corridors. I did that because it’s consistent with some early design inspirations of how houses are split.
it looks weird for these huge buildings, but it feels a little more natural for “normal” house sizes.
When a room is split, it adds a Connection wall where the split has happened. The problem is that right now my room splitting logic doesn’t have any knowledge of where doors exist (unlike the main room).
I could recurse the Main room logic instead because that already only allows for splits outside of areas that have a door (but I got a bit bamboozled by my own spaghetti)
I need to improve (or rethink this), because this does not guarantee that the walls won’t land where a door already exists, I think perhaps that’s where
Numerical.MergeRanges Would come in handy, though I’m not sure I understand how it works.
Also, is there an example that’s using it?
Ah, no, still lacking detailed docs on all the operators. Probably going to need that for release aren’t I.
But, yes, that’s exactly what I used it for. Although its only part of the process. Something like this:
- Lets assume we are wanting to split a room into a left and right part, and have doorways along the top and bottom.
- Work out the distances of each door from the left side, as a min/max range (I.e. the leftmoay side and rightmost side).
- Collect these into a list of floats, alternating left, right.
- Pass into merge operator.
- Go through the result, adding up the amount of space that isn’t covered by doors across the room. This is the space you could split the room.
- Now choose a split value within this amount.
- Go through the list again, counting up sections until you get to the one with your split point in. Calculate where this is within this section, and thus where across the room it is.
- This is where you split the room, and insert a wall.
- All doors need to be assigned to the appropriate new sub rooms. Including any newly added doors on the new dividing wall.
- If there are no doors on one side you either have a dead-end room, or don’t have to insert a door to leave a dead-space.
This explanation would be aided by a diagram or two. Some of this might be covered on the old blog posts I made actually. Check the About/Archive section on the website.
In terms of insuring rooms have a purpose, I know I saw a paper a while back that got into this based on recognising that there are broad categories of linked purposes for rooms, and that the easier it is to get to a room, the more ‘public’ that purpose is. I can’t find the original paper, but I found three others that seems to touch similar areas:
I went back to this thread after the conversation we had this morning and then I re-read the post about the
Numerical.MergeRanges Operator so I decided to test.
The ConnectingWall procedure is outputting the “location” of the door packed into the Vector3 (being X the split amount, and Y the split amount plus the door width.
Then I’m running these Pairs of floats, into the Merge Ranges
and I’m getting either 4 numbers or 2 numbers.
- I get 4 numbers when the doors are not in front of each other
- I get 2 numbers when the doors ARE in front of each other.
so that’s cool, I’m detecting overlaps somehow. however I think I’m not doing it right, or passing in the right values.
I guess what I’m looking for is to get a range of wall segments where adding a wall would be valid.
And my guess is what that operator does, just need a little help with what list of pairs I need to pass to it.
I think I got it.
the inputs are actually defining where the holes (future doors) are.
The outputs spit out the non-overlapping segments.
output with overlapping segments
(Note: there is no real logic to put the label of these segments on top of the right segment
Sounds like you have it right. The returned list is where the doors are. If you mentally add a 0 to at the front of the list and a [Wall Length] at the end then the list can be considered a list of pairs wall segments instead, where you can split.
-------[ ]----------[ ]---
[------] [----------] [--]
A simple way to choose a split point is then:
- Randomly select a wall segment
- Select a random point along it’s length
- Split there
However, this is biased in favour of shorter segments. If you want to properly select a place among the wall segments you need:
- Add up the wall segment lengths
- Randomly choose a value within that value
- Step through the segments ‘using up’ the value
- Until you are part way through a segment
- Split there.
Got it. That’s a great suggestion. Couldn’t honor the stream today, unfortunately. Very Busy
It frigging works. I think the stream will be interesting today.
Last night I got the RTXGI enabled.
Before I was using SSGI (ScreenSpace GI)
SSGI has ghosting, RTX doesn’t because is based on lightprobes.
I want to cry right now
Of happiness that is!
It’s a real maze in there. but everything is connected.
- Adding multiple openings (doors / windows) to external walls.
- randomizing size of interior doors at each connection split.
- Adding extra doors to rooms that are longer than the Maximum Room Size.
Things to think about
- merging rooms to get interesting L shapes (I think this will be hard.
- filtering which rooms are on the exterior so they can be deleted / extended (will then need to come up with a way of redesign the outer wall.
Awesome, it’s great when you crack a hard problem and then everything ‘just works’, especially with proc-gen where you can then go crazy with trying things.
- Extra doors would come from adding multiple doors on long split walls, i.e. aiming for some min spacing between adjacent doors perhaps.
- The exterior rooms are those that have any openings tagged as windows (unless you allow interior windows, but you could tag them as a third opening type).
Started doing some janitorial work
Copy-paste is helping me with tidying the graph up
All of this was outside of the graph. No wonder I was having a hard time getting it to propagate correctly.
I’m hoping that when I add more doors to the connection wall it will automagically work.
I’m going to duplicate the connection wall graph to make the specialized External Wall graph.
One of the Positive knock-ons of the split working is that I can actually pass the Door Data to each room time
And also run checks like
If a room is small (under a certain area) and only has one door, make it a bathroom, or a closet. etc.