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.


Sunday, 10 November 2013

TikiTown 3 - CGA creation - Tiki Spike


I really wanted the 'spike' of the Tiki to be a stand out feature and so decided that it would be the tallest part of the city. As previously mentioned, the Spike' starting geometry was a single parcel, this would allow me to create a building that is stepped in appearance. The code for this part of the city is posted at the bottom, along with a link to a web scene containing this feature.

TikiTown Spike

The spike would actually prove to be the easiest part of the city to code . It consists of just one input feature and so the code only has to work on one parcel. I wanted to create other parts of the city in a similar way and so I expected to use parts of this code again.

The first step was to again extrude the entire parcel to give the island like appearance, I then carried out a comp(f) split to selected the top as the base on which to keep building. The shape was then split in the longest (x) direction  into approx. 15m sections. The split index and split total values were stored which would be used to help create the stepped appearance. Instead of carrying the values continually I have set the values to two hidden attributes - SpikeIndex and SpikeTotal, these can be recalled at any point in time without having to duplicate any code. A 3m gap between each building footprint was created, except for the last one footprint where there is no need to create a separation. To do this, using the SpikeIndex and SpikeTotal I can select the just the last footprint and pass it to the next level whereas the rest of the footprints are subject to another split that takes the 3m away.


 The footprints are now ready to extrude and again I used the SpikeIndex and SpikeTotal values to generate a height value. I didn't really care what height that the buildings should be but that it should be taller than anything else in the city. The extrude function is a little complex in that it needed to include a reverse function so that the lowest index footprint receives the highest heights. This is achieved by subtracting the SpikeIndex from the SpikeTotal (plus one). The resulting height value is multiplied by 10 to give a nice height for extrusion.

The resulting building can now be split into floors and in this case instead of simply dividing by a single floor height I decided to make a little more use of the SplitIndex value and to use this as a way of splitting floors. I buildings that as they became taller the floor heights became smaller.


Another Comp(f) split was carried out to select the side facade of the buildings to which a 'y' split was carried out to generate a floor separator whose value is based on the height of the floor divided by 5. The last stage of the process was to create a simple facade that included tall, slim windows and to add colours to the entire complex. The outcome kind of reminds me of a casino / hotel in Las Vegas...



Here is a link to a web scene containing the Spike complex and the code for the TikiSpike is below:

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

version "2012.1"

##-----------------------------------------##
## Hidden Attributes ##
##-----------------------------------------##
@Hidden
attr SpikeIndex = 0

@Hidden
attr SpikeTotal = 0

##-----------------------------------------##
## Rules ##
##-----------------------------------------##

@startRule
Spike -->
      extrude(2)
      comp(f){top : Spike1 SpikeBaseTop | all : Base.}
     
Spike1 -->
      split(x){~15 : Spike2(split.index,split.total)}*           
     
Spike2(idx,n) -->
      set(SpikeIndex, idx)
      set(SpikeTotal, n)
      Spike3
     
Spike3 -->
      case SpikeIndex == SpikeTotal -1 : Spike4
      else : split(x){~12 : Spike4 | ~3 : NIL}

Spike4 -->
      extrude((SpikeTotal-SpikeIndex+1)*10)
      split(y){~SpikeIndex+1*3 : Spike5}*
     
Spike5 -->
      comp(f){side : SpikeFacade | top : SpikeRoofs | all : SpikeKeeper}     

SpikeFacade -->
      split(y){~1 : SpikeFacade1 | scope.sy/5 : SpikeFloorSeparator}
           
SpikeFacade1 -->
      case scope.sx < 1 : SpikeWall
      case scope.sx < 5 :
            split(x){{~scope.sx*0.5 : SpikeWall | ~1.2 : SpikeWindow}* | ~scope.sx*0.5 : SpikeWall  }     
      else :
            split(x){{~2 : SpikeWall | 1.2 : SpikeWindow}* | ~2 : SpikeWall }
           
SpikeWindow -->
      setback(0.1){all : SpikeWindowFrame | remainder : SpikeWindow1}  
     
SpikeWindowFrame -->
      extrude(0.3)
      t(0,-0.3,0)
      color("#808080")  #Grey
     
SpikeWindow1 -->
      t(0,0,-0.3)


SpikeFloorSeparator -->
      color("#808080")  #Grey
     
SpikeWall -->
      color("#003366")  #Dark Blue       
     
SpikeRoofs -->
      color("#808080")  #Grey
     
SpikeBaseTop --> 
      setupProjection(0, scope.xy, 2, 2)
      projectUV(0)
      texture("pavement.jpg")

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.

Sunday, 29 September 2013

Uses and users of CityEngine and CityEngine 2013


A recent article on Computer World outlined the use that had been made of CityEngine across a number of different industries. Recently, according to the article, the entertainment industry has used CityEngine to produce a number of cityscapes for films such as the latest reincarnation of Superman - Man of Steel, Cars 2 and Total Recall. Now, the pipeline for city generation should be made more efficient for this industry with the upcoming release of CityEngine 2013.



CityEngine 2013 sees the additon of an SDK (Software Development Kit) that will allow users of software such as Maya to procedurally generate models inside this software, instead of in CityEngine. The entertainment industry especially has pretty high standards when it comes to the quality of the visual output of CityEngine. Being able to marry up the procedural power of CityEngine and a high end graphics package like Maya we should be looking at some seriously awesome work.

The SDK is just one of a few small hints that Esri have released regarding CityEngine 2013. I have in the last week or so watched a few webinars through Directions Magazine. People such as Geoff Taylor and Eric Wittner have been bringing some of the latest information to the geospatial masses. There seems to be quite a few updates in the upcoming version but I've yet to see a clearly defined list of what's new.

It is obvious that Esri are having to service a number of different industries and so far the integration with the geospatial world has been steady. Obviously the SDK is made to service the entertainment industry. This got me thinking what industries would be the main users of CityEngine:

1. Government - Planning, Engineering, Architecture, Urban Design
2. Entertainment - Films, Advertising, Gaming
3. Military - Simulation

I'm pretty sure that there could be a wide range of other uses too. CityEngine is by name a city generation tool. However, many of the operations could be used to create all kinds of models based on any kind of input geometry. Making the operations work on a project that isn't city based could be a good way of showing the potential of other industries also using the software. I'll have to have a think about that and come up with something that is fun, will teach me some new techniques and that could be applicable to an industry.

Getting back to the Computer World article, Gert van Maren mentions some of the work that Auckland Council have done regarding a land resource document called the Unitary Plan. This is one of the major projects that I've been lucky enough to work on and I'll be reporting on some of the things I learnt in carrying out this project in upcoming posts.


Thursday, 26 September 2013

(Re)Tweet City - A great use of CityEngine from en-topia


Please take a look at the outstanding blog post here from en-topia. They have even been kind enough to provide sample rules and instructions through GitHub

If you're not into the developer world or like me just getting into it, the number of API's seems to increase ten-fold by the day into a dizzying array of wonderful tools that allow access to all kinds of exciting data. One of these API's is the Twitter API. My very shallow knowledge of the API is that it can be used to carry out a search of tweets, providing back a list that, if geotagged, can be mapped.

The guys at en-topia have taken the simple 2D mapping of Twitter data another step forward by applying geotagged tweets to buildings. The 2D building data is plugged into CityEngine and a rule file is applied giving the building an extrusion based on the number of tweets located in or around that building. From what I can take from their article they are able to take live feeds from Twitter (or a search result over a given period of time) and feed this into CityEngine, outputting a time-lapse style video. Of course the tweets can be applied to any 2D data you wish, in another example they show the data applied to a 1km grid instead of buildings. Depending on the scale of the area you wish to show this is a great example of how easily CityEngine can be used to create the same type of output but for different inputs but based on the same attributes.

I guess the next step, which is in the hands of Esri, would be to make it possible to create a 3D Web Scene that itself can handle ongoing updates and show 'live' data that is supplied by a pipeline of (Python?) processes.

This is such a great use of CityEngine, I think anyone who sees this and understands the power of the Twitter API and/or CityEngine instantly sees the potential to make something similar for their area of expertise.

Tuesday, 17 September 2013

CityEngine Starter Project 7 - Lessons learnt and the full rule file


Lessons learnt

As a result of this project I had learnt a number of things regarding rule file creation:

  • How to structure the rule file
  • How to create attributes and give them additional information such as a range
  • How to create functions and loops within the rule file
  • How to create a conditional (case) statement

Within the rule file I also was able to understand and use a number of CityEngine operations including:

  • Split
  • Extrude
  • Comp
  • Align
  • Set
  • Report
  • Setback
  • Roof operations
  • Translate
Looking back at what I intended to achieve a result of the project, I think I covered everything pretty well. I created a basic street and parcel network, however, I think that maybe some of the parcels are either to big and / or I could have added more streets. I created a rule file that included some very basic planning rules, I will of course include more rules in future but this will also require me to understand what the planning rules are, what they intend to allow or not allow to happen on a zone and how this can be translated into a rule file. I also created an output visualisation, this step was by far the easiest and using the Web Scene technology is something that I very much look forward to utilising again in future. 

The full rule file

Below is the full rule file created for this project:


/**

 * File:    StarterProject.cga
 * Created: 01 Jan 2013 01:02:07 GMT
 * Author:  ellawayc
 */

version "2012.1"

## Hidden Attributes ##
@Hidden
attr ParcelArea = 0
@Hidden
attr FootprintNeeded = 0
@Hidden
attr Length = 0
@Hidden
attr Width = 0

## Attributes ##

@Range("Residential","Commercial")
attr ZONETYPE = "Residential"
@Range(0,5)
attr YARD = 2.5
@Range(25,100)
attr COVERAGE = 30

## Functions ##

getHeight =
      25% : 8.5
      50% : 12.5
      else : 16.5

## Constants ##

## Assets ##

## Rules

@startRule

Lot -->
      alignScopeToGeometry(yUp, auto)
      set(ParcelArea, geometry.area)
      report("A Parcel Area (m2)", ParcelArea)
      Lot1
     
Lot1 -->
      case ZONETYPE == "Residential" : ResidentialLot
      else : CommercialLot

##### Residential Zone ##### 
ResidentialLot -->
      setback(YARD) {street.front : NIL  | remainder : ResidentialLot2}      
     
ResidentialLot2 -->
      innerRect
      shapeL(scope.sz*0.75,scope.sx*0.75) {shape : ResidentialLot3 | remainder : NIL}

ResidentialLot3 -->
      set(FootprintNeeded, (ParcelArea/100)*COVERAGE)
      report("B Footprint Needed (m2)", FootprintNeeded)   
      ResidentialLot4

ResidentialLot4 -->
      case geometry.area > FootprintNeeded : s('0.99,0,'0.99) center(xz) ResidentialLot4
      else : ResidentialLot5
     
ResidentialLot5 -->    
      extrude(world.y, 5)
      split(y){~0.25 : ResidentialLot6 BaseKeeper | 5 : NIL}     
     
ResidentialLot6 -->
      comp(f){top : alignScopeToGeometry(yUp, auto) ResidentialLot7}   

ResidentialLot7 -->    
      extrude(5.5)     
      comp(f){top : Roof | all : ResidentialLot8}    
     
Roof -->
      roofHip(18.5, 0.4)     
     
##### Commercial Zone #####  
CommercialLot -->
      innerRect  
      alignScopeToGeometry(yUp, 0, longest)    
      set(Length, scope.sx)
      set(Width, scope.sz)
      report("B Length (m)", Length)
      report("C Width (m)", Width)
      CommercialLot1
     
CommercialLot1 -->
      20% : CommercialLot3
            else :
                  case ParcelArea < 500 : CommercialLot3
                  case Width < 15 :
                        50% : setback(4){street.left : NIL | remainder : CommercialLot2}
                        else :      setback(4){street.right : NIL | remainder : CommercialLot2}
                  else :
                        50% : setback(scope.sz*0.2){street.left : NIL | remainder : CommercialLot2}
                        else :      setback(scope.sz*0.2){street.right : NIL | remainder : CommercialLot2}
                 
CommercialLot2 -->
      20% : CommercialLot3
            else :
                  case Length > 30 : setback(scope.sx*0.3){street.back : NIL | remainder : CommercialLot3}
                  else :
                        50% : setback(4){street.back : NIL | remainder : CommercialLot3}             
                        else : CommercialLot3
                 
CommercialLot3 -->
      extrude(world.y, 5)
      split(y){~0.25 : CommercialLot4 BaseKeeper | 5 : NIL}                        
                 
CommercialLot4 -->
      comp(f){top : alignScopeToGeometry(yUp, auto) CommercialLot5}                
     
CommercialLot5 -->
      extrude(getHeight)                 
      split(y){4.5 : GroundFloor | {~4 : UpperFloor(split.index, split.total)}*}         
                 
GroundFloor -->
      comp(f){side: GroundFloor1}              
                 
GroundFloor1 -->
      split(x){0.5 : Wall | ~5 : LargeWindow | 3 : Entrance | {~5 : LargeWindow}* | 0.5 : Wall}
                 
UpperFloor(idx,n) -->              
      case idx == n-1 : TopFloor UpperFloor1
      else : UpperFloor1           

UpperFloor1 -->
      comp(f){side: UpperFloor2}
     
UpperFloor2 -->
      split(x){0.5 : Wall | {~2.5 : Window}* | 0.5 : Wall}                   
     
LargeWindow -->
      split(x){0.25 : SolidWall | ~5 : split(y){0.50 : SolidWall | ~1 : LargeWindow1 | 0.25 : SolidWall} | 0.25 : SolidWall}                 
                 
LargeWindow1 -->
      t(0,0,-0.4)
           
Window -->
      split(x){0.2 : SolidWall | ~2.5 : split(y){0.2 : SolidWall | ~1 : Window1 | 0.2 : SolidWall} | 0.2 : SolidWall}            

Window1 -->
      t(0,0,-0.2)

Entrance -->
      split(x){0.2 : SolidWall | ~2.5 : split(y){~1 : Entrance1 | 0.35 : SolidWall} | 0.2 : SolidWall}
     
Entrance1 -->
      t(0,0,-0.4)
           
SolidWall -->
      extrude(0.4)
      t(0,-0.4,0)            
     
TopFloor -->
      comp(f){top : CommercialRoof}
     
CommercialRoof -->
      extrude(0.15)    
      comp(f){top : CommercialRoof1 RoofKeeper | all : RoofKeeper }    

CommercialRoof1 -->
      setback(0.4){all : CommercialRoof2}
     
CommercialRoof2 -->
      extrude(0.4)