Skip to content

Instantly share code, notes, and snippets.

@netmin-net
Created March 24, 2019 02:52
Show Gist options
  • Save netmin-net/6edb8e91a308bd691f0a84899834e824 to your computer and use it in GitHub Desktop.
Save netmin-net/6edb8e91a308bd691f0a84899834e824 to your computer and use it in GitHub Desktop.
Low Poly Tree Generator
<div id="world"/>
<div id="credits">
- Press and drag to turn around -
<p><a href="https://codepen.io/Yakudoo/" target="blank">my other codepens</a> | <a href="https://www.epic.net" target="blank">epic.net</a> | <a href="http://www.meusinvest.be/2016/" target="blank">Created initially for this project</a></p>
</div>
/*
Used initially for a client to create this greeting card :
http://www.meusinvest.be/2016/
*/
////////////////////////////////////////////////
// THREE JS RELATED VARIABLES
////////////////////////////////////////////////
var scene,
camera, fieldOfView, aspectRatio, nearPlane, farPlane,
renderer,
container,
controls,
mouseDown = false;
////////////////////////////////////////////////
// SCREEN & MOUSE VARIABLES
////////////////////////////////////////////////
var HEIGHT, WIDTH, windowHalfX, windowHalfY,
mousePos = { x: 0, y: 0 },
oldMousePos = {x:0, y:0};
////////////////////////////////////////////////
// 3D MODELS VARIABLES
////////////////////////////////////////////////
var floor, tree,
globalWind = 0,
trees = [],
waitingParticles = [],
flyingParticles = [],
foliages = [],
foliagesComplex = [];
////////////////////////////////////////////////
// STATS & GUI
////////////////////////////////////////////////
var stats;
var parameters = {
truncHeight:100,
truncThickness:4,
truncColor:Colors.grey_d,
truncNoise:.5,
foliageColor:"pinks",
foliageDensity:5,
foliageNoise:.05,
foliageSize : 10,
animationSpeed: 2.5,
};
function initGUI(){
var gui = new dat.GUI();
gui.width = 250;
gui.add(parameters, 'truncHeight').min(60).max(110).step(10).name('Trunc Height');
gui.add(parameters, 'truncThickness').min(2).max(6).step(1).name('Trunc Thickness');
gui.add(parameters, 'truncNoise').min(0).max(5).step(.5).name('Trunc Disp.');
gui.add(parameters, 'truncColor', {
"Light White":Colors.white_l,
"Dark White":Colors.white_d,
"Light Grey":Colors.grey_l,
"Dark Grey":Colors.grey_d
}).name('Trunc Color');
gui.add(parameters, 'foliageColor', {
"White":"whites",
"Grey":"greys",
"Pink":"pinks",
"Yellow":"yellows",
"Purple":"purples"
}).name('Foliage Color');
gui.add(parameters, 'foliageDensity').min(3).max(8).step(1).name('Foliage Density');
gui.add(parameters, 'foliageNoise').min(0).max(.2).step(.01).name('Foliage Disp.');
gui.add(parameters, 'foliageSize').min(5).max(30).step(1).name('Foliage Size.');
//gui.add(parameters, 'animationSpeed').min(1).max(4).step(.5).name('Anim. Speed');
gui.add({generate:function(){createTree()}}, 'generate').name(">GENERATE");
}
////////////////////////////////////////////////
// INIT THREE JS, MOUSE, SCREEN
////////////////////////////////////////////////
function initCore() {
HEIGHT = window.innerHeight;
WIDTH = window.innerWidth;
windowHalfX = WIDTH / 2;
windowHalfY = HEIGHT / 2;
scene = new THREE.Scene();
var fogcol = 0xcefaeb;//0x1c0403
scene.fog = new THREE.FogExp2( fogcol, 0.0028 ); //new THREE.Fog(fogcol, 300, 1000);
aspectRatio = WIDTH / HEIGHT;
fieldOfView = 60;
nearPlane = .1;
farPlane = 3000;
camera = new THREE.PerspectiveCamera(
fieldOfView,
aspectRatio,
nearPlane,
farPlane
);
camera.position.x = 0;
camera.position.z = 150;
camera.position.y = 100;
renderer = new THREE.WebGLRenderer({ alpha: true, antialias: true });
renderer.setSize(WIDTH, HEIGHT);
renderer.shadowMap.enabled = true;
container = document.getElementById('world');
container.appendChild(renderer.domElement);
window.addEventListener('resize', handleWindowResize, false);
document.addEventListener('mousemove', handleMouseMove, false);
document.addEventListener('touchmove', handleTouchMove, false);
document.addEventListener('touchmove', handleTouchMove, false);
document.addEventListener('mousedown', mouseDownHandler, false);
document.addEventListener('mouseup', mouseUpHandler, false);
//*
controls = new THREE.OrbitControls(camera, renderer.domElement);
controls.target = new THREE.Vector3(0,60,0);
controls.minPolarAngle = -Math.PI*.45;
controls.maxPolarAngle = Math.PI*.45;
controls.minDistance = 130;
controls.maxDistance = 500;
//controls.enabled = false;
//controls.noZoom = true;
//controls.noPan = true;
//*/
console.log(controls);
}
////////////////////////////////////////////////
// MOUSE EVENTS / SCREEN EVENTS
////////////////////////////////////////////////
function handleWindowResize() {
HEIGHT = window.innerHeight;
WIDTH = window.innerWidth;
windowHalfX = WIDTH / 2;
windowHalfY = HEIGHT / 2;
renderer.setSize(WIDTH, HEIGHT);
camera.aspect = WIDTH / HEIGHT;
camera.updateProjectionMatrix();
}
function mouseDownHandler(event) {
mouseDown = true;
}
function mouseUpHandler(event) {
mouseDown = false;
}
function handleMouseMove(event) {
mousePos = {x:event.clientX, y:event.clientY};
}
function handleTouchMove(event) {
if (event.touches.length == 1) {
event.preventDefault();
mousePos = {x:event.touches[0].pageX, y:event.touches[0].pageY};
}
}
////////////////////////////////////////////////
// RENDER
////////////////////////////////////////////////
function render(){
if (controls && controls.enabled) controls.update();
renderer.render(scene, camera);
}
////////////////////////////////////////////////
// LIGHTS
////////////////////////////////////////////////
var gobalLight, shadowLight, backLight;
function createLights() {
var globalLight = new THREE.HemisphereLight(Colors.white_d, Colors.white_d, .8)
/*
var pointLight = new THREE.PointLight( Colors.pink_l, 1, 300 );
pointLight.position.set( 35, 50, 30 );
var sphereSize = 10;
var pointLightHelper = new THREE.PointLightHelper( pointLight, sphereSize );
//*/
shadowLight = new THREE.DirectionalLight(0xffffff, 1);
shadowLight.position.set(100, 150, 100);
shadowLight.castShadow = true;
shadowLight.shadowDarkness = .2;
shadowLight.shadowMapWidth = shadowLight.shadowMapHeight = 1024;
scene.add(shadowLight);
//scene.add(pointLight );
scene.add(globalLight);
}
////////////////////////////////////////////////
// FLOOR
////////////////////////////////////////////////
var Floor = function(){
var floorCol = Colors.green_d;
this.mesh = new CustomMesh.PlaneMesh(1600,1600,12, floorCol);
var vertices = this.mesh.geometry.vertices;
for (var i=0; i<vertices.length; i++){
var v = vertices[i];
v.x += Math2.rangeRandom(-10,10);
v.y += Math2.rangeRandom(-10,10);
v.z += Math2.rangeRandom(-10,10);
}
this.mesh.geometry.computeFaceNormals();
this.mesh.geometry.verticesNeedUpdate = true;
this.mesh.geometry.colorsNeedUpdate = true;
//
//this.mesh.geometry.computeVertexNormals();
this.mesh.rotation.x = -Math.PI / 2;
}
////////////////////////////////////////////////
// CREATE MODELS
////////////////////////////////////////////////
// FLOOR
function createFloor(){
floor = new Floor();
scene.add(floor.mesh);
}
// FOREST
function createForest(){
var nTrees = 25;
var treesDist = 25;
for (var i = 0; i< nTrees; i++){
var tree = new Tree(false);
tree.mesh.position.y = -5;
tree.mesh.position.x = -((nTrees/2)*treesDist) + (i*treesDist);
tree.mesh.position.z = -Math.random()*150 -150;
scene.add(tree.mesh);
trees.push(tree);
}
}
// TREE
function createTree(){
if (tree){
tree.fly(function(){
scene.remove(tree.mesh);
tree.kill();
tree = null;
foliagesComplex = [];
createTree();
})
}else{
tree = new Tree(true);
tree.mesh.position.y = -10;
scene.add(tree.mesh);
tree.trunc.grow();
updateShadows();
}
}
////////////////////////////////////////////////
// MODELS
////////////////////////////////////////////////
// TREE
Tree = function(complex){
this.mesh = new THREE.Object3D();
this.trunc = new Trunc(complex);
this.mesh.add(this.trunc.mesh);
}
Tree.prototype.kill = function(){
this.trunc.kill();
this.mesh = null;
}
Tree.prototype.fly = function(callback){
TweenMax.to(this.mesh.position, 1.5, {x:100, y:300, z:300, ease:Strong.easeIn, onComplete:function(){
if (callback) callback();
}});
TweenMax.to(this.mesh.rotation, 2, {z:-Math.PI/6, y:Math.PI/2, ease:Strong.easeInOut});
TweenMax.to(this.mesh.scale, 1.5, {y:1.1, x:.9, ease:Strong.easeInOut});
}
// TRUNC
Trunc = function(complex){
this.type = "trunc";
this.pointsTrunc = [];
this.hierarchy = 1;
// parametrables
this.truncColor = (complex) ? parameters.truncColor : Colors.getRandomFrom(Colors.trunc);
this.truncHeight = (complex) ? parameters.truncHeight : Math2.rangeRandom(70,100);
this.truncStartRadius = (complex) ? parameters.truncThickness : Math2.rangeRandom(2,4);
this.verticalSegments = (complex)? Math2.rangeRandomInt(9,12) : Math2.rangeRandomInt(3,5);
this.radiusSegments = (complex)? Math2.rangeRandomInt(6,10) : Math2.rangeRandomInt(4,6);
this.shapeAngleStart = Math2.rangeRandom(Math.PI/4, Math.PI/2);
this.shapeAmplitude = Math2.rangeRandom(this.truncStartRadius/4, this.truncStartRadius*6);
this.noise = (complex)? parameters.truncNoise : Math2.rangeRandom(this.truncStartRadius/8, this.truncStartRadius/4);
this.foliageDensity = (complex)? parameters.foliageDensity : 2;
this.shapeAngle = Math.PI - this.shapeAngleStart;
this.freq = this.shapeAngle/this.verticalSegments;
this.segHeight = (this.truncHeight / this.verticalSegments);
/*
console.log("this.truncColor",this.truncColor);
console.log("this.truncHeight",this.truncHeight);
console.log("this.truncStartRay",this.truncStartRay);
console.log("this.verticalSegments",this.verticalSegments);
console.log("this.shapeAngleStart",this.shapeAngleStart);
console.log("this.shapeAmplitude",this.shapeAmplitude);
console.log("this.noise",this.noise);
console.log("this.shapeAngle",this.shapeAngle);
console.log("this.freq",this.freq);
console.log("this.segHeight",this.segHeight);
*/
this.pointsTrunc.push( new THREE.Vector3( 0, 0, 0 ) );
var ty,tx, tz, i;
ty = 0;
for ( i = 0; i < this.verticalSegments; i ++ ) {
tx = Math.sin( this.shapeAngleStart + (i * this.freq) ) * this.shapeAmplitude + this.truncStartRadius;
tz = 0;
this.pointsTrunc.push( new THREE.Vector3( tx, ty, tz ) );
if (i < this.verticalSegments -1) {
ty += this.segHeight;
}else{
ty += this.segHeight/4;
}
}
this.pointsTrunc.push( new THREE.Vector3( 0, ty, 0 ) );
this.mesh = new CustomMesh.Lathe( this.pointsTrunc, this.radiusSegments, this.truncColor);
this.mesh.userData.hierarchy = this.hierarchy;
this.mesh.userData.refClass = this;
var geom = this.mesh.geometry;
var defAttachs;
if (complex){
defAttachs = [
{
type:"elbowBranch",
count : this.foliageDensity,
minH : this.truncHeight*.75,
maxH:this.truncHeight*.95,
minAngle:0,//-Math.PI*3/4,
maxAngle:0,//-Math.PI/4
},
/*
{
type:"elbowBranch",
count : 1,
minH : this.truncHeight*.75,
maxH:this.truncHeight*.95,
minAngle:Math.PI/4,
maxAngle:Math.PI*3/4
},
{
type:"elbowBranch",
count : 1,
minH : this.truncHeight*.8,
maxH:this.truncHeight*1,
minAngle:-Math.PI/4,
maxAngle:Math.PI/4
},
*/
{
type:"branch",
count : 1,
minH : this.truncHeight*.45,
maxH:this.truncHeight*.75,
minAngle:-Math.PI,
maxAngle:0
},
{
type:"branch",
count : 1,
minH : this.truncHeight*.35,
maxH:this.truncHeight*.50,
minAngle:Math.PI/4,
maxAngle:Math.PI*3/4
},
{
type:"leaf",
count : 5,
minH : this.truncHeight*.30,
maxH:this.truncHeight*.90,
minAngle:0,
maxAngle:0
},
{
type:"fruit",
count : 4,
minH : this.truncHeight*.30,
maxH:this.truncHeight*.80,
minAngle:0,
maxAngle:0
},
{
type:"spike",
count : 6,
minH : this.truncHeight*.10,
maxH:this.truncHeight*.90,
minAngle:0,
maxAngle:0
},
{
type:"moss",
count : 6,
minH : this.truncHeight*.10,
maxH:this.truncHeight*.90,
minAngle:0,
maxAngle:0
},
];
}else{
defAttachs = [
{
type:"elbowBranch",
count : 1,
minH : this.truncHeight*.75,
maxH:this.truncHeight*.9,
minAngle:-Math.PI*3/4,
maxAngle:-Math.PI/4
},
{
type:"elbowBranch",
count : 1,
minH : this.truncHeight*.45,
maxH:this.truncHeight*.7,
minAngle:Math.PI/4,
maxAngle:Math.PI*3/4
},
{
type:"branch",
count : 1,
minH : this.truncHeight*.45,
maxH:this.truncHeight*.75,
minAngle:-Math.PI,
maxAngle:0
},
{
type:"branch",
count : 1,
minH : this.truncHeight*.15,
maxH:this.truncHeight*.45,
minAngle:Math.PI/4,
maxAngle:Math.PI*3/4
},
{
type:"fruit",
count : 2,
minH : this.truncHeight*.30,
maxH:this.truncHeight*.80,
minAngle:0,
maxAngle:0
},
];
}
this.attachsVerts = GeometryHelpers.getAttachs(geom, defAttachs);
if (this.noise) GeometryHelpers.makeNoise(geom, this.noise);
this.verticesNormals = GeometryHelpers.getVerticesNormals(geom);
CustomMesh.flatshadeGeometry(geom);
var cols = [];
cols["leaf"] = Colors.green_d;
cols["branch"] = this.truncColor;
cols["elbowBranch"] = this.truncColor;
cols["moss"] = Colors.white_l;
cols["spike"] = Colors.red_l;
cols["fruit"] = Colors.red_d;
var colorFoliagePalette = (complex)? Colors[parameters.foliageColor] : Colors.getRandomFrom([Colors.pinks, Colors.yellows, Colors.greens, Colors.purples]);
for (i=0; i<this.attachsVerts.length; i++){
var attDef = this.attachsVerts[i];
var v = geom.vertices[attDef.index];
var type = attDef.type;
var col = cols[type];
var attach, s, r, th;
if (type == "moss"){
s = Math2.rangeRandom(1,2);
attach = new CustomMesh.SphereMesh(s,5,3, col, true);
attach.geometry.applyMatrix(new THREE.Matrix4().makeScale(.6,1,.3));
attach.quaternion.setFromUnitVectors ( new THREE.Vector3( 0, 0, 1 ), this.verticesNormals[attDef.index] );
}else if (type == "spike"){
s = Math2.rangeRandom(1,3);
attach = new CustomMesh.SphereMesh(s,2,2, col, true);
attach.geometry.applyMatrix(new THREE.Matrix4().makeScale(.1,1,.1));
attach.quaternion.setFromUnitVectors ( new THREE.Vector3( 0, 1, 0 ), this.verticesNormals[attDef.index] )
}else if(type == "fruit"){
s = Math2.rangeRandom(2,4);
attach = new Tomatoe(s, Colors.getRandomFrom(Colors.pinks, complex), Colors.getRandomFrom(Colors.greens)).mesh;
attach.quaternion.setFromUnitVectors ( new THREE.Vector3( 0, 1, 0 ), this.verticesNormals[attDef.index] )
//attach.rotation.z += Math.PI/4 + Math.random()*Math.PI/4;
}else if(type == "leaf"){
s = Math2.rangeRandom(1,2);
attach = new Leaf(s,col).mesh;
attach.quaternion.setFromUnitVectors ( new THREE.Vector3( 0, 1, 0 ), this.verticesNormals[attDef.index] )
//attach.rotation.z += Math.PI/4 + Math.random()*Math.PI/4;
}else if(type == "elbowBranch"){
r = Math2.rangeRandom(this.truncHeight*.05,this.truncHeight*.15);
th = Math2.rangeRandom(this.truncStartRadius*40/(1+v.y),this.truncStartRadius*60/(1+v.y));
attach = new ElbowBranch(r,th,col, colorFoliagePalette, this.hierarchy+1, complex).mesh;
attach.quaternion.setFromUnitVectors ( new THREE.Vector3( -1, 0, 0 ), new THREE.Vector3( v.x, 0, v.z ).normalize() );
}else if(type == "branch"){
s = Math2.rangeRandom(this.truncHeight*.03,this.truncHeight*.06);
th = Math2.rangeRandom(this.truncStartRadius*.2,this.truncStartRadius*.4);
attach = new Branch(s,th,col, colorFoliagePalette, this.hierarchy+1, complex).mesh;
attach.quaternion.setFromUnitVectors ( new THREE.Vector3( 0, 1, 0 ), new THREE.Vector3( v.x, 0, v.z ).normalize() );
}
attach.position.copy(v);
attach.userData.targetY = v.y;
attach.userData.targetRotZ = attach.rotation.z;
attach.userData.hierarchy = this.hierarchy+1;
this.mesh.add(attach);
attDef.mesh = attach;
}
geom.verticesNeedUpdate = true;
}
Trunc.prototype.kill = function(){
var mesh = this.mesh;
var geom = this.mesh.geometry;
var l = geom.vertices.length;
for (var i=0; i<l; i++){
var v = geom.vertices[i];
TweenMax.killTweensOf(v);
}
killGrow(mesh);
mesh = null;
}
function killGrow(mesh){
for (var i=0; i<mesh.children.length; i++){
var child = mesh.children[i];
TweenMax.killTweensOf(child.scale);
TweenMax.killTweensOf(child.rotation);
killGrow(child);
child = null;
}
}
Trunc.prototype.grow = function(){
var scope = this;
var geom = this.mesh.geometry;
var l = geom.vertices.length;
for (var i=0; i<l; i++){
var v = geom.vertices[i];
var d = v.y/100;//Math.abs(this.truncHeight - v.y) /100;
var s = 30/parameters.animationSpeed;// + (Math.abs(this.truncHeight - v.y)/100);
TweenMax.from(v, s*.5, {x:0,z:0, delay:d*2, ease:Strong.easeInOut, onUpdate:scope.replaceAttachs, onUpdateScope:scope});
TweenMax.from(v, s, {y:0, delay:d, ease:Strong.easeOut});
}
grow(this.mesh);
}
Trunc.prototype.replaceAttachs = function(){
for (i=0; i<this.attachsVerts.length; i++){
var attDef = this.attachsVerts[i];
var v = this.mesh.geometry.vertices[attDef.index];
attDef.mesh.position.copy(v);
}
this.mesh.geometry.verticesNeedUpdate = true;
}
//Trunc.prototype.growAttachs = grow;
/*
BottleTrunc.prototype.updateWind = function(power){
this.mesh.rotation.z = (power/1000);
for (var i=0; i<this.mesh.children.length; i++){
var child = this.mesh.children[i];
child.rotation.z = child.userData.targetRotZ + Math.random()*power/1000;
}
}
//*/
// BRANCH
ElbowBranch = function(radius, thickness, color, colorFoliagePalette, hierarchy, complex){
this.type = "elbowBranch";
var radSegs = (complex)?5:3;
var tubSegs = (complex)?10:4;
this.mesh = new CustomMesh.QuarterTorusMesh(radius,thickness,radSegs,tubSegs,Math.PI/2,color);
this.mesh.geometry.applyMatrix(new THREE.Matrix4().makeTranslation(thickness,-thickness*2,0));
this.mesh.userData.hierarchy = hierarchy;
this.mesh.userData.refClass = this;
var folThick = (complex)? parameters.foliageSize*(1+Math.random()*.5) : Math2.rangeRandom(8,24);
this.attach = new Foliage(folThick, colorFoliagePalette, hierarchy+1,complex).mesh;
this.attach.position.x = -radius;
this.attach.position.y = radius - (thickness*3);
this.mesh.add(this.attach);
}
Branch =function(h, thickness, color, colorFoliagePalette, hierarchy,complex){
this.type = "branch";
var radSegs = (complex)?5:3;
this.mesh = new CustomMesh.CylinderMesh(thickness,thickness,h,radSegs, 1, color, false);
this.mesh.geometry.applyMatrix(new THREE.Matrix4().makeTranslation(0,-1,0));
this.mesh.userData.hierarchy = hierarchy;
this.mesh.userData.refClass = this;
var folThick = thickness*Math2.rangeRandom(3,8);
this.attach = new Foliage(folThick, colorFoliagePalette, hierarchy+1,complex).mesh;
this.attach.position.y = h-2;
this.mesh.add(this.attach);
}
// FRUIT
Tomatoe = function(scale, colorFruit, colorLeaves,complex){
this.type = "fruit";
this.mesh = new THREE.Object3D();
this.mesh.userData.refClass = this;
this.core = new THREE.Object3D();
this.stem = new Stem(scale, colorLeaves,complex);
this.line = this.stem.mesh;
var sw = (complex)?5:3;
var sh = (complex)?5:3;
this.fruit = new CustomMesh.SphereMesh(scale,sw,sh, colorFruit, false);
this.fruit.position.y = 1;
this.crown = new THREE.Object3D();
var stepAngle = Math.PI*2 / 5;
var crownCount = Math2.rangeRandomInt(3,6);
for (var i=0;i<5; i++){
var leaf = new CustomMesh.DiamondMesh(scale,scale*1.5,.33,scale*.2,colorLeaves);
leaf.geometry.applyMatrix(new THREE.Matrix4().makeRotationX(Math.PI/4));
leaf.position.x = Math.cos( stepAngle*i)*.3;
leaf.position.z = Math.sin( stepAngle*i)*.3;
leaf.rotation.y = Math.PI/2 - stepAngle*i;
this.crown.add(leaf);
}
this.core.position.x = this.stem.endPoint.x;
this.core.position.y = this.stem.endPoint.y;
this.core.position.z = this.stem.endPoint.z;
this.core.quaternion.setFromUnitVectors ( new THREE.Vector3( 0, 1, 0 ), this.stem.lastDirection);
this.core.add(this.crown);
this.core.add(this.fruit);
this.mesh.add(this.line);
this.mesh.add(this.core);
}
// LEAF
Leaf = function(scale, color){
this.type = "leaf";
this.mesh = new THREE.Object3D();
this.mesh.userData.refClass = this;
this.core = new CustomMesh.DiamondMesh(scale*2,scale*3,.33,scale*.1,color);
this.stem = new Stem(scale, color);
this.line = this.stem.mesh;
this.mesh.add(this.line);
this.core.position.x = this.stem.endPoint.x;
this.core.position.y = this.stem.endPoint.y;
this.core.position.z = this.stem.endPoint.z;
this.core.quaternion.setFromUnitVectors ( new THREE.Vector3( 0, 1, 0 ), this.stem.lastDirection);
this.mesh.add(this.core);
}
// STEM
Stem = function(scale, color, complex){
this.type = "stem";
this.lastDirection = new THREE.Vector3();
this.endPoint = new THREE.Vector3();
var linePoints = [];
var sl = scale/50;
var nHandlers = (complex)?6:3;
var i, a=0,
tx = 0, ty=0, tz=0,
pStartPoint = new THREE.Vector3(),
v;
for (i=0; i<nHandlers; i++){
v = new THREE.Vector3( tx, ty, tz );
linePoints.push(v);
if (i==nHandlers-2){
pStartPoint.x = tx;
pStartPoint.y = ty;
pStartPoint.z = tz;
}
if (i<nHandlers-1){
tx += Math2.rangeRandom(-2,2);
ty += sl*20;
tz += Math2.rangeRandom(-2,2);
}else{
this.endPoint.x = tx;
this.endPoint.y = ty;
this.endPoint.z = tz;
this.lastDirection.subVectors( this.endPoint, pStartPoint )
}
}
this.mesh = CustomMesh.CurvedPath(linePoints, scale/10, color);
this.mesh.userData.refClass = this;
}
// FOLIAGE
Foliage = function(scale, colorPalette, hierarchy,complex){
this.type="foliage";
var sw = (complex)? Math2.rangeRandomInt(3,10):Math2.rangeRandomInt(3,5);
var sh = Math2.rangeRandomInt(3,6);
var noise = (complex)? parameters.foliageNoise*scale : Math2.rangeRandom(scale/20,scale/5);
this.colPalette = colorPalette;
this.col = Colors.getRandomFrom(this.colPalette);
this.scale = scale;
this.mesh = new CustomMesh.SphereMesh(scale,sw,sh,this.col, false);
this.mesh.userData.refClass = this;
this.mesh.userData.hierarchy = hierarchy;
var geom = this.mesh.geometry;
geom.mergeVertices();
var h = scale*2;
var defAttachs;
if (complex){
defAttachs = [
{type:"subFol", count : 6, minH : h*.2, maxH:h*.9, minAngle:0, maxAngle:0 },
];
}else{
defAttachs = [];
}
this.attachsVerts = GeometryHelpers.getAttachs(geom, defAttachs);
GeometryHelpers.makeNoise(geom, noise);
CustomMesh.flatshadeGeometry(geom);
for (var i=0;i<this.attachsVerts.length;i++){
var attDef = this.attachsVerts[i];
var v = geom.vertices[attDef.index];
var s = Math2.rangeRandom(scale*.05, scale*.2);
var subFol = new SubFoliage(s, hierarchy+1).mesh;
attDef.mesh = subFol;
subFol.position.copy(v);
subFol.rotation.z = Math2.rangeRandom(-Math.PI/8, Math.PI/8);
subFol.rotation.x = Math2.rangeRandom(-Math.PI/8, Math.PI/8);
this.mesh.add(subFol);
}
if (complex) foliagesComplex.push(this);
else foliages.push(this);
}
SubFoliage = function(scale, hierarchy){
this.type = "subfoliage";
var sw = Math2.rangeRandomInt(2,4);
var sh = Math2.rangeRandomInt(2,4);
this.mesh = new CustomMesh.SphereMesh(scale,sw,sh,Colors.getRandomFrom(Colors.leaves), true);
this.mesh.userData.hierarchy = hierarchy;
this.mesh.userData.refClass = this;
}
Foliage.prototype.launchParticle = function(){
var p;
var col = Colors.getRandomFrom(this.colPalette);
if (waitingParticles.length){
p = waitingParticles.pop();
p.material.color.setHex(col);
}else {
p = new FlyingParticle(col, this.scale);
}
p.visible = true;
p.scale.x = p.scale.y = p.scale.z = this.scale/20;
p.userData.speedX = Math2.rangeRandom(1,3);
p.userData.speedY = Math2.rangeRandom(.5,1);
var vIndex = Math.floor(Math.random()*this.mesh.geometry.vertices.length);
var pos = this.mesh.geometry.vertices[vIndex].clone();
pos = this.mesh.localToWorld( pos );
p.position.copy(pos);
flyingParticles.push(p);
scene.add(p);
}
Foliage.prototype.grow = function(){
console.log("grow foliage");
var scope = this;
var geom = this.mesh.geometry;
var l = geom.vertices.length;
for (var i=0; i<l; i++){
var v = geom.vertices[i];
var d = Math.random()*.5 + v.y/25;
var s = Math.random() + 20/parameters.animationSpeed;
var sx = Math2.rangeRandom(-2,2);
var sy = Math2.rangeRandom(-2,2);
var sz = Math2.rangeRandom(-2,2);
TweenMax.from(v, s, {x:0,z:0, y:0, delay:d, ease:Strong.easeInOut, onUpdate:scope.replaceAttachs, onUpdateScope:scope});
}
grow(this.mesh);
}
Foliage.prototype.replaceAttachs = function(){
for (i=0; i<this.attachsVerts.length; i++){
var attDef = this.attachsVerts[i];
var v = this.mesh.geometry.vertices[attDef.index];
attDef.mesh.position.copy(v);
}
this.mesh.geometry.verticesNeedUpdate = true;
}
FlyingParticle = function(color){
var mesh;
var s = Math2.rangeRandom(1,4);
var segs = Math2.rangeRandomInt(3,5);
var segs2 = Math2.rangeRandomInt(2,5);
if (Math.random()<.25){
mesh = new CustomMesh.TriMesh(s,s,.2,color,"top");
}else if (Math.random()<.5){
mesh = new CustomMesh.RegPolyMesh(s,segs,1,color,false);
}else if (Math.random()<.75){
mesh = new CustomMesh.SphereMesh(s,segs,segs2,color,false);
}else{
mesh = new CustomMesh.CylinderMesh(0,s,s,segs, 1, color,true);
}
return mesh;
}
function grow(mesh){
//console.log("new grow");
for (var i=0; i<mesh.children.length; i++){
var child = mesh.children[i];
if (child.userData.refClass && child.userData.refClass.type == "foliage"){
child.userData.refClass.grow();
}else{
var d = (child.position.y / 200) + child.userData.hierarchy;
var s = 10+child.userData.hierarchy*2;
s = s/parameters.animationSpeed;
TweenMax.from(child.scale, s, {x:0.01, y:0.01, z:0.01, delay:d, ease:Back.easeInOut});
TweenMax.from(child.rotation, s, {x:0, y:0, z:0, delay:d, ease:Back.easeInOut});
grow(child);
}
}
}
window.addEventListener('load', init, false);
function init(event){
createStats();
initCore();
initGUI();
createLights();
createFloor();
createForest();
createTree();
loop();
}
function updateShadows(){
scene.traverse( function ( object ) {
if ( object instanceof THREE.Mesh ) {
object.castShadow = true;
object.receiveShadow = true;
}
});
}
function createStats(){
// STATS
stats = new Stats();
stats.setMode( 0 ); // 0: fps, 1: ms, 2: mb
// align top-left
stats.domElement.style.position = 'absolute';
stats.domElement.style.left = '0px';
stats.domElement.style.top = '0px';
document.body.appendChild( stats.domElement );
}
function loop(){
stats.begin();
console.log("waiting",waitingParticles.length);
var frustum = new THREE.Frustum();
frustum.setFromMatrix( new THREE.Matrix4().multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse ) );
//*
if (Math.random()>.5){
var fol = foliages[Math.floor(Math.random()*foliages.length)];
fol.launchParticle();
}
if (Math.random()>.9){
var fol = foliagesComplex[Math.floor(Math.random()*foliagesComplex.length)];
fol.launchParticle();
}
for (var i=0; i<flyingParticles.length; i++){
var p = flyingParticles[i];
p.position.x += p.userData.speedX + Math.random()/10;
p.position.y += p.userData.speedY + Math.random()/20;
p.rotation.x += .01 + Math.random()/100;
p.rotation.y += .01 + Math.random()/10;
p.rotation.z += .1 + Math.random()/20;
if(!frustum.containsPoint( p.position )){
p.visible = false;
waitingParticles.push(flyingParticles.splice(i,1)[0]);
}
}
//*/
render();
stats.end();
requestAnimationFrame(loop);
}
<script src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/264161/Math2.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/stats.js/r14/Stats.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r73/three.min.js"></script>
<script src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/264161/GeometryHelpers.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/gsap/1.16.1/TweenMax.min.js"></script>
<script src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/264161/scene_colors.js"></script>
<script src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/264161/OrbitControls.js"></script>
<script src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/264161/CustomMeshFlat.js"></script>
<script src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/264161/dat.gui.min.js"></script>
@import url(https://fonts.googleapis.com/css?family=Open+Sans:800);
#world{
position: absolute;
width:100%;
height: 100%;
background: #cefaeb;
overflow:hidden;
}
#credits{
position:absolute;
width:100%;
margin: auto;
bottom:0;
margin-bottom:20px;
font-family:'Open Sans', sans-serif;
color:#f7e1be;;
font-size:0.7em;
text-transform: uppercase;
text-align : center;
line-height:1.5;
letter-spacing: 2px;
}
#credits a {
color:#dbf4e8;
}
#credits a:hover {
color:#fb5d24;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment