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")

No comments:

Post a Comment