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