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.


Thursday, 31 October 2013

TikiTown 2 - CGA creation - Tiki Eyes


As I mentioned in the previous post, I will be creating a number of different start rules which can be applied to groups of parcels to generate buildings depending on their location within the Tiki. I'll run briefly through each start rule showing what I wanted to achieve and how some of the code is built (revised - this will now be split over a few blogs due to the length...not so brief).


TikiTown Eyes

Selecting parcels is something that could have been quite annoying at this stage due to the many different start rules but it was made very easy by using the selection tools within the right-click menu. One of the eye parcels was selected and following a right-click, under the Select menu a number of choices are given. The option Select by Group selects all parcels within the same block and so I could now easily select all parcels in one of the eyes without having to either add each one individually or selecting them all by drawing a shape around them. Just a small time and annoyance saving technique! Later the Select by Same Start Rule option also became helpful as I could use this to select all eye parcels in both eyes.


The parcels were created using the skeleton subdivision because I wanted to create rings of buildings that became taller the closer they came to the centre of the eye. The centre itself would consist of empty space, but that also gave me the option to fill the centre with a potential tower like structure if I felt it necessary. The buildings making up the eye would also be staggered in height with three layers, each becoming smaller the higher the layer was.

The full CGA can be found at the bottom of this post along with a link to a web scene showing one of the Tiki eyes.

In anticipation of using water to surround all of the islands of land I first extruded the entire parcel by two metres to give the feeling of a raised platform of land. I then split the parcel into 15m sections with the first one forming the 'pupil' which would be left empty for now. Each of these would contain a separate building. After setting a number of random values (more on these later) a small space was created between each building area.


A small Setback operation and an innerRect operation were applied next to give a nice normal rectangle building footprint. Using the Split Index and Total values each footprint was extruded by 100m minus the Split Index times a random value between 15m and 20m except the first building (the one nearest the centre) which was extruded to 90 (shown in blue). At this point the buildings were split into floors of 4m.


Going back to one of the values we had previously Set - EyeType, I now wanted to stagger each building into three layers so that they would become smaller the taller the were. The EyeType value is set at a point where each footprint will receive a different value as opposed to when the entire parcel is still one part. This style of adding value at a particular point in the model creation is crucial to producing the right outcome. The EyeType returns a value between 1-4 (integers). Each value is used to produce slightly different staggering layers (blue is the first layer, dark grey the second and light grey the third).

EyeType 1 gives three layers that are split exactly into three equal parts (dependant on the number of floors). EyeType 2 has a first layer of the bottom 20% of floors, the second up to 50% and the remainder is the third layer. EyeType 3 has a first layer of the bottom 45% of floors, the second up to 60% and the remainder is the third. Lastly EyeType 4 has a first layer of the bottom 30% of floors, the second up to 70% and the remainder is the third layer. Each floor is subject to Size operation to decrease the floor plate area in each of the 2nd and 3rd layers. Each time the model is regenerated the values are randomly reassigned so there are an endless number of outcomes that could be given.


After selecting the top floor for later roof detail, each floor is subject to a Comp(f) Split so that facade detail can be added. After quite a lot of testing, due to failed attempts, the amount of detail I was able to add was reduced severely. I think that because each parcel of land creates at least 4 buildings that each model is really quite complex. This results in very heavy models that detail can be added to, just not exported to the web viewer without size issues. I generally found that any web viewer over the size of 200Mb when unpacked was a little unwieldy. Certainly adding any more models to a highly detailed eye was a problem and so it was with great shame that the amount of detail was steadily reduced.

Any building wider than 15m had its facade split in two and then a process similar to the EyeType was applied. This time using the EyeFacadeType value, four slightly different facade styles are created, each with different window sizes. Again the value for the facade type is generated at the building level so each building within the total model will receive a different value. A range of operations are applied so that each window receives a frame that is Extruded and Translated so that it gives a fully 3D style frame.


At the same time that the Eye and Facade Types are assigned the last values for the window colours are also assigned. In this case the RGB values are provided by a random number for each component. In each case I have allowed only a certain range of randomness for the RGB colours so that each time the colour produced is from blue to black in colour. This is an attempt to limit the colour to realistic colours of windows of commercial buildings. The wall colour is provided by a constant which contains a list of colours taken from colour chart website.


The last step was to add roof detail and greebles (small additional blocks on top of the roofs) and add a simple concrete texture to the base. Apologies if this is a very brief run over the CGA methodology but this is simply for one start rule. I will be going through the others but I hope that you can follow the methodology when looking at the code created for the Eye start rule which is below.

A web scene of the finished eye can also be found through this link. Google Chrome or at least Google frame for IE is needed to open the application (9Mb).


The full code for the Eye start rule - apologies for the looseness of some of the code. It was revised a number of times due to having to dumb down the code and so it probably needs to be completely updated for efficiency.

/**
 * File:    TikiTown.cga
 * Created: 1 Jan 2013 02:26:48 GMT
 * Author:  ellawayc
 */

##-----------------------------------------##
## Hidden Attributes ##
##-----------------------------------------##

@Hidden
attr EyeType = 0

@Hidden
attr EyeFacadeType = 0

@Hidden
attr EyeWindowColourR = 0

@Hidden
attr EyeWindowColourG = 0

@Hidden
attr EyeWindowColourB = 0

##-----------------------------------------##
## Constants ##
##-----------------------------------------##

const getWallColour =
            10% : "#F0F8FF" #Alice Blue
            10% : "#CDC0B0" #Antique White 3
            10% : "#E0EEEE" #Azure1
            10% : "#C1CDCD" #Azure2
            10% : "#838B8B" #Azure3
            10% : "#CDC8B1" #Cornsilk2
            10% : "#8B8878" #Cornsilk3
            10% : "#4F4F4F" #Grey31
            10% : "#696969" #Grey41
            else : "#BEBEBE" #Grey

##-----------------------------------------##
## Assets ##
##-----------------------------------------##
getEyeColorR =
      (1/255)*EyeWindowColourR     
     
getEyeColorG =
      (1/255)*EyeWindowColourG
     
getEyeColorB =
      (1/255)*EyeWindowColourB
##-----------------------------------------##
## Rules ##
##-----------------------------------------##

@startRule
Eyes -->
      extrude(2)
      comp(f){top : EyesA EyesBaseTop | all : EyesBase.}
     
EyesA -->
      alignScopeToGeometry(yUp, largest, 0)
      split(z){~15 : Pupil. | {15 : Eyes1(split.index,split.total)}*}



Eyes1(idx,n) -->
      set(EyeType, (ceil(rand(0,4))))
      set(EyeFacadeType, (ceil(rand(0,4))))
      set(EyeWindowColourR, (ceil(rand(0,51))))
      set(EyeWindowColourG, (ceil(rand(0,25))))
      set(EyeWindowColourB, (ceil(rand(0,175))))
      split(z){1 : NIL | ~1 : Eyes2(idx,n) | 1 : NIL}
     
Eyes2(idx,n) -->
      setback(0.25){all : NIL | remainder : Eyes3(idx,n)}
           
Eyes3(idx,n) -->
      case idx > 1 : innerRect extrude(100-(idx*rand(15,20))) split(y){~4 : EyesFloors(split.index,split.total)}*    
      else : extrude(90) split(y){~4 : EyesFloors(split.index,split.total)}*
           
EyesFloors(idx,n) -->
      case EyeType == 1 :
            case idx < n*0.33 : EyeFloors1(idx,n)
           case idx < n*0.66 : s(scope.sx*0.85,'1,scope.sz*0.85) center(xz) EyeFloors1(idx,n)
            else : s(scope.sx*0.65,'1,scope.sz*0.65) center(xz) EyeFloors1(idx,n)
      case EyeType == 2 :
            case idx < n*0.20 : EyeFloors1(idx,n)
           case idx < n*0.50 : s(scope.sx*0.85,'1,scope.sz*0.85) center(xz) EyeFloors1(idx,n)
            else : s(scope.sx*0.65,'1,scope.sz*0.65) center(xz) EyeFloors1(idx,n)
      case EyeType == 3 :
            case idx < n*0.45 : EyeFloors1(idx,n)
           case idx < n*0.60 : s(scope.sx*0.85,'1,scope.sz*0.85) center(xz) EyeFloors1(idx,n)
            else : s(scope.sx*0.65,'1,scope.sz*0.65) center(xz) EyeFloors1(idx,n)
      else :
            case idx < n*0.3 : EyeFloors1(idx,n)
            case idx < n*0.7 : s(scope.sx*0.85,'1,scope.sz*0.85) center(xz) EyeFloors1(idx,n)
            else : s(scope.sx*0.65,'1,scope.sz*0.65) center(xz) EyeFloors1(idx,n)

EyeFloors1(idx,n) -->
      case idx == n-1 : EyeRoof EyeFloors2
      else : EyeFloors2
     
EyeFloors2 -->
      comp(f){top : EyeFloors3 | side : EyeFacade | all : EyeKeeper.}

EyeFacade -->
      case scope.sx > 15 : split(x){~1 : EyeFacade1 | 0.35 : Wall | ~1 : EyeFacade1}
      else : EyeFacade1
           
EyeFacade1 -->   
      case EyeFacadeType == 1 :
            split(x){0.2 : Wall | {~3.5 : EyeFacadeType1}* | 0.2 : Wall}
      case EyeFacadeType == 2 :
            split(x){0.2 : Wall | {~2 : EyeFacadeType2}* | 0.2 : Wall} 
      case EyeFacadeType == 3 :
            split(x){0.2 : Wall | {~4 : EyeFacadeType3}* | 0.2 : Wall} 
      else :
            split(x){0.2 : Wall | {~4 : EyeFacadeType4(split.index,split.total)}* | 0.2 : Wall}

EyeFacadeType1 -->
      setback(0.2){all : Frame | remainder : Window} 
           
EyeFacadeType2 -->
      setback(0.1){all : Frame | remainder : Window} 
     
EyeFacadeType3 -->
     split(y){0.2 : Wall | 1.25 : EyeFacadeType3_1A | 0.10 : Frame |  ~1 : EyeFacadeType3_2A}
     
EyeFacadeType3_1A -->
      split(x){{0.1 : Frame | ~1 : Window}* | 0.1 : Frame}
     
EyeFacadeType3_2A -->
      split(x){{~scope.sx/2 : EyeFacadeType3_2B | 0.2 : Wall} | ~scope.sx/2 : EyeFacadeType3_2B}

EyeFacadeType3_2B -->
      setback(0.15){all : Frame | remainder : Window}
     
EyeFacadeType4(idx,n) -->
      case idx%2 == 1 : split(y){2.25 : EyeFacadeType4_1A | 0.20 : Wall | ~1 : EyeFacadeType4_1B}
      else : split(y){~1 : EyeFacadeType4_1B | 0.20 : Wall | 2 : EyeFacadeType4_1A}

EyeFacadeType4_1A -->
      split(x){~1 : Window | 0.1 : Frame}*
     
EyeFacadeType4_1B -->
      setback(0.1){all : Frame | remainder : Window}

Frame -->
      extrude(0.25)
      t(0,-0.25,0)
      comp(f){side : WallTex | top : WallTex | bottom : WallTex} 

Window -->
      t(0,0,-0.25)
      color(getEyeColorR, getEyeColorG, getEyeColorB)
      set(material.opacity, 0.95)  
     
Wall -->
      extrude(0.25)
      t(0,-0.25,0)     
      comp(f){side : WallTex | top : WallTex | bottom : WallTex}

EyeFloors3 -->
      color(getWallColour)
     
WallTex -->
      color(getWallColour)
     
EyeRoof -->
      comp(f){top : color(getWallColour) EyeRoof1}
     
EyeRoof1 -->
      extrude(0.5)
      comp(f){top : EyeRoof2 EyeRoofKeeper | all : Wall}

EyeRoofKeeper -->
      extrude(0.01)
     
EyeRoof2 -->
      15% :
            roofHip((rand(30,45))) FullHip      # Type A
      15% :
            roofHip((rand(30,45)))
            split(y){(rand(0.5,2.5)) : ChoppedHip} # Type B
      15% : Flat # Type C
      30% :
            setback(rand(1,2)){all : NIL | remainder : BoxRaised} # Type D and E
      else :
            setback((rand(0.15, 0.35))){all : RaisedEdge  | remainder : GreebleBase} # Type F

FullHip -->
      FullHip1.  
     
ChoppedHip -->
      comp(f){horizontal : ChoppedHip1 GreebleBase | all : ChoppedHip1}
           
Flat -->
      Flat1
      GreebleBase
     
BoxRaised -->
      extrude(rand(0.5,1))   
      comp(f){top : BoxRaisedMulti BoxRaisedKeeper | all : BoxRaisedKeeper}
     
BoxRaisedMulti -->
      setback(rand(1,2)){all : NIL | remainder : alignScopeToGeometry(yUp, largest, 0) GreebleBase BoxRaisedMulti1}
     
BoxRaisedMulti1 -->
      case scope.sz < 2 : NIL
      else : innerRect extrude(rand(0.5,1))          
     
RaisedEdge -->
      extrude(rand(0.3, 0.75))     
     
GreebleBase -->
      alignScopeToGeometry(yUp, largest, 0)
      Greeble1

Greeble1 -->
      setback(scope.sz*0.20){all : NIL | remainder : Greeble2}
     
Greeble2 -->
      case scope.sz < 2 : NIL
      else :
            innerRect
            split(x){~2 : GreebleSmall1 | 1 : NIL}*
     
GreebleSmall1 -->
      case scope.sz < 2 : NIL
      else : split(z){~2 : GreebleSmall2 | 1 : NIL}*
     
GreebleSmall2 -->
      comp(f){all : GreebleSmall3}
           
GreebleSmall3 -->
      50% : extrude(1)
      else : NIL 

EyesBaseTop -->
      setupProjection(0, scope.xy, 2, 2)
      projectUV(0)
      texture("pavement.jpg")

Wednesday, 30 October 2013

TikiTown 1 - The start of a new project


I've been wanting to produce something in CityEngine that was a little more pleasing on the eye compared to the planning projects that I had previously worked on. Taking inspiration from the work that Fold 7 did with CityEngine to produce an advert for a Ministry of Sound release, I wanted to create a city that had a wider vision, more than just simple blocks and streets.


The guys at Fold 7 did a great job in taking the Ministry of Sound logo and creating a city around it, revealing the logo at the end of the animation. They are obviously experts in producing a high end visual product so I don't think I can match that but it would be fun to produce a city based on the same principal.

The initial work flow that I thought would work would be:

1. Decide on a city design template
2. Create the roads and blocks network
3. Generate a CGA rule file to create the city
4. Attempt to produce a few high detail renders and export the city as a 3D web scene.

So here is one of the end renders that I produced using the Luxology render engine within Bentley MicroStation V8i. I am most definitely a beginner in using MicroStation and Luxology and the amount of set up options is outrageous. Thankfully, a colleague had used the software in some testing and could at least point me in the right direction. I'll show more images in a later post.




The city design template

New Zealand has great symbology everywhere (mostly visualised in tattoo's!) and so making a decision on what to base the city on was a pleasure. From the massive array of images and symbology I decided that I wanted to create a city in the mould of a Tiki. The symbol has great significance for the Maori population and forms part of the national cultural heritage.

A simple scan of the internet gave a raft of excellent images to use as a template for the city. Compared to the previous projects I would actually use very little GIS knowledge to produce this city which I felt could free up more time to put towards more creative use. I did however import the chosen Tiki image into ArcGIS and geo-reference the image so that I could control the size of the city. I had in a previous test made a city that was around 5km by 5km which turned out to be far too large to be handled properly in both CityEngine and the 3D web viewer. I decided that a city 1.5km by 750m should suit the needs of the project whilst still proving easy to handle when it comes to visualisation.


Generating the road network and blocks

The image below shows the Tiki after it has been georeferenced (potentially to be used in a wider context) with a hillshade layer underneath.



The lines, that would form the road network, were captured as line features and saved as a shapefile. The shapefile could easily be imported into CityEngine and took around an hour to capture and modify as necessary. Not all of the detail in the above first cut of data was eventually used, the Tiki was simplified and amended to help create data that was a little easier to work with.

After the line features were imported into CityEngine creating the blocks was a fairly simple process. For the town I didn't actually want to produce a city with roads, instead using the new water texture to make the city appear as a group of islands. I therefore made the roads as narrow as possible to accommodate as much land as possible within the shapes. After a period of clean up and further amendment of the roads I was happy with the first cut of parcels that I would use to create the city.


The parcel network that I created was made so that I could utilise a number of start rules to generate different building models based on the location within the Tiki. The eyes of the Tiki have been generated by using the skeleton subdivision, this has created what looks like a pie split into lots like slices based on minimum width and area values. The spike, above the nose, and the helmet (start rule names I have given these pieces) have not been subdivided at all and I will generate a model that covers the entire area. The rest of the head including the mouth and tongue have had the recursion subdivision applied which has also been applied to the rest of the body (suburbia).

In the next blog post I will run through some of the CGA code that was generated for each of the parts of the city.