About CityEngine Blog


CityEngine is a great tool that is able to create large scale models, mainly of cityscapes, quickly, and with the ability to make adjustments based on a rule file in a procedural manner.

I hope to show you some of the work I have done with CityEngine creating a variety of models across a range of projects. I have mainly used the software for planning applications but have learnt a great deal of the potential for other applications.

I want to concentrate on the writing of rule files which is the core use of CityEngine. Without rule files no 3D content can be generated and this is very important to understand. I will also strive to bring news and updates regarding CityEngine as well.

I hope you find what I share useful and please feel free to share and contribute your thoughts and experience.


Monday, 2 June 2014

TikiTown 5 - The final piece of the Tiki

I wanted to do something a little bit different for the main body of the Tiki - called suburbia, and also the area that I've very lazily (or incorrectly) called the helmet. Terminology aside, I wanted to try some new techniques including creating a rule specifically for one input shape. This is a general change to how I had used CityEngine before as all other CGA scripts I had written were aimed to be used on multiple, often hundreds, if not thousands, of input shapes. I also wanted to try and vary the shape of houses a little bit more than I had up to this point.

Suburbia

The suburbia start rule is pretty crude and was developed pretty quickly because I wanted to keep the focus of  the TikiTown model on the head. However, bored as I was of creating simple residential footprints, I wanted to give importing some prepared footprints a go. This would allow much more variety in the footprint shape, providing a more realistic birds eye view of a suburban area.

I am fortunate enough to access to a wide range of geospatial data and in this case footprints for existing buildings came in very handy. I simply imported 25 building footprints from a geodatabase into CityEngine and exported them as a Collada file without doing a single thing to them. The files that exported from CityEngine still had the original orientation and so I brought them into Sketch Up where I simple re-orientated them so that they were all aligned in the same manner.

There is nothing extraordinary about the rule for the suburbia area and I've not attempted to do anything creative with the footprints after they have been inserted as a model but the trick works quite nicely. The footprints do not necessarily have to be taken from real world data - although it was very easy simply selecting 25 out of the hundreds of thousands on offer, they could simply be created from scratch in Sketch Up. The building footprints that I've used meet the New Zealand style I wanted but if I were creating a different style of city then possibly I'd look to create or extract others. Open Street Map might be a good source for similar data for other areas of the globe, or other local sources of geospatial data.



One Shape, One Rule

The Tiki Helmet provided an opportunity to forget the whole building / city thing and just create a model that made a cool backdrop to the rest of the city. On another project I had created a rule for a single shape that looked at making a long building into a wave style shape. Of course we could just drop in a building that has already been modelled as an insertion but where is the fun in that? Here I wanted to produce a model that resembled a building, or buildings but that are very tall and thin with heights that vary according to their place within the model. I wanted the buildings at the back to be taller and gradually decrease in height both as they came closer to the front and to the centre of the model. It gives the impression of the focus being pulled into the centre of the TikiHead towards the TikiSpike.

After the base had been built the shape is split into 20 sections along the longest axis - which in this case is scope.sy. As much as I attempted to re-align the scope with these shapes they never played nice - obviously the scope.sx should be the longest scope but sometimes you just have to go with the flow. Keep that in mind when you follow the code below. Next the shapes were split into 10m sections to create a rough grid pattern. In each of the splits that had just been carried out, both the index and total values had been passed on. The piece of code called Helmet3 takes these values and gives them to hidden attributes so I could use these to give the individual sections their heights.

A small setback is applied to create separate blocks which are then extruded using a fluffy calculation. The TikiHelmet will actually be applied to two shapes - one either side of the TikiSpike. The left hand side is considered the base shape and the one on the right is the reverse. When it comes to splitting, the reverse side it is done in the opposite direction to that of the base and therefore to get the correct extrusion I need to carry out a slightly different fluffy calculation to work out the extrusion height. The image below shows the base shape after the extrusion has been applied. Its extrusion works by dividing the original split total (20) by two, adding the index value of the individual block plus the 2nd index value. The calculation includes some values by which these are multiplied by, simply to give a height that seems realistic (hence the fluffiness).



The reverse shape is subject to a similar extrusion calculation but it also includes the addition of the 2nd total value. The rest of the rule concerns the pretty simplistic look of the 'building' model. As each model here is made of many buildings the number of polygons can get out of control pretty quickly and so the inclusion of elaborate windows etc. is a bit of a no go, but in this case I did model up some simple frames. The mass of each individual block has been modelled in the same fashion as the decreasing mass buildings shown in an earlier post.

Apart from an attempt to play with the colours in the same way as the extrusion (which I didn't dedicate much thought to) the models were pretty much complete and the Tiki finished. Now I could move on to the the final step of visualisation...




@startRule
Suburbia -->
      extrude(world.y, 2)
      color("#C0C0C0")
      SuburbiaBase
      Suburbia1

Suburbia1 -->
      comp(f){top : setback(2){all : NIL | remainder : alignScopeToGeometry(yUp, auto) Suburbia2}}
           
Suburbia2 -->          
      case geometry.area < 80 : NIL
      else : innerRect Suburbia3
     
Suburbia3 -->
      15% : s('0.5,'1,'0.5) center(xz) i(fileRandom("assets/DAE_FloorPlates_Realigned/*.dae")) comp(f){top : Suburbia4}
      30% : s('0.8,'1,'0.75) center(xz) i(fileRandom("assets/DAE_FloorPlates_Realigned/*.dae")) comp(f){top : Suburbia4}
      30% : s('0.65,'1,'0.80) center(xz) i(fileRandom("assets/DAE_FloorPlates_Realigned/*.dae")) comp(f){top : Suburbia4}
      else : s('0.65,'1,'0.80) center(xz) i(fileRandom("assets/DAE_FloorPlates_Realigned/*.dae")) comp(f){top : Suburbia4}
     
Suburbia4 -->
      case geometry.area < 55 : NIL
      else :
            25% : extrude(5.5) Suburbia5
            else : extrude(3) Suburbia5
           
Suburbia5 -->
      split(y){3 : SuburbiaFloors(split.index,split.total) | 2.5 : SuburbiaFloors(split.index,split.total)}


SuburbiaFloors(idx,n) -->
      case idx == n-1 : comp(f){top : alignScopeToGeometry(yUp, auto) SuburbiaRoof | side : SuburbiaFacade | all :  SuburbiaFinish}
      else :      comp(f){side : SuburbiaFacade | all :  SuburbiaFinish}

SuburbiaRoof -->
      75% :roofHip(22,0.3) SuburbiaRoof1       
      else : roofGable(22, 0.3) SuburbiaRoof1  
     
SuburbiaRoof1 -->
      comp(f){vertical : SuburbiaFacade | aslant : alignScopeToGeometry(yUp, auto) SuburbiaRoof2}
     
SuburbiaRoof2 -->
      setupProjection(0, scope.xy, 4, 2)
      projectUV(0)
      texture("IronRoof2.jpg")     
      color(getSteelRoofColour)
     
SuburbiaFacade -->
      setupProjection(0, scope.xy, 1, 1.25)
      projectUV(0)
      texture("Weatherboard.jpg")  
      color(getWeatherboardColour)
     
SuburbiaBase -->
      comp(f){top : SuburbiaBase1 | all : SuburbiaBase2}
     
SuburbiaBase1 -->
      setupProjection(0, scope.xy, ~2, ~2)
      projectUV(0)

      texture("lawn2.jpg")




@startRule
Helmet -->
      alignScopeToGeometry(yUp, auto)
      extrude(2)
      comp(f){top : Helmet1 HelmetBaseTop  | all : Base.}
     
Helmet1 -->
      split(y){scope.sy/20 : Helmet2(split.index,split.total)}*  
     
Helmet2(idx,n) -->
      split(x){~10 : Helmet3(idx,n,split.index,split.total)}*
     
Helmet3(idx,n,idd,m) -->     
      set (HelmetIndex1, idx)
      set (HelmetTotal1, n)
      set (HelmetIndex2, idd)
      set (HelmetTotal2, m)
      setback(1){all : NIL | remainder : Helmet4(idx,n,idd,m)}
     
Helmet4(idx,n,idd,m) -->
      case Reverse == "True" : extrude(HelmetTotal1/2+HelmetIndex1*5+(HelmetTotal2-HelmetIndex2)*3) Helmet5
      else : extrude(HelmetTotal1/2+HelmetIndex1*5+HelmetIndex2*3) Helmet5
           
Helmet5 -->
      split(y){~4 : Helmet6(split.index,split.total)}*
     
Helmet6(idx,n) -->
      s((scope.sx*(1-((idx-1)/n)/2)), '1,(scope.sz*(1-((idx-1)/n)/2)))center(xz) Helmet7(idx,n)
     
Helmet7(idx,n) -->
      comp(f){side : HelmetFacade | top : HelmetRoof | all : HelmetKeeper}   
     
HelmetFacade -->
      setback(0.2){all : HelmetFrame | remainder : HelmetWindow
     
HelmetFrame -->
      extrude(0.3)
      t(0,-0.3,0)
      color("#808080"
           
HelmetRoof -->   
      color("#808080"
     
HelmetWindow -->
      t(0,0,-0.3)      
      set(material.color.r, 0)
      set(material.color.g, 0)
      set(material.color.b, 1 - ((255/(HelmetTotal1-HelmetIndex1))/100) - (255/(HelmetTotal1-HelmetIndex2)/100))
      set(material.opacity, 0.90)
     
     
HelmetBaseTop -->
      setupProjection(0, scope.xy, 2, 2)
      projectUV(0)
      texture("pavement.jpg")