/**
 * File:    textured_city.cga
 * Created: 
 * Author:  
 * 
 */

version "2019.0"


#
# City Wizard Example Rule
#
# Notes: 
# - since city wizard examples have to run on arbitrary lot sizes, lots might be subdivided further in the grammar
# - the different building types are depending on the distance to origin (usually this is controlled via landuse maps etc)
#



# ----------------------------------------
# Buildings
# ----------------------------------------

# will be overridden by corresponding attribute of initial shape

# constants
const radiusHighriseArea   = 400
const radiusCommercialArea = 800
const radiusApartmentArea = 1200
const radiusResidentialArea = 1600

# Determine a building type depending on distance to world-origin and some stochastics 
# 0: highrise
# 1: commercial buildings
# 2: apartment blocks
# 3: residential
# 4: openspace
distanceToCenter = sqrt(initialShape.origin.px*initialShape.origin.px +initialShape.origin.pz*initialShape.origin.pz)
attr buildingType =
	case distanceToCenter < radiusHighriseArea   :  65%: 0 33%: 1   2%: 2 else: 4     # mainly highrises and commercial
	case distanceToCenter < radiusCommercialArea :  19%: 0 70%: 1  10%: 2 else: 4     # mainly commercial
	case distanceToCenter < radiusApartmentArea  :  20%: 1 76%: 2   3%: 3 else: 4     # mainly apartments 
	case distanceToCenter < radiusResidentialArea:   5%: 1 40%: 2  53%: 3 else: 4     # mainly residential and apartments
	else                                         :   1%: 1  4%: 2  90%: 3 else: 4     # more or less only residential
	
attr buildingHeightFactor = 1

attr nightMode = true // switch between night and day textures



Lot --> 
	translate(rel,world,0,sidewalkHeight,0) LotAligned

LotAligned -->
	case buildingType == 4: YardGround Trees
	case buildingType == 3: ResidentialBlock
	case buildingType == 2: ApartmentBlock
	case buildingType == 1: CommercialBlock
	else                  : HighriseBlock

LotInner --> 
	translate(rel,world,0,sidewalkHeight,0) LotInnerAligned

LotInnerAligned -->
	case buildingType == 4: YardGround Trees 
	case buildingType == 3: 30% : ResidentialBlock else: YardGround Trees 
	case buildingType == 2: 20% : ApartmentBlock    10%: ParkingLot     else: YardGround Trees 
	case buildingType == 1: 30% : CommercialBlock   30%: ParkingLot		else: YardGround Trees
	else                  : 70% : HighriseBlock     25%: ParkingLot		else: YardGround Trees


LotCorner --> Sidewalk

# ----------------------------------------
# Residential

# here we subdivide the initial shape further in case it is too big (to get kind of row housing...)
ResidentialBlock -->
	case scope.sx > 30:
		split(x){ ~18: ResidentialBlock }*
	case geometry.isConcave && scope.sx > 25:	# concaves are subdivided further (trying to get rid of the concavity)
		split(x){ ~14: ResidentialBlock }*
	case geometry.isConcave && scope.sz > 25:
		split(z){ ~14: ResidentialBlock }*
	case geometry.isRectangular(25) && scope.sz > scope.sx*2:
		split(z){ ~1: alignScopeToGeometry(yUp,2) ResidentialLot | ~1: ResidentialLot }
	case geometry.isRectangular(15) && scope.sz > scope.sx*3:
		split(z){ ~1: alignScopeToGeometry(yUp,2) ResidentialLot | ~1: ResidentialLot }
	else:
		ResidentialLot

ResidentialLot -->
	YardGround ResidentialLotAlign

ResidentialLotAlign -->
	case geometry.nVertices < 4: 
		Trees
	case !geometry.isRectangular(10):
		innerRect
        split(x){ 1 : NIL 
                | 'rand(.85,.9): split(z){ ~rand(1,3): Trees | (rand(15,20)): ResidentialBuildingArea | ~1: Trees }
                | 1 : NIL }
	else:
		split(x){ 1 : NIL 
				| 'rand(.85,.9): split(z){ ~rand(1,3): Trees | (rand(15,20)): ResidentialBuildingArea | ~1: Trees }
				| 1 : NIL }

ResidentialBuildingArea -->
	case scope.sx < 10 || scope.sz < 10:  # too small to build a house
		NIL
	else:
		alignScopeToAxes(y) s('1,0,'1) # there are different possibilites to handle non-horizontal lots... scaling it to zero in the y-direction like here is the simplest way to handle it
		ResidentialBuilding

# generate some mass models according to a few very simple rules
ResidentialBuilding -->
		ResidentialShape
		ResidentialExtension
		ResidentialGarage
	
ResidentialShape -->
	50% : s('1.0,0,'0.6) center(xz) ResidentialShape(rand(5,7))
	20% : s('0.8,0,'0.5) center(xz) ResidentialShape(rand(4.5,5.5))
	else: s('0.8,0,'0.6) center(xz) ResidentialShape(rand(5,7))
	
ResidentialShape(h) -->	
	15% : Mass(h,"gable")
	5%  : Mass(h,"hip")
	17% : [ s('1.0,0,'0.8) Mass(h,"gable") ] split(x){ 'rand(0.35,0.6): t(0,0,'0.4) s('1,0,'rand(0.45,0.65)) Mass(h,"gable2") } # L-shape left for gable roofs
	17% : [ s('1.0,0,'0.8) Mass(h,"hip")   ] split(x){ 'rand(0.35,0.6): t(0,0,'0.1) s('1,0,'rand(0.8,0.95)) Mass(h,"hip")     } # L-shape left for hip roofs
	17% : [ s('1.0,0,'0.8) Mass(h,"gable") ] split(x){ ~1: NIL | 'rand(0.35,0.6): t(0,0,'0.4) s('1,0,'rand(0.45,0.65)) Mass(h,"gable2") } # L-shape right for gable roofs
	17% : [ s('1.0,0,'0.8) Mass(h,"hip")   ] split(x){ ~1: NIL | 'rand(0.35,0.6): t(0,0,'0.1) s('1,0,'rand(0.8,0.95))  Mass(h,"hip")    } # L-shape right for hip roofs
	5%  : [ s('1.0,0,'0.8) Mass(h,"gable") ] split(x){ ~1: NIL | 'rand(0.35,0.6): t(0,0,'0.4) s('1,0,'rand(0.55,0.65)) Mass(h,"gable2") | ~1: NIL } # T-shape for gable roofs
	5%  : [ s('1.0,0,'0.8) Mass(h,"hip")   ] split(x){ ~1: NIL | 'rand(0.35,0.6): t(0,0,'0.1) s('1,0,'rand(0.8,0.95))  Mass(h,"hip")    | ~1: NIL } # T-shape for hip roofs
	1%  : [ s('1.0,0,'0.8) Mass(h,"gable") ] t(0,0,'0.4) s('1,0,'rand(0.55,0.65)) split(x){ 'rand(0.3,0.4): Mass(h,"gable2") | ~1: NIL | 'rand(0.3,0.4): Mass(h,"gable2") }  # U-shape for gable roofs
	else: [ s('1.0,0,'0.8) Mass(h,"hip")   ] t(0,0,'0.1) s('1,0,'rand(0.8,0.95))  split(x){ 'rand(0.3,0.4): Mass(h,"hip")    | ~1: NIL | 'rand(0.3,0.4): Mass(h,"hip")    }  # U-shape for hip roofs
	
ResidentialExtension -->
	7%  : split(z){ (rand(1,3)): NIL | ~1: Mass(rand(3,4),extensionRoofType) | (rand(1,3)): NIL }
	18% : split(z){ '(rand(.4,.7)): split(x){ '(rand(.3,.7)): Mass(rand(3,4.5),extensionRoofType) } }
	18% : split(z){ '(rand(.2)): NIL | '(rand(.2,.5)): split(x){ '(rand(.3,.7)): Mass(rand(3,4.5),extensionRoofType) } }
	18% : split(z){ '(rand(.4,.7)): split(x){ ~1: NIL | '(rand(.3,.7)): Mass(rand(3,4.5),extensionRoofType) } }
	18% : split(z){ '(rand(.2)): NIL | '(rand(.2,.5)): split(x){ ~1: NIL | '(rand(.3,.7)): Mass(rand(3,4.5),extensionRoofType) } }
	else: NIL

extensionRoofType = 70%: "gable" else: "hip"	
garageRoofType = 20%: "gable" else: "flat"
	
ResidentialGarage -->
	46% : split(z){ ~1: NIL | '(rand(.4,.5)): split(x){ (rand(5,8)): Mass(rand(2,3),garageRoofType) } | '(rand(.2)): NIL }
	46% : split(z){ ~1: NIL | '(rand(.4,.5)): split(x){ ~1: NIL | (rand(5,8)): Mass(rand(2,3),garageRoofType) } | '(rand(.2)): NIL }
	else: NIL


	
Mass(h,rooftype) -->
	extrude(h*buildingHeightFactor) Stories comp(f){top: Roof(rooftype) }

Stories -->
	comp(f){ side: Facade }

const roofAngle = rand(20,30)
const roofOverhang = 30%: 0 else: rand(0.5,1.5)

Roof(rooftype) -->
	case rooftype == "rooffloorRect":
		90% : FlatRoofTexture innerRect [ s('(rand(0.6,0.9)),'(rand(0.6,0.9)),'1) center(xy) alignScopeToGeometry(yUp,auto) RoofFloorShape(rand(2,3)) ]
		else: FlatRoofTexture
	case rooftype == "rooffloor":
		70% : FlatRoofTexture innerRect [ s('(rand(0.6,0.9)),'(rand(0.6,0.9)),'1) center(xy) alignScopeToGeometry(yUp,auto) RoofFloorShape(rand(2,3)) ]
		25% : FlatRoofTexture innerRect [ s('(rand(0.8,0.95)),'(rand(0.8,0.95)),'1) center(xy) alignScopeToGeometry(yUp,auto) ResidentialShape(rand(2,3)) ]
		else: FlatRoofTexture
	case rooftype == "gable1":
		roofGable(roofAngle,roofOverhang,roofOverhang,false,1) comp(f){ bottom: NIL | vertical: Facade | all: RoofPlane }
	case rooftype == "gable2":
		roofGable(roofAngle,0,roofOverhang,false,0) comp(f){ bottom: NIL | vertical: Facade | all: RoofPlane }
	case rooftype == "gable":
		roofGable(roofAngle,roofOverhang) comp(f){ bottom: NIL | vertical: Facade | all: RoofPlane }
	case rooftype == "hip":
		roofHip(roofAngle,roofOverhang) comp(f){ bottom: NIL | all: RoofPlane }
	else: 
		FlatRoofTexture 

RoofFloorShape(h) -->	
	split(z){ ~1: RoofFloorBack(h) | 'rand(0.5,0.7): Mass(h,"flat") }
		
RoofFloorBack(h) -->
	20% : split(x){ 'rand(0.2,0.8): Mass(h,"flat") } # L-shape left
	20% : split(x){ ~1: NIL | 'rand(0.2,0.8): Mass(h,"flat") } # L-shape right
	40% : split(x){ ~(rand): NIL | (rand(0.8,1.5)*scope.sz):  Mass(h,"flat") | ~(rand): NIL } # T-shape
	10% : split(x){ 'rand(0.2,0.45): Mass(h,"flat") | ~1: NIL | 'rand(0.2,0.45): Mass(h,"flat") }  # U-shape
	else: NIL



# ----------------------------------------
# Apartments

ApartmentBlock -->
	case scope.sx > 60:
		split(x){ (rand(35,50)): ApartmentLot | ~1: ApartmentBlock }
	else:
		ApartmentLot

ApartmentLot -->
	YardGround ApartmentLotAlign

ApartmentLotAlign -->
	case geometry.nVertices < 4 || geometry.isConcave:   # we do not want ghery-like residential buildings ;-) ... and innerRect does not yet work on concave shapes
		Trees
	case !geometry.isRectangular(10):
		innerRect
        split(x){ 1 : NIL 
                | 'rand(.9,1.1): split(z){ ~rand(1,3): NIL | (rand(20,30)): ApartmentBuildingArea | ~1: NIL }
                | 1 : NIL }
	else:
		split(x){ 1 : NIL 
				| 'rand(.9,1.1): split(z){ ~rand(1,3): NIL | (rand(20,30)): ApartmentBuildingArea | ~1: NIL }
				| 1 : NIL }

ApartmentBuildingArea -->
	case scope.sx < 20:  # too small to build a house
		NIL
	else:
		alignScopeToAxes(y) s('1,0,'1) # there are different possibilites to handle non-horizontal lots... scaling it to zero in the y-direction like here is the simplest way to handle it
		ApartmentBuilding

ApartmentBuilding -->
	50% : s('1.0,0,'0.7) center(xz) ApartmentShape(rand(8,12))
	20% : s('0.8,0,'0.5) center(xz) ApartmentShape(rand(7,10))
	else: s('0.8,0,'0.6) center(xz) ApartmentShape(rand(7,11))

ApartmentShape(h) -->
	3%  : Mass(h,"gable")
	1%  : Mass(h,"hip")
	16% : [ s('1.0,0,'0.9) Mass(h,"gable") ] split(x){ 'rand(0.1,0.2): t(0,0,'0.45) s('1,0,'rand(0.55,0.7)) Mass(h,"gable") } # L-shape left for gable roofs
	8%  : [ s('1.0,0,'0.9) Mass(h,"hip")   ] split(x){ 'rand(0.1,0.2): t(0,0,'0.10) s('1,0,'rand(0.85,1.0)) Mass(h,"hip")     } # L-shape left for hip roofs
	16% : [ s('1.0,0,'0.9) Mass(h,"gable") ] split(x){ ~1: NIL | 'rand(0.1,0.2): t(0,0,'0.45) s('1,0,'rand(0.55,0.7)) Mass(h,"gable") } # L-shape right for gable roofs
	8%  : [ s('1.0,0,'0.9) Mass(h,"hip")   ] split(x){ ~1: NIL | 'rand(0.1,0.2): t(0,0,'0.10) s('1,0,'rand(0.85,1.0))  Mass(h,"hip")    } # L-shape right for hip roofs
	16% : [ s('1.0,0,'0.9) Mass(h,"gable") ] split(x){ ~(rand): NIL | 'rand(0.1,0.2): t(0,0,'0.45) s('1,0,'rand(0.55,0.7)) Mass(h,"gable") | ~(rand): NIL } # T-shape for gable roofs
	8%  : [ s('1.0,0,'0.9) Mass(h,"hip")   ] split(x){ ~(rand): NIL | 'rand(0.1,0.2): t(0,0,'0.10) s('1,0,'rand(0.85,1.0))  Mass(h,"flat")  | ~(rand): NIL } # T-shape for hip roofs
	16% : [ s('1.0,0,'0.9) Mass(h,"gable") ] t(0,0,'0.45) s('1,0,'rand(0.55,0.7)) split(x){ 'rand(0.1,0.2): Mass(h,"gable") | ~1: NIL | 'rand(0.2,0.3): Mass(h,"gable") }  # U-shape for gable roofs
	else: [ s('1.0,0,'0.9) Mass(h,"hip")   ] t(0,0,'0.10) s('1,0,'rand(0.85,1.0)) split(x){ 'rand(0.1,0.2): Mass(h,"flat")    | ~1: NIL | 'rand(0.2,0.3): Mass(h,"flat")    }  # U-shape for hip roofs


# ----------------------------------------
# Commercials

commercialBuildingWidth = 70%: rand(15,25) else: rand(25,80)

CommercialBlock -->
	CommercialRow(0)

CommercialRow(count) -->
	case scope.sx > 30:
		split(x){ commercialBuildingWidth: CommercialLot(count) | ~1: CommercialRow(count+1) }
	else:
		CommercialLot(0)

CommercialLot(count) -->
	case geometry.nVertices < 4 || geometry.isConcave:  
		ParkingLot FewTrees
	else:
		case count == 0 && p(0.5):
			  ParkingLot CommercialBuildingArea(0)
		else:
			40% : ParkingLot split(z){ '(rand(0.2,0.4)): NIL | ~1: CommercialBuildingArea(1) | (rand(4)): NIL }		# 20% with a setback
			else: ParkingLot split(z){ '(rand(0.2,0.4)): NIL | ~1: CommercialBuildingArea(1) }

CommercialBuildingArea(count) -->
	case scope.sx < 10 || scope.sz < 10:  # too small to build a house
		NIL
	else:
		alignScopeToAxes(y) s('1,0,'1) # there are different possibilites to handle non-horizontal lots... scaling it to zero in the y-direction like here is the simplest way to handle it
		CommercialBuilding(count)
		
CommercialBuilding(count) -->
	case scope.sx > 40:
		CommercialShape(count,rand(16,30))
	else:
		CommercialShape(count,rand(10,20))
		
CommercialShape(count,h) -->
	case scope.sx > 20:
		split(z){ ~1: CommercialBack(h) | 'rand(0.7,0.9): Mass(h,"rooffloor") }
	case count != 0:
		split(z){ ~1: CommercialBack(h) | 'rand(0.85,0.95): Mass(h,"rooffloor") }
	else:
		 Mass(h,"flat")		
		
 CommercialBack(h) -->
	20% : split(x){ 'rand(0.2,0.8): Mass(h,"flat") } # L-shape left
	20% : split(x){ ~1: NIL | 'rand(0.2,0.8): Mass(h,"flat") } # L-shape right
	50% : split(x){ ~(rand): NIL | (rand(0.8,1.5)*scope.sz):  Mass(h,"flat") | ~(rand): NIL } # T-shape
	else: split(x){ 'rand(0.2,0.45): Mass(h,"flat") | ~1: NIL | 'rand(0.2,0.45): Mass(h,"flat") }  # U-shape

# ----------------------------------------
# Highrise

HighriseBlock -->
	HighriseBlock(0)
	
HighriseBlock(index) -->
	case index >= geometry.nEdges:
		ParkingLot
	case scope.sz > 50:
		split(z){ ~1: alignScopeToGeometry(yUp,index+1) HighriseBlock(index+1) | (rand(40,80)): HighriseRow }
	else:
		alignScopeToGeometry(yUp,index+1) HighriseRow
		
HighriseRow -->
	case scope.sx > 60:
		split(x){ (rand(40,80)): HighriseLot | ~1: HighriseRow }
	else:
		HighriseLot

HighriseLot  -->
	case scope.sx < 20 || scope.sz < 20:  # too small to build a house
		ParkingLot
	else:
		ParkingLot
		alignScopeToAxes(y) s('1,0,'1) # there are different possibilites to handle non-horizontal lots... scaling it to zero in the y-direction like here is the simplest way to handle it
		HighriseBuilding(rand(6,10))

# distance to center controls the building height		
highriseBuildingHeight =
	case distanceToCenter < radiusHighriseArea/4 && geometry.area > 400 : rand(100,250)  
	case distanceToCenter < radiusHighriseArea/2 && geometry.area > 200 : rand(80,150)  
	case distanceToCenter < radiusHighriseArea   && geometry.area > 150 : rand(40,80)  
	case distanceToCenter < (radiusHighriseArea+radiusCommercialArea)/2      : rand(30,60)  
	else :  rand(20,50)
		
HighriseBuilding(lowerH) -->
	20% : Mass(lowerH,"flat") t(0,lowerH,0) s('0.7,'1,'0.7) center(xz) HighriseUpper
	70% : Mass(lowerH,"flat") t(0,lowerH,0) s('0.8,'1,'0.8) center(xz) HighriseUpper
	else: Mass(lowerH,"flat") t(0,lowerH,0) HighriseUpper


HighriseUpper -->
	case distanceToCenter < radiusHighriseArea/2 :  HighriseTower
	case distanceToCenter < radiusHighriseArea/2 :  70%: HighriseTower else: HighriseShape
	case distanceToCenter < radiusHighriseArea   :  30%: HighriseTower else: HighriseShape
	else                                         :  HighriseShape
	
HighriseShape -->
	case scope.sz > 40 && p(0.5):
		split(z){ 'rand(.1,.3): NIL | ~1: HighriseShape }
	else:
		70% : LShape(highriseBuildingHeight)
		20% : UShape(highriseBuildingHeight)
		else: split(z){ ~1: NIL | 'rand(.4,.6): HighriseMass }
	
LShape(h) --> 
	split(z){ ~1: LShapeWing(h) | 'rand(.4,.6): HighriseMass(h) }

LShapeWing(h) -->
	50% : split(x){ ~1 : NIL | 'rand(0.4,0.6) : Wing(h) }  // right side
	else: split(x){ 'rand(0.4,0.6) : Wing(h) }				// left side

UShape(h) -->
	case scope.sx > scope.sz:
		split(z){ ~1: UShapeWings(h) | 'rand(0.4,0.7): HighriseMass(h) }
	else:
		split(z){ 'rand(.3,.4): HighriseMass(h) | ~1: CHWing(h) | 'rand(.3,.4): HighriseMass(h) }
		
UShapeWings(h) -->
	split(x){ 'rand(0.3,0.4): Wing(h) | ~1: NIL | 'rand(0.3,0.4): Wing(h) }

Wing(h) -->
	30% : HighriseMass(h)  						// same height
	30% : HighriseMass(h*rand(0.5,0.9))	        // lower height 1
	else: HighriseMass(highriseBuildingHeight*rand(0.5,0.7))	// lower height 2
	
CHWing(h) -->
	40% : split(x){ ~1: NIL | 'rand(0.5,0.7): HighriseMass(h) | ~1: NIL }
	30% : split(x){ 'rand(0.5,0.7): HighriseMass(h) }
	else: split(x){ ~1: NIL | 'rand(0.5,0.7): HighriseMass(h) }			

HighriseMass -->
	Mass(highriseBuildingHeight,"rooffloorRect")	
	
HighriseMass(h) -->
	Mass(h,"rooffloorRect")	


		
HighriseTower -->
	case geometry.isConcave:
		alignScopeToGeometry(yUp,auto) InternationalTower
	case scope.sx > 20 && scope.sz > 20:
		alignScopeToGeometry(yUp,auto) innerRect SetbackTower
	else: 
		LShape(highriseBuildingHeight*0.5)

SetbackTower -->
	case scope.sx < 20 || scope.sz < 20:
		LShape(highriseBuildingHeight*0.3)
	case scope.sx < 30:
		[ s('1,'1,'0.8) center(z) extrude(highriseBuildingHeight*buildingHeightFactor*rand(0.7,0.95)) RecursiveSetbacks ]
		[ s('0.6,'1,'1.0) center(x) extrude(highriseBuildingHeight*buildingHeightFactor) RecursiveSetbacks ]
	else: 
		50% : 
			[ s('1,'1,'0.7) center(z) extrude(highriseBuildingHeight*buildingHeightFactor*rand(0.7,0.95)) RecursiveSetbacks ]
			[ s('0.6,'1,'1.0) center(x) extrude(highriseBuildingHeight*buildingHeightFactor) RecursiveSetbacks ]
		else: 
			[ s('1,'1,'0.6) center(z) extrude(highriseBuildingHeight*buildingHeightFactor) RecursiveSetbacks ]
			[ s('0.8,'1,'1.0) center(x) extrude(highriseBuildingHeight*buildingHeightFactor*rand(0.7,0.95)) RecursiveSetbacks ]
	
lowHeight 	= 50% : 0.3 else: 0.7	// switching between these two values creates visually appealing setbacks
const scale 	= rand(0.7,0.9)		// has to be constant

RecursiveSetbacks -->
	case scope.sy > 10 && scope.sx > 10 &&  scope.sz > 10:
		split(y){ 'lowHeight : Stories comp(f){top: Roof("rooffloorRect") } | ~1: s('scale,'1,'scale) center(xz) RecursiveSetbacks }
	else: 
		s('1,5,'1) Stories comp(f){top: Roof("rooffloorRect") }												
			
		
InternationalTower -->
	case scope.sx < 30:
		HighriseMass(highriseBuildingHeight*0.6)
	case p(0.7):
		split(z){ scope.sx*0.4: HighriseMass }
	else: 
		LShape(highriseBuildingHeight*0.7)


# ----------------------------------------
# Facade Texturing
# ----------------------------------------

const floorheight = rand(3.5,4.5)
const tilewidth   = floorheight/4*3

nFloors = ceil(scope.sy/floorheight)
nTiles  = ceil(scope.sx/tilewidth)

# first we determine which facade to take according to its number of floors
attr texFloors = 
	case buildingType == 0		: 60
	case nFloors <= 3 && p(0.4) : 3
	case nFloors <= 4 && p(0.4) : 4
	case nFloors <= 5 && p(0.4) : 5
	case nFloors <= 6 && p(0.4) : 6
	case nFloors <= 7 && p(0.4) : 7
	else						: 60


# get a random texture with n floors
const getFacade =	# string with potential texture, "high enough to use as texture"
	case nightMode == true:
		fileRandom("assets/textured/fac_*_floors"+texFloors+"*_night.jpg")	# truncate the file name to get actual number of floors
	else:
		fileRandom("assets/textured/fac_*_floors"+texFloors+"*_day.jpg")

const getFacadeReflMap = replace(getFacade, "day", "refl")

getFloors 	= float(getRange(getFacade,"floors","_tiles"))					



getTiles 	= float(getRange(getFacade,"tiles",".jpg"))						# truncate the file name to get actual number of tiles from the upper file

# align the texture projection height to the current facade so that the texture floors fit	
getFacadeTexProjectionHeight = scope.sy/nFloors*getFloors

# align the texture projection width to the current facade so that the texture tiles fit	
getFacadeTexProjectionWidth = scope.sx/nTiles*getTiles

# set facade texture and project the texture coordinates accordingly 
Facade -->
	setupProjection(0,scope.xy,getFacadeTexProjectionWidth,getFacadeTexProjectionHeight) projectUV(0)
	FacadeTexture

	
FacadeTexture -->
	case nightMode :
		texture(getFacade)
	else : 
		texture(getFacade)
		set(material.specular.r,3) set(material.specular.g,3) set(material.specular.b,3)		# for the looks in the CityEngine we add some over-exagerated specularity (otherwise the model is too dark for opengl...)
		set(material.specularmap, getFacadeReflMap)
		set(material.shininess,20)
		
	

getFlatRoofTexture =
	case nightMode :
		fileRandom("assets/textured/flatroof*_night.jpg")
	else:
		fileRandom("assets/textured/flatroof*_day.jpg")

FlatRoofTexture -->
	alignScopeToGeometry(zUp,auto)
	setupProjection(0,scope.xy,'1,'1) projectUV(0)
	texture(getFlatRoofTexture)

const getRoofTexture =
	case nightMode :
		fileRandom("assets/textured/rooftile*_night.jpg")
	else:
		fileRandom("assets/textured/rooftile*_day.jpg")

const getTextureSizeU = rand(3,5)
const getTextureSizeV = getTextureSizeU*rand(2,5)
const getDirtmapTexture = fileRandom("assets/textured/dirtmap.*.512x512.jpg")

RoofPlane -->
	texture(getRoofTexture) setupProjection(0,scope.xy,getTextureSizeU,getTextureSizeV) projectUV(0) 
	set(material.dirtmap,getDirtmapTexture) setupProjection(2,scope.xy,'1,'1) projectUV(2) 

ParkingLot -->
	case nightMode :
		alignScopeToGeometry(zUp,0,longest) 
		texture(imageBestRatio("assets/textured/parkingLots*_night.jpg","yx"))
		setupProjection(0,scope.yx,scope.sy,scope.sx) projectUV(0)
		set(material.dirtmap,getDirtmapTexture) setupProjection(2,scope.yx,'1,'1) projectUV(2) 
	else:
		alignScopeToGeometry(zUp,0,longest) 
		texture(imageBestRatio("assets/textured/parkingLots*_day.jpg","yx"))
		setupProjection(0,scope.yx,scope.sy,scope.sx) projectUV(0)
		set(material.dirtmap,getDirtmapTexture) setupProjection(2,scope.yx,'1,'1) projectUV(2) 


YardGround -->
	case nightMode :
		alignScopeToGeometry(zUp,0,longest) 
		texture(imageBestRatio("assets/textured/yard*_night.jpg","yx"))
		setupProjection(0,scope.yx,'1,'1) projectUV(0)
		set(material.dirtmap,getDirtmapTexture) setupProjection(2,scope.yx,'1,'1) projectUV(2) 
	else:
		alignScopeToGeometry(zUp,0,longest) 
		texture(imageBestRatio("assets/textured/yard*_day.jpg","yx"))
		setupProjection(0,scope.yx,'1,'1) projectUV(0)
		set(material.dirtmap,getDirtmapTexture) setupProjection(2,scope.yx,'1,'1) projectUV(2) 


# ----------------------------------------
# Trees
# ----------------------------------------


attr showTrees = false # switching it on for all lots will need some resources ;-)

FewTrees --> 
	case showTrees:
		30% : split(x){ (rand(8,11)): Tree }*
		20% : split(z){ (rand(11,15)): Tree }*
		10%  : split(x){ (rand(13,18)): Tree }*
		else: NIL
	else: 
		NIL

Trees --> 
	case showTrees:
		40% : split(x){ (rand(2,5)): Tree }*
		30% : split(z){ (rand(3,7)): Tree }*
		20% : split(x){ (rand(5,10)): Tree }*
		else: NIL
	else: 
		NIL

treeasset = 
	case nightMode : "textured/tree_night.obj"
	else : "textured/tree_day.obj"

Tree -->
	case p(0.2):
		NIL
	case geometry.area > 30:
		40% : t('rand(0.3,0.7),0,'rand(0.3,0.7)) s(0,rand(10,23),0)i(treeasset) Tree(rand(1,3))
		40% : t('rand(0.2,0.8),0,'rand(0.2,0.8)) s(0,rand(8,17),0) i(treeasset) Tree(rand(1,3))
		      t('rand(0.2,0.8),0,'rand(0.2,0.8)) s(0,rand(8,17),0) i(treeasset) Tree(rand(1,3))
		else: t('rand(0.2,0.8),0,'rand(0.2,0.8)) s(0,rand(8,17),0) i(treeasset) Tree(rand(1,3))
	else:
		t('rand(0.3,0.7),0,'rand(0.3,0.7)) s(0,rand(8,17),0) i(treeasset) Tree(rand(1,3))
				
Tree(scale) -->
	s('scale,'1,'scale) center(xz) r(scopeCenter,0,rand(360),0)

		
		

# ----------------------------------------
# Streets
# ----------------------------------------

	
	
texIndexByDistance =
	case distanceToCenter < 300 : 1
	case distanceToCenter < 800 : 2
	case distanceToCenter < 1000 : 3
	case distanceToCenter < 1200 : 4
	else : 5



## Textures : depending on day/night mode, and distance to center different textures are applied
road_tex     =
	case nightMode :
		"textured/road_night_"+texIndexByDistance+".jpg"
	else:
		"textured/road_day.jpg"
concrete_tex =
	case nightMode :
		"textured/concrete_night_"+texIndexByDistance+".jpg"
	else:
		"textured/concrete_day.jpg"
		
pavement_tex =
	case nightMode :
		"textured/pavement_night_"+texIndexByDistance+".jpg"
		
	else:
		"textured/pavement_day.jpg"

curb_tex     =
	case nightMode :
		"textured/curb_night_"+texIndexByDistance+".jpg"
	else:
		"textured/curb_day.jpg"
## User attributes

attr sidewalkHeight = 0.5
attr avgLanewidth = 4


## Functions

// Calculates the (rounded) number of lanes depending on the street width
calcLanes(streetwidth) = streetwidth/ceil(streetwidth/avgLanewidth)


/******************** 
   Street Rules 
*********************/


Street -->
	// The uv's are set to correctly scale the road texture on the street polygon. 
	// The texture is repeated every 3 meter along the street (x-axis). The repetions along the z-axis defines the number of lanes
	setupProjection(0,scope.xz, 6,calcLanes(scope.sz)) 
	// Apply texture coordinates
	projectUV(0) scaleUV(0, 1, 0.98)
	// Define what texture to use
	texture(road_tex)
	setupProjection(2,scope.xz,200,100) projectUV(2) translateUV(2, rand, rand)	
	set(material.dirtmap,"textured/streets_texture_paintings.jpg")
	Street.
	
Crossing -->
	Concrete

// Junctions use same rules as Streets
Junction --> Street

// JunctionEntries use same rules as Crossings
JunctionEntry --> Crossing


Sidewalk -->
	set(trim.vertical,false) set(trim.horizontal,false)  // no triming needed here
	comp(f){ all: SidewalkPart }
  
SidewalkPart -->   
	// use sidewalk length (scope.sx) to decide wether to place lamps or not
	case scope.sx > 5:
  		SidewalkWithCurbs
  		alignScopeToAxes(y) t(0,sidewalkHeight,0)
 	else:
 		// no lamps on short sidewalks
  		SidewalkWithCurbs

SidewalkWithCurbs --> 
	// extrude sidewalks
	extrude(world.y,sidewalkHeight)
	comp(f)	
			// top faces are split into curb and pavement part
			{ top   : split(y){ sidewalkHeight : Curbs | ~1 : Pavement }
        	// curbs only on front faces
        	| front : Curbs }

Pavement -->
	setupProjection(0,world.xz,5,5) projectUV(0)
	texture(pavement_tex)

Curbs --> 
	// calculate uv size to have fitting curb pieces
  	setupProjection(0,scope.xy,scope.sx/ceil(scope.sx/1.1),scope.sy) projectUV(0) 
  	texture(curb_tex)

Concrete -->
	setupProjection(0,scope.xz,3,3) projectUV(0) texture(concrete_tex)
	setupProjection(2,scope.xz,200,100) projectUV(2) translateUV(2, rand, rand)	
    set(material.dirtmap,"textured/streets_texture_paintings.jpg")
	Concrete.
	

