Package: procedural-buildings
Procedural Generation of BuildingsWelcome to my third year university project on the procedural generation of buildings!There are two main goals for this project:Design a grammar language that can be used to describe buildings with sets of procedural rules, which can then be transformed into a 3D modelsGiven an example set of 3D buildings, attempt to reverse engineer a grammar from the given buildings, which could then be used to create more buidings similar to the examplesTo discover how I achieved the above, I encourage you to read (or perhaps just flick through!) myproject report. The report also teaches you how to write your own grammars and demonstrates some of my own architectural creations.If you want to have a go at writing your own grammars and generating your own buildings, read on!Installation (using Pip)This project is packaged and available onPyPiand can be installed (or updated) with the following:python -m pip install --upgrade procedural-buildingsBasic usageWriting your first grammarIf you want to learn the ins and outs of writing a grammar, readmy report. Here we'll learn by example and leave the rest to intuition!
A grammar isrunon an initialscope(a 3D bounding box) and consists of a set of rules, one of which is astart rule. By default, the start rule isplotbut you can specify a different name if you'd prefer. The following grammar takes the initial scope and splits it in half along the x-axis into two cuboids:plot --> split(x){~1 : I(rect) | ~1 : I(rect)}Save the above to a new file calledsplit_grammar(make sure to include a newline at the end as above). We can then generate a.objfile from the grammar using the following on the command line:python -m procedural_buildings -i split_grammar -o split.objThis will create a 3D model insplit.obj. Open this in your favourite 3D model viewer (I recommendBlender), noting that the positive z-axis corresponds to upward and the positive y-axis corresponds to foward. Blender lets you specify these axis directions when you import an object. You should see a 10x10x10 cube which is cut in half along the x-axis. To change the initial scope (which defaults to the 10x10x10 cube you see), use the-s(or--start-scope) option. To see a full list of options, use-h:python -m procedural_buildings -i split_grammar -o split.obj -s 2,2,0,10,5,20Play around with different start scopes and check out the results.Creating a houseNow lets look at a slightly more interesting example - a house with a garage. Copy the following grammar into a new file calledhouse_grammar. Once again, don't forget the newline (I really ought to change that "feature"!):plot --> split(x){~2 : house | ~1 : garage}
house --> split(z){~2 : I(rect) | ~1 : I(triangle)}
garage --> split(z){~1 : I(rect) | ~2 : nil}Now generate the buildings with:python -m procedural_buildings -i house_grammar -o house.objTake a look athouse.objand try and understand how it's been generated by the grammar we wrote. Play around with some numbers in the grammar to see how it affects the result.As a final example, let's adapthouse_grammarso it has some randomness. Copy the below into a new file calledhouse2_grammar:plot --> split(x){~2 : house | ~1 : garage} : 0.5
plot --> split(x){~1 : garage | ~2 : house} : 0.5
house --> split(z){~2 : I(rect) | ~1 : I(triangle)}
garage --> split(z){~(rand(0.75,1.25)) : I(rect) | ~2 : nil}See if you can figure out what the result will look like then generate a building:python -m procedural_buildings -i house2_grammar -o house2.objNotice that if you run the above command a few times, the result will be different each time.To generate many buildings from our grammar, we can use the following options:-n(or--num_buildings) (default 1) specifies the number of buildings to generate from the grammar-d(or--separation) (default 10) specifies the distance between buldings.-f(of--file_per_obj) specifies whether to use a separate file for each building. In this case the-doption will be ignored and the given output file name will be used as the file prefix to use for the output files.Let's create 10 buildings each 2 units apart. We'll stick to one output file for now:python -m procedural_buildings -i house2_grammar -o house2.obj -n 10 -d 2How about backwards?To create a grammar from a given buildings, we can use the-r(or--reverse) flag. Now, our input file is the.objfile and the output is a grammar file. Let's try and reverse engineer the grammar we used to create the first house:python -m procedural_buildings -i house.obj -o house_engineered_grammar -rTake a look athouse_engineered_grammarand you should see its equivalence to the originalhouse_grammar.Now lets try and engineer a grammar from mutliple example buildings. First create 10 example buildings usinghouse2_grammar. We'll put them in separate files this time by using the-fflag. The output file nameexamplewill be used as the prefix for the 10 output files so we'll getexample0.obj,example1.obj, ... ,example9:python -m procedural_buildings -i house2_grammar -o example -n 10 -fNow we can create a grammar from the example buildings. Create a new file calledexamplesand enter the file names of the example buildings:example0.obj
example1.obj
example2.obj
example3.obj
example4.obj
example5.obj
example6.obj
example7.obj
example8.obj
example9.objNow, create a grammar from the buildings. The input file will be the file containing the file names (it knows it's a list of file names rather than a single object input by checking for a.objextension):python -m procedural_buildings -i examples -o house2_engineered_grammar -rTake a look at the engineered grammar. You should be able to see the similarity to the originalhouse2_grammar.
We can now use our engineered grammar to create more buildings. Note that we'll have to specify that the start rule for this grammar isrule0:python -m procedural_buildings -i house2_engineered_grammar -o more_houses.obj -n 10 -R rule0You've now generated 10 buildings similar to the 10 examples.To see some more complex examples, check outmy reportor create your own buildings!