diff --git a/.gitignore b/.gitignore
new file mode 100644
index 00000000..5171c540
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+node_modules
+npm-debug.log
\ No newline at end of file
diff --git a/README.md b/README.md
index fad423fa..cb127fa3 100644
--- a/README.md
+++ b/README.md
@@ -8,38 +8,22 @@ For this assignment you'll be building directly off of Project 3. To make things
**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!
## 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
+Shape class
## 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)
+- Subdivision of buildings
+- Floors
+- Roofs on top floor
+- Columns on bottom floor
+- I have the geometry to make onings, but alas I didn't end up using it.
+
## 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.
+- Regular ground plane (I understand, I'm sorry)
+- Made multiple buildings
## Make it interesting (10)
-Experiment! Make your city a work of art.
-
-
-## 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.
+- Well, I was going to go for the forbidden city, but that didn't work out.
+But I am satisfied to the point I got to despite the point allocation.
-## 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.
+Submitted Late.
\ No newline at end of file
diff --git a/deploy.js b/deploy.js
new file mode 100644
index 00000000..9defe7c3
--- /dev/null
+++ b/deploy.js
@@ -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);
+ }
+ })
+ })
+})
\ No newline at end of file
diff --git a/index.html b/index.html
new file mode 100644
index 00000000..e609adf4
--- /dev/null
+++ b/index.html
@@ -0,0 +1,19 @@
+
+
+
+ HW2: LSystems
+
+
+
+
+
+
diff --git a/package.json b/package.json
new file mode 100644
index 00000000..be683fcb
--- /dev/null
+++ b/package.json
@@ -0,0 +1,31 @@
+{
+ "scripts": {
+ "start": "webpack-dev-server --hot --inline",
+ "build": "webpack",
+ "deploy": "node deploy.js"
+ },
+ "gh-pages-deploy": {
+ "prep": [
+ "build"
+ ],
+ "noprompt": true
+ },
+ "dependencies": {
+ "dat-gui": "^0.5.0",
+ "gl-matrix": "^2.3.2",
+ "stats-js": "^1.0.0-alpha1",
+ "three": "^0.82.1",
+ "three-orbit-controls": "^82.1.0"
+ },
+ "devDependencies": {
+ "babel-core": "^6.18.2",
+ "babel-loader": "^6.2.8",
+ "babel-preset-es2015": "^6.18.0",
+ "colors": "^1.1.2",
+ "gh-pages-deploy": "^0.4.2",
+ "simple-git": "^1.65.0",
+ "webpack": "^1.13.3",
+ "webpack-dev-server": "^1.16.2",
+ "webpack-glsl-loader": "^1.0.1"
+ }
+}
diff --git a/src/framework.js b/src/framework.js
new file mode 100644
index 00000000..76f901a5
--- /dev/null
+++ b/src/framework.js
@@ -0,0 +1,72 @@
+
+const THREE = require('three');
+const OrbitControls = require('three-orbit-controls')(THREE)
+import Stats from 'stats-js'
+import DAT from 'dat-gui'
+
+// when the scene is done initializing, the function passed as `callback` will be executed
+// then, every frame, the function passed as `update` will be executed
+function init(callback, update) {
+ var stats = new Stats();
+ stats.setMode(1);
+ stats.domElement.style.position = 'absolute';
+ stats.domElement.style.left = '0px';
+ stats.domElement.style.top = '0px';
+ document.body.appendChild(stats.domElement);
+
+ var gui = new DAT.GUI();
+
+ var framework = {
+ gui: gui,
+ stats: stats
+ };
+
+ // run this function after the window loads
+ window.addEventListener('load', function() {
+
+ var scene = new THREE.Scene();
+ var camera = new THREE.PerspectiveCamera( 75, window.innerWidth/window.innerHeight, 0.1, 1000 );
+ var renderer = new THREE.WebGLRenderer( { antialias: true } );
+ renderer.setPixelRatio(window.devicePixelRatio);
+ renderer.setSize(window.innerWidth, window.innerHeight);
+ renderer.setClearColor(0x020202, 0);
+
+ var controls = new OrbitControls(camera, renderer.domElement);
+ controls.enableDamping = true;
+ controls.enableZoom = true;
+ controls.target.set(0, 0, 0);
+ controls.rotateSpeed = 0.3;
+ controls.zoomSpeed = 1.0;
+ controls.panSpeed = 2.0;
+
+ document.body.appendChild(renderer.domElement);
+
+ // resize the canvas when the window changes
+ window.addEventListener('resize', function() {
+ camera.aspect = window.innerWidth / window.innerHeight;
+ camera.updateProjectionMatrix();
+ renderer.setSize(window.innerWidth, window.innerHeight);
+ }, false);
+
+ // assign THREE.js objects to the object we will return
+ framework.scene = scene;
+ framework.camera = camera;
+ framework.renderer = renderer;
+
+ // begin the animation loop
+ (function tick() {
+ stats.begin();
+ update(framework); // perform any requested updates
+ renderer.render(scene, camera); // render the scene
+ stats.end();
+ requestAnimationFrame(tick); // register to call this again when the browser renders a new frame
+ })();
+
+ // we will pass the scene, gui, renderer, camera, etc... to the callback function
+ return callback(framework);
+ });
+}
+
+export default {
+ init: init
+}
\ No newline at end of file
diff --git a/src/linkedlist.js b/src/linkedlist.js
new file mode 100644
index 00000000..5a7444b2
--- /dev/null
+++ b/src/linkedlist.js
@@ -0,0 +1,77 @@
+export default class LinkedList {
+ constructor() {
+ this.head = null;
+ this.tail = null;
+ };
+
+ // Pushes node with value into the LL
+ push(value) {
+ var node = {
+ value: value,
+ next: null,
+ prev: null
+ }
+
+ if (this.head === null) {
+ // First element
+ this.head = node;
+ this.tail = node;
+ } else {
+ // Add to end of list
+ node.prev = this.tail;
+ this.tail.next = node;
+ this.tail = node; // Update tail
+ }
+ };
+
+ // Pops node off of the LL
+ pop() {
+ // 0 Elements
+ if (this.head === null) {
+ return null;
+ }
+
+ // 1 Element
+ else if (this.tail === this.head) {
+ var node = this.head;
+
+ this.head = null;
+ this.tail = null;
+
+ return node;
+ }
+
+ // 2+ Elements
+ else {
+ var node = this.tail;
+ this.tail = this.tail.prev;
+ this.tail.next = null;
+
+ return node;
+ }
+ };
+
+ // Returns a string version of the LL
+ // Assuming that the value is a letter
+ toString() {
+ if (this.head === null) {
+ return '';
+ } else {
+ var curr = this.head;
+ var result = '';
+
+ while (curr !== null) {
+ result += curr.value;
+ curr = curr.next;
+ }
+
+ return result;
+ }
+ };
+
+ // Clones the LL
+ clone() {
+ return JSON.parse(JSON.stringify(this));
+ };
+}
+
diff --git a/src/main.js b/src/main.js
new file mode 100644
index 00000000..f1039be2
--- /dev/null
+++ b/src/main.js
@@ -0,0 +1,66 @@
+
+const THREE = require('three'); // older modules are imported like this. You shouldn't have to worry about this much
+import Framework from './framework'
+import ShapeSystem from './shapesystem'
+import Shape from './shape.js'
+
+
+// Materials
+var lambertWhite = new THREE.MeshLambertMaterial( {color: 0xffffff} );
+
+// Geometry
+var boxGeo = new THREE.BoxGeometry( 1, 1, 1 );
+
+// Mesh
+var cubeMesh = new THREE.Mesh( boxGeo, lambertWhite );
+
+// called after the scene loads
+function onLoad(framework) {
+ var scene = framework.scene;
+ var camera = framework.camera;
+ var renderer = framework.renderer;
+ var gui = framework.gui;
+ var stats = framework.stats;
+
+ // initialize a simple box and material
+ var directionalLight = new THREE.DirectionalLight( 0xffffff, 1 );
+ directionalLight.color.setHSL(0.1, 1, 0.95);
+ directionalLight.position.set(1, 3, 2);
+ directionalLight.position.multiplyScalar(10);
+ scene.add(directionalLight);
+
+ // set camera position
+ camera.position.set(50, 100, 200);
+ camera.lookAt(new THREE.Vector3(0,0,0));
+
+ new Shape(null, )
+ var axiom = [new Shape(), new Shape(), new Shape(), new Shape(), new Shape(), new Shape(), new Shape()];
+ for (var i = 0; i < axiom.length; i++) {
+ var sc = axiom[i].mesh.scale;
+ axiom[i].mesh.position.set((3.0 + sc.x) * i , sc.y / 2.0, sc.z);
+ }
+
+ var ss = new ShapeSystem(axiom, scene);
+
+ gui.add(camera, 'fov', 0, 180).onChange(function(newVal) {
+ camera.updateProjectionMatrix();
+ });
+
+ gui.add(ss, 'iterate');
+
+ var geometry = new THREE.PlaneGeometry( 250, 50, 32 );
+ var material = new THREE.MeshBasicMaterial( {color: 0xffff00, side: THREE.DoubleSide} );
+ var plane = new THREE.Mesh( geometry, material );
+ plane.rotateX(Math.PI / 2.0);
+ scene.add( plane );
+
+ // ss.axiom[0].subdivide(0);
+ ss.traverse(scene);
+}
+
+// called on frame updates
+function onUpdate(framework) {
+}
+
+// when the scene is done initializing, it will call onLoad, then on frame updates, call onUpdate
+Framework.init(onLoad, onUpdate);
diff --git a/src/shape.js b/src/shape.js
new file mode 100644
index 00000000..2daee1f9
--- /dev/null
+++ b/src/shape.js
@@ -0,0 +1,362 @@
+
+const THREE = require('three'); // older modules are imported like this. You shouldn't have to worry about this much
+import Framework from './framework'
+
+var lambertWhite = new THREE.MeshLambertMaterial( {color: 0xffffff} );
+
+// Generate a random color
+function randColor() {
+ return Math.random() * 0xffffff;
+};
+
+function mapRand(start, end) {
+ return Math.random() * (end - start) + start;
+};
+
+function mat_randColor() {
+ return new THREE.MeshLambertMaterial({color:randColor()});
+}
+
+// Shape Node
+// parent: parent of the node
+// boundingbox: bounding box of the shape in world space
+// geometry: geometry to render
+// children
+export default class Shape {
+ constructor(parent, mesh, name) {
+ if (arguments.length === 0) {
+ // Default empty
+ this.parent = null;
+ // this.boundingBox = new THREE.BoxGeometry(10, 10, 10);
+ this.mesh = new THREE.Mesh(new THREE.BoxGeometry(1, 1, 1),
+ lambertWhite);
+ this.mesh.scale.set(10, 10, 10);
+ this.mesh.position.set(0, 5, 0);
+ this.iteration = 0;
+
+ // For continuation purposes
+ this.name = 'building';
+ } else {
+ if (name) {
+ this.name = name;
+ } else {
+ this.name = '';
+ }
+
+ this.parent = parent;
+ // this.boundingBox = boundingBox;
+ this.mesh = mesh;
+
+ if (parent !== null) {
+ this.iteration = this.parent.iteration + 1;
+ } else {
+ this.iteration = 0;
+ }
+ }
+
+ this.show = true;
+ this.division = ''; // Current subdivision
+ this.children = [];
+ };
+
+ // Creates floors
+ // Can only create on a building that has floors for the first time
+ // This should only happen once per building and should really be the first thing that happens
+ createFloors(numFloors) {
+ this.show = false;
+ this.name = ''; // No longer a building
+
+ // subdivide
+ var s_factor = 0.5;
+
+ var s = this.mesh.scale;
+ var p = this.mesh.position;
+
+ var s_child = s.clone();
+ var s_axis;
+
+ if (numFloors != null && numFloors > 0) {
+ numFloors = Math.floor(numFloors);
+ s_axis = 1.0 / numFloors;
+ } else {
+ numFloors = 2.0;
+ s_axis = 0.5;
+ }
+
+ s_axis = s.y * s_axis;
+ s_child.setComponent(1, s_axis);
+
+ for(var i = 0; i < numFloors; i++) {
+ var child = new THREE.Mesh(new THREE.BoxGeometry(1.0, 1.0, 1.0),
+ new THREE.MeshLambertMaterial( {color: randColor()} ));
+ child.scale.set(s_child.x, s_child.y, s_child.z);
+
+ child.position.set(p.x, p.y - s.y / 2.0 + s_axis / 2.0 + s_axis * i, p.z);
+ var shape = new Shape(this, child, 'floor');
+
+ if (i == 0) {
+ shape.name = 'bottom';
+ }
+ else if (i == numFloors - 1) {
+ shape.name = 'top';
+ }
+ this.children.push(shape);
+ }
+ };
+
+ // Subdivides Evenly in Half
+ // Should not be used for in y direction, instead use floor creation
+ subdivide(axis) { // 0 = x, 1 = y, 2 = z
+ if (this.children.length === 0) {
+ var s = this.mesh.scale;
+ var p = this.mesh.position;
+
+ // Random heights for the two different subdivisions
+ var h1 = mapRand(s.y / 2.0, s.y);
+ var h2 = mapRand(s.y / 2.0, s.y);
+
+ if (axis == null) {
+ axis = 0;
+ }
+
+ this.name = ''; // No longer a building once there are floors
+ this.show = false;
+
+
+ var sx1, sx2, sz1, sz2;
+
+ if (axis == 0) {
+ sx1 = mapRand(s.x / 4.0, s.x / 2.0);
+ sx2 = mapRand(s.x / 4.0, s.x / 2.0);
+
+ sz1 = mapRand(s.z / 2.0, s.z);
+ sz2 = mapRand(s.z / 2.0, s.z);
+
+ var p_left = new THREE.Vector3(
+ p.x - sx1 / 2.0,
+ p.y - s.y / 2.0 + h1 / 2.0,
+ p.z - s.z / 2.0 + sz1 / 2.0);
+ var p_right = new THREE.Vector3(
+ p.x + sx2 / 2.0,
+ p.y - s.y / 2.0 + h2 / 2.0,
+ p.z - s.z / 2.0 + sz2 / 2.0);
+
+ var left = new THREE.Mesh(
+ new THREE.BoxGeometry(1.0, 1.0, 1.0),
+ mat_randColor());
+ left.scale.set(sx1, h1, sz1);
+
+ var right = new THREE.Mesh(
+ new THREE.BoxGeometry(1.0, 1.0, 1.0),
+ mat_randColor());
+ right.scale.set(sx2, h2, sz2);
+
+ left.position.set(p_left.x, p_left.y, p_left.z);
+ right.position.set(p_right.x, p_right.y, p_right.z);
+
+ // Add to the children
+ this.children.push(new Shape(this, left, 'building'));
+ this.children.push(new Shape(this, right, 'building'));
+ } else {
+ sx1 = mapRand(s.x / 2.0, s.x);
+ sx2 = mapRand(s.x / 2.0, s.x);
+
+ sz1 = mapRand(s.z / 4.0, s.z / 2.0);
+ sz2 = mapRand(s.z / 4.0, s.z / 2.0);
+
+ var p_left = new THREE.Vector3(
+ p.x - s.x / 2.0 + sx1 / 2.0,
+ p.y - s.y / 2.0 + h1 / 2.0,
+ p.z - sz1 / 2.0);
+ var p_right = new THREE.Vector3(
+ p.x - s.x / 2.0 + sx2 / 2.0,
+ p.y - s.y / 2.0 + h2 / 2.0,
+ p.z + sz2 / 2.0);
+
+ var left = new THREE.Mesh(
+ new THREE.BoxGeometry(1.0, 1.0, 1.0),
+ mat_randColor());
+ left.scale.set(sx1, h1, sz1);
+
+ var right = new THREE.Mesh(
+ new THREE.BoxGeometry(1.0, 1.0, 1.0),
+ mat_randColor());
+ right.scale.set(sx2, h2, sz2);
+
+ left.position.set(p_left.x, p_left.y, p_left.z);
+ right.position.set(p_right.x, p_right.y, p_right.z);
+
+ // Add to the children
+ this.children.push(new Shape(this, left, 'building'));
+ this.children.push(new Shape(this, right, 'building'));
+ }
+
+ } else {
+ for(var i = 0; i < this.children.length; i++) {
+ this.children[i].subdivide(i % 2);
+ }
+ }
+ };
+
+ // Returns a (1, 1, 1) rectangular prism with the top shrunk in so that when scaled
+ // to the input it looks like a oning of a building in the forbidden city
+ createOningGeometry(scale, width) {
+ if (!width) {
+ width = 1.0; // Standard width of the oning
+ }
+
+ var oning = new THREE.BoxGeometry( 1.0, 1.0, 1.0 );
+ var v = oning.vertices;
+
+ // console.log(oning.vertices);
+ var sx = (scale.x - width) / (scale.x * 2.0);
+ var sz = (scale.z - width) / (scale.z * 2.0);
+
+ v[0].set(sx, 0.5, sz);
+ v[1].set(sx, 0.5, -sz);
+ v[4].set(-sx, 0.5, -sz);
+ v[5].set(-sx, 0.5, sz);
+ var oning_mesh = new THREE.Mesh(oning, new THREE.MeshLambertMaterial());
+ oning_mesh.scale.set(scale.x, scale.y, scale.z);
+ return oning_mesh;
+ };
+
+ createRoofGeometry(scale, width) {
+ this.name = '';
+
+ if (!width) {
+ width = 2.0;
+ }
+
+ var roof = new THREE.BoxGeometry( 1.0, 1.0, 1.0 );
+ var v = roof.vertices;
+
+ var sx, sz;
+ if (scale.x <= scale.z) {
+ sx = (scale.x - width) / (scale.x * 2.0);
+ sz = 0.0;
+ } else { // scale.z > scale.x
+ sx = 0.0;
+ sz = (scale.z - width) / (scale.z * 2.0);
+ }
+
+ v[0].set(sx, 0.5, sz);
+ v[1].set(sx, 0.5, -sz);
+ v[4].set(-sx, 0.5, -sz);
+ v[5].set(-sx, 0.5, sz);
+
+ var roof_mesh = new THREE.Mesh(roof, new THREE.MeshLambertMaterial());
+ roof_mesh.scale.set(scale.x, scale.y, scale.z);
+
+ var p = this.mesh.position;
+ roof_mesh.position.set(p.x, p.y + scale.y, p.z);
+
+ this.children.push(new Shape(this, roof_mesh, 'roof'));
+
+ //return roof_mesh;
+ };
+
+ // Scale of the space
+ // Width for how much space on the perimeter should the columns take up
+ createBoxwColGeometry(scale, width) {
+ this.name = '';
+
+ if (!width) {
+ width = 0.25;
+ }
+
+ var sx = (scale.x - width) / scale.x;
+ var sz = (scale.z - width) / scale.z;
+
+ var geo = new THREE.BoxGeometry( sx, 1.0, sz );
+ var v = geo.vertices;
+
+ var r = width / 4.0;
+
+ var colGeo1 = new THREE.CylinderGeometry(r, r, 1.0, 10.0);
+ colGeo1.scale(1.0/scale.x, 1.0, 1.0/scale.z);
+ colGeo1.translate(sx / 2.0 + r, 0.0, sz / 2.0 + r);
+
+ var colGeo2 = new THREE.CylinderGeometry(r, r, 1.0, 10.0);
+ colGeo2.scale(1.0/scale.x, 1.0, 1.0/scale.z);
+ colGeo2.translate(sx / 2.0 + r, 0.0, -sz / 2.0 - r);
+
+ var colGeo3 = new THREE.CylinderGeometry(r, r, 1.0, 10.0);
+ colGeo3.scale(1.0/scale.x, 1.0, 1.0/scale.z);
+ colGeo3.translate(-sx / 2.0 - r, 0.0, sz / 2.0 + r);
+
+ var colGeo4 = new THREE.CylinderGeometry(r, r, 1.0, 10.0);
+ colGeo4.scale(1.0/scale.x, 1.0, 1.0/scale.z);
+ colGeo4.translate(-sx / 2.0 - r, 0.0, -sz / 2.0 - r);
+
+ geo.merge(colGeo1);
+ geo.merge(colGeo2);
+ geo.merge(colGeo3);
+ geo.merge(colGeo4);
+
+ var mesh = new THREE.Mesh(geo, new THREE.MeshLambertMaterial());
+ mesh.scale.set(scale.x, scale.y, scale.z);
+ var p = this.mesh.position;
+ mesh.position.set(p.x, p.y, p.z);
+
+ this.mesh = mesh;
+ }
+
+ // THIS IS WHERE ALL THE GRAMMAR IS BASICALLY
+ // This is called by the ShapeSystem whatever.
+ iterate() {
+ var r = Math.random();
+
+ if (this.children.length > 0) {
+ for(var i = 0; i < this.children.length; i++) {
+ this.children[i].iterate();
+ }
+ }
+
+ // This is a finished intermediate node that no longer matters in terms of iteration
+ if (this.name == '') {
+ return;
+ } else if (this.name == 'building') {
+ // Subdivision into more buildings
+ if (r < 0.8) {
+ if (r < 0.4) {
+ this.subdivide(0);
+ } else {
+ this.subdivide(1);
+ }
+
+ // Divides the building into at least 2 floors
+ // More floors the taller the building is though
+ } else {
+ this.createFloors(Math.floor(Math.random() * this.mesh.scale.y) + 2);
+ }
+ } else if (this.name == 'top') {
+ if (r < 0.8) {
+ // make a roof
+ console.log('making a roof');
+ this.createRoofGeometry(this.mesh.scale, this.mesh.scale.x / 4.0);
+ }
+ } else if (this.name == 'bottom') {
+ if (r < 0.5) {
+ // add columns
+ console.log('adding some columns');
+ this.createBoxwColGeometry(this.mesh.scale, this.mesh.scale.x / 4.0);
+ }
+ }
+ };
+
+ draw(scene, n) {
+ // if (n < this.iteration && this.children.length != 0) {
+ if (this.children.length > 0) {
+ for(var i = 0; i < this.children.length; i++) {
+ this.children[i].draw(scene, n);
+ }
+ }
+ if (this.show === true) {
+ scene.add(this.mesh);
+ }
+ else {
+ scene.remove(this.mesh);
+ }
+ };
+}
\ No newline at end of file
diff --git a/src/shapesystem.js b/src/shapesystem.js
new file mode 100644
index 00000000..d34436be
--- /dev/null
+++ b/src/shapesystem.js
@@ -0,0 +1,75 @@
+const THREE = require('three');
+import Shape from './shape.js'
+import Framework from './framework'
+
+function Rule(successor, probability) {
+ this.successor = sucessor;
+ this.probability = probability;
+}
+
+export default function shapeSystem(axiom, scene) {
+ if (axiom) {
+ this.axiom = axiom;
+ } else {
+ this.axiom = [new Shape()];
+ }
+
+ // LOL JK FORGET THIS, JUST GO TO ITERATE I MEAN IT"S JUST THERE, BYE
+ // this.grammar = {
+ // 'bottom' : 'create columns',
+ // 'building' : 'createFloors or subdivide',
+ // 'middle': 'possible oning',
+ // 'top': 'add a roof'
+ // };
+
+ //this.grammar = grammar;
+ this.iteration = 0;
+ this.scene = scene;
+
+ // Set up the axiom string
+ if (typeof axiom !== "undefined") {
+ this.axiom = axiom;
+ }
+
+ // Set up the grammar as a dictionary that
+ // maps a single character (symbol) to a Rule.
+ if (typeof grammar !== "undefined") {
+ this.grammar = Object.assign({}, grammar);
+ }
+
+ // Set up iterations (the number of times you
+ // should expand the axiom in DoIterations)
+ if (typeof iterations !== "undefined") {
+ this.iterations = iterations;
+ }
+
+ // A function to alter the axiom string stored
+ // in the L-system
+ this.updateAxiom = function(axiom) {
+ // Setup axiom
+ if (typeof axiom !== "undefined") {
+ this.axiom = axiom;
+ }
+ }
+
+ this.traverse = function() {
+ for (var i = 0; i < axiom.length; i++) {
+ axiom[i].draw(this.scene, this.iterations);
+ }
+ }
+
+ this.iterate = function() {
+ for (var i = 0; i < axiom.length; i++) {
+ var a = axiom[i];
+
+ // Iterate
+ a.iterate();
+ }
+
+ this.traverse();
+
+
+ this.iterations++
+ console.log('iterate');
+ }
+}
\ No newline at end of file
diff --git a/webpack.config.js b/webpack.config.js
new file mode 100644
index 00000000..57dce485
--- /dev/null
+++ b/webpack.config.js
@@ -0,0 +1,28 @@
+const path = require('path');
+
+module.exports = {
+ entry: path.join(__dirname, "src/main"),
+ output: {
+ filename: "./bundle.js"
+ },
+ module: {
+ loaders: [
+ {
+ test: /\.js$/,
+ exclude: /(node_modules|bower_components)/,
+ loader: 'babel',
+ query: {
+ presets: ['es2015']
+ }
+ },
+ {
+ test: /\.glsl$/,
+ loader: "webpack-glsl"
+ },
+ ]
+ },
+ devtool: 'source-map',
+ devServer: {
+ port: 7000
+ }
+}
\ No newline at end of file