Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added .DS_Store
Binary file not shown.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
node_modules
/tmp
npm-debug.log
74 changes: 40 additions & 34 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,45 +1,51 @@
# Shape Grammars

# Project 4: Shape Grammar
![](img/1.PNG)
![](img/2.png)

For this assignment you'll be building directly off of Project 3. To make things easier to keep track of, please fork and clone this repository [https://github.com/CIS700-Procedural-Graphics/Project4-Shape-Grammar](https://github.com/CIS700-Procedural-Graphics/Project4-Shape-Grammar) and copy your Project 3 code to start.
# Grammars
Much of the grammar is baked into the code. There are in total 8 distinct rules that buildings go by.
- Explicit
- G -> (O1, O2, O3)* The ground spawns trees of three different forms
- B -> bD A giant block is subdivided and a Door is added
- b -> bOW* subdivided blocks are further subdivided + roof + windows
- D is a symbolic terminal node
- Implicit (Could be refactored into explicit rules, but the effort taken isn't worth it
- O -> (r0|r1|r2|r3) Roofs can take on multiple forms and will subdivide with buildings
- b -> (b s1|b s2) Subdivided blocks are sliced along xz or yz planes
- b s1, b s2 -> (b 1r|b 2r) Subdivided blocks are rotated
- r0 -> b Roofs on tall buildings will actually convert back into a box cap. There is an iteration cap so we don't have to worry about non-terminal cyclic grammars.

**Goal:** to model an urban environment using a shape grammar.
See [Buildings](#Buildings) for more information on how Building grammars behave

**Note:** We’re well aware that a nice-looking procedural city is a lot of work for a single week. Focus on designing a nice building grammar. The city layout strategies outlined in class (the extended l-systems) are complex and not expected. We will be satisfied with something reasonably simple, just not a uniform grid!
# The Grid
There is an in-memory grid structure that directs and oversees all object construction. This is to keep track of what is where, demarcate zones, spawning rules.

## Symbol Node (5 points)
Modify your symbol node class to include attributes necessary for rendering, such as
- Associated geometry instance
- Position
- Scale
- Anything else you may need
# Zoning
A **Voronoi diagram** is created such that 5 points arbitrarily selected as "seed" locations will demarcate zones on our plane. Zones range from 1 to 5 (1 being lowest pop. density, 5 being highest pop. density) and a zone of 3 indicates forestry.

## Grammar design (55 points)
- Design at least five shape grammar rules for producing procedural buildings. Your buildings should vary in geometry and decorative features (beyond just differently-scaled cubes!). At least some of your rules should create child geometry that is in some way dependent on its parent’s state. (20 points)
- Eg. A building may be subdivided along the x, y, or z axis into two smaller buildings
- Some of your rules must be designed to use some property about its location. (10 points)
- Your grammar should have some element of variation so your buildings are non-deterministic. Eg. your buildings sometimes subdivide along the x axis, and sometimes the y. (10 points)
- Write a renderer that will interpret the results of your shape grammar parser and adds the appropriate geometry to your scene for each symbol in your set. (10 points)
# Buildings
Urban Houses (Zone 1): These houses are wider and will arbitrarily rotate its subdivisions to extend further outward

## Create a city (30 points)
- Add a ground plane or some other base terrain to your scene (0 points, come on now)
- Using any strategy you’d like, procedurally generate features that demarcate your city into different areas in an interesting and plausible way (Just a uniform grid is neither interesting nor plausible). (20 points)
- Suggestions: roads, rivers, lakes, parks, high-population density
- Note, these features don’t have to be directly visible, like high-population density, but they should somehow be visible in the appearance or arrangement of your buildings. Eg. High population density is more likely to generate taller buildings
- Generate buildings throughout your city, using information about your city’s features. Color your buildings with a method that uses some aspect of its state. Eg. Color buildings by height, by population density, by number of rules used to generate it. (5 points)
- Document your grammar rules and general approach in the readme. (5 points)
- ???
- Profit.
Suburban houses (Zone 2): These houses are unit sized and follow default subdivision rules.

## Make it interesting (10)
Experiment! Make your city a work of art.
High-density Residence (Zone 4): These are apartment-like houses that have their height extended and follow default subdivision rules. These will appear pink, have more windows, and a door.

City Financial District (Zone 5): These are similar to Zone 4 except taller and appear more indigo and all features are more brought out.

## Warnings:
You can very easily blow up three.js with this assignment. With a very simple grammar, our medium quality machine was able to handle 100 buildings with 6 generations each, but be careful if you’re doing this all CPU-side.

## Suggestions for the overachievers:
Go for a very high level of decorative detail!
Place buildings with a strategy such that buildings have doors and windows that are always accessible.
Generate buildings with coherent interiors
If dividing your city into lots, generate odd-shaped lots and create building meshes that match their shape ie. rather than working with cubes, extrude upwards from the building footprints you find to generate a starting mesh to subdivide rather than starting with platonic geometry.
Windows are placed based on the building height. The larger the zone number, the more windows (because the buildings get bigger). They are placed randomly with no constraints. Doors are only placed on the primary building body (the body that eventually will be subdivided) and thus buildings will each have a single door.

# Tree Placement
Tree placement is an interesting problem. Aside from modulating spawn frequency based on zoning, we also ensure that trees don't spawn near houses. The forward naïve solution seems to work better. I also tried doing a convex optimization where I constructed a cost function that penalized tree-object intersections. Then I used Monte-Carlo sampling to select a configuration that maximized on that cost function. It was too slow.

# Road (In progress)
There are 3 approaches which I am perfecting (the code is there but it's not ready...).

Another convex optimization: Again we construct a cost function and feel out the topology with some Monte-Carlo method and maybe with naïve greedy construction. The cost function here would probably mimic properties of real road systems: minimizing winding paths, minimizing travel distance between any two buildings, minimizing the number of roads used, promoting axis alignment (crutch feature that I hypothesize will help with the first two features), minimizing nearby intersections, etc.

Grid Road: Here we use the grid to help us construct roads. We establish arbitrary points near a building to denote an entry point. We have now reduced the problem to path-finding on a grid-structure with obstacles (the buildings). Since we want to promote more structure in a road system, we use a geometric mean heuristic. I thought about using a l2 norm or even a l1 norm but I hypothesize that they're not strong enough as heuristics. You can visualize this by imagining an astroid for a geometric mean heuristic–this is essentially how it weights its choices.

Generative Road Construction: Here we have the same approach to Grid Road except it is without a grid. It's free-form and starts with points near buildings to denote entry points. We then connect those points with straight lines and then add control points to create curves that move the roads so they do not intersect with houses.

It would also make sense to build the road system first, and then insert houses in. We can use the first two algorithms above to do so, and I believe the first will outperform the second. But I did not have time to explore this option.
50 changes: 50 additions & 0 deletions css/styles.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
html, body {
margin: 0;
overflow: hidden;
}
canvas {
width: 100%;
height: 100%;
}

#stats {
position: absolute;
left: 0px;
top: 0px;
}

.grammar {
background: rgba(204, 204, 204, 0.0);
position: fixed;
top: 15%;
left: 0;
right: 10px;
height: 0;
z-index: 0;
}

textarea {
border: 3px solid #cccccc;
padding: 5px;
font-family: Tahoma, sans-serif;
background-position: bottom right;
background-repeat: no-repeat;
opacity: 0.2;
transition: opacity .5s ease-out;
-moz-transition: opacity .5s ease-out;
-webkit-transition: opacity .5s ease-out;
-o-transition: opacity .5s ease-out;
}

textarea:hover {
border: 3px solid #cccccc;
padding: 5px;
font-family: Tahoma, sans-serif;
background-position: bottom right;
background-repeat: no-repeat;
opacity: 1.0;
transition: opacity .5s ease-out;
-moz-transition: opacity .5s ease-out;
-webkit-transition: opacity .5s ease-out;
-o-transition: opacity .5s ease-out;
}
38 changes: 38 additions & 0 deletions deploy.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
var colors = require('colors');
var path = require('path');
var git = require('simple-git')(__dirname);
var deploy = require('gh-pages-deploy');
var packageJSON = require('require-module')('./package.json');

var success = 1;
git.fetch('origin', 'master', function(err) {
if (err) throw err;
git.status(function(err, status) {
if (err) throw err;
if (!status.isClean()) {
success = 0;
console.error('Error: You have uncommitted changes! Please commit them first'.red);
}

if (status.current !== 'master') {
success = 0;
console.warn('Warning: Please deploy from the master branch!'.yellow)
}

git.diffSummary(['origin/master'], function(err, diff) {
if (err) throw err;

if (diff.files.length || diff.insertions || diff.deletions) {
success = 0;
console.error('Error: Current branch is different from origin/master! Please push all changes first'.red)
}

if (success) {
var cfg = packageJSON['gh-pages-deploy'] || {};
var buildCmd = deploy.getFullCmd(cfg);
deploy.displayCmds(deploy.getFullCmd(cfg));
deploy.execBuild(buildCmd, cfg);
}
})
})
})
Binary file added img/1.PNG
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added img/2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added img/Thumbs.db
Binary file not shown.
Binary file added img/nx.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added img/ny.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added img/nz.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added img/px.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added img/py.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added img/pz.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
13 changes: 13 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<!DOCTYPE html>
<html>
<head>
<title>HW4: Shape Grammar</title>
<link rel="stylesheet" href="./css/styles.css">
</head>
<body>
<div class="grammar" align="right">
<textarea autofocus id="grammar" rows="60" cols="50" placeholder="Enter rules here separated by new line..." autocomplete="off" style="display: none;"></textarea>
</div>
<script src="bundle.js"></script>
</body>
</html>
Loading