Pen Settings

HTML

CSS

CSS Base

Vendor Prefixing

Add External Stylesheets/Pens

Any URL's added here will be added as <link>s in order, and before the CSS in the editor. If you link to another Pen, it will include the CSS from that Pen. If the preprocessor matches, it will attempt to combine them before processing.

+ add another resource

JavaScript

Babel is required to process package imports. If you need a different preprocessor remove all packages first.

Add External Scripts/Pens

Any URL's added here will be added as <script>s in order, and run before the JavaScript in the editor. You can use the URL of any other Pen and it will include the JavaScript from that Pen.

+ add another resource

Behavior

Save Automatically?

If active, Pens will autosave every 30 seconds after being saved once.

Auto-Updating Preview

If enabled, the preview panel updates automatically as you code. If disabled, use the "Run" button to update.

Format on Save

If enabled, your code will be formatted when you actively save your Pen. Note: your code becomes un-folded during formatting.

Editor Settings

Code Indentation

Want to change your Syntax Highlighting theme, Fonts and more?

Visit your global Editor Settings.

HTML

              
                <html>
<div id="world"> </div>
</html>
              
            
!

CSS

              
                html, body {
    margin: 0; //Remove default margins
    padding: 0;
   overflow: hidden;
}

#world {
  position: absolute;
  width: 100%;
  height: 100%;
  overflow: hidden;
  background: linear-gradient(#eff6f7, #47e5ff);
}

              
            
!

JS

              
                //Essential setup variables
var scene;
var renderer;
var container;

//Window constants
var HEIGHT = window.innerHeight;
var WIDTH = window.innerWidth;
var aspectRatio = WIDTH / HEIGHT;
var fieldOfView = 60;
var nearPlane = 1;
var farPlane = 10000;

//Lighting
var hemisphereLight, shadowLight, ambientLight;

//Scene variables (specific to sample) ----
var cube, cube2;

var mousePos = { x: 0, y: 0 };

var Colors = {
  red: 0xf25346,
  white: 0xd8d0d1,
  brown: 0x59332e,
  pink: 0xf5986e,
  brownDark: 0x23190f,
  blue: 0x68c3c0
};

//Texture loading - recommended (but more complicated) to be done before use
var textures = {
  stone: {
    url: "https://cdn.rawgit.com/antu3199/WebAssets/master/textures/stonePattern.jpg"
  },
  stoneBump: {
    url: "https://cdn.rawgit.com/antu3199/WebAssets/master/textures/normal2.jpg"
  }
};

var texturesLoaded = 0; //Counts number of loaded textures for determining when to start



var player;


var modelsLoaded = 0;

var controls;
var clock = new THREE.Clock();

var raycaster = new THREE.Raycaster();
var terrain;
//Main function:
function init() {
  loadTexturesAndStart(); //must load before creating scene
}

//Foreach texture, load, and then check if completed
function loadTexturesAndStart() {
  var loader = new THREE.TextureLoader(); //Textures must be loaded via TextureLoader

  //Prevent crossorigin issue
  loader.crossOrigin = "anonymous";

  //Load textures one by one (for a loading screen) - searches for .url tag, and adds a .texture field to the object
  for (var name in textures) {
    (function(name) {
      loader.load(textures[name].url, function(texture) {
        textures[name].texture = texture;
        checkLoadingTexturesFinished();
      });
    })(name);
  }
}

//Check if loading completed, if yes then createScene()!
function checkLoadingTexturesFinished() {
  texturesLoaded++;
  if (texturesLoaded == Object.keys(textures).length) {
    //Object.keys gives an array of the keys
    loadOBJsAndStart();
    // createScene();
  }
}
//Note: In blender, be sure to check Type: Geometry, and Textures: true

function loadOBJsAndStart() {
        document.body.classList.remove("loading");
    createScene();
}


var tester = 0;
//Real scene init function
function createScene() {
  if (tester == 0) {
    tester = 1;
    window.requestAnimationFrame(createScene);
    return;
  }
  initalizeScene();

  createLights();
  //	createCubes();
  createTerrain();
  //loadPlayer();

  document.addEventListener("mousemove", handleMouseMove, false); //Add mouse listener
  loop(); //Main Game Loop
  createObjects(); //Here because raycast costs one frame
}

function createTerrain() {
  var worldWidth = 64,
    worldDepth = 64;
  (worldHalfWidth = worldWidth / 2), (worldHalfDepth = worldDepth / 2);

  var data = generateHeight(worldWidth, worldDepth);
  //camera.position.y = data[ worldHalfWidth + worldHalfDepth * worldWidth ] * 10 + 500;
  var geometry = new THREE.PlaneBufferGeometry(
    1000,
    1000,
    worldWidth - 1,
    worldDepth - 1
  );
  geometry.rotateX(-Math.PI / 2);
  var vertices = geometry.attributes.position.array;
  for (var i = 0, j = 0, l = vertices.length; i < l; i++, j += 3) {
    vertices[j + 1] = data[i] * 10;
  }

  //mesh = new THREE.Mesh( geometry, new THREE.MeshBasicMaterial( { map: texture } ) );
  terrain = new THREE.Mesh(
    geometry,
    new THREE.MeshPhongMaterial({ flatShading: true })
  );
  scene.add(terrain);
  terrain.scale.set(1, 0.5, 1);
  terrain.position.set(0, -500, 0);
  terrain.receiveShadow = true;
}

function generateHeight(width, height) {
  var size = width * height,
    data = new Uint8Array(size),
    perlin = new ImprovedNoise(),
    quality = 1,
    z = Math.random() * 100;
  for (var j = 0; j < 4; j++) {
    for (var i = 0; i < size; i++) {
      var x = i % width,
        y = ~~(i / width);
      data[i] += Math.abs(
        perlin.noise(x / quality, y / quality, z) * quality * 1.75
      );
    }
    quality *= 5;
  }
  return data;
}

Player = function() {
  this.mesh = models.player.mesh;
};

function loadPlayer() {
  player = new Player();
  player.mesh.rotation.set(0, Math.PI / 4, 0);
  player.mesh.scale.set(50, 50, 50);
  scene.add(player.mesh);
}

//Initalizes the camera and the renderer
function initalizeScene() {
  scene = new THREE.Scene(); //Create scene - used to add objects inside
  scene.fog = new THREE.Fog(0xffffff, 100, 950); //Fog( hex, near, far )

  //Create camera
  camera = new THREE.PerspectiveCamera(
    fieldOfView, //Constants at the top of the file
    aspectRatio,
    nearPlane,
    farPlane
  );

  camera.position.x = 0;
  camera.position.z = 0;
  camera.position.y = 100;
  camera.rotation.x = -45;

  //Create renderer
  renderer = new THREE.WebGLRenderer({ alpha: true, antialias: true });
  renderer.setSize(WIDTH, HEIGHT);
  renderer.shadowMap.enabled = true;

  //Attach renderer to container in html
  container = document.getElementById("world");
  container.appendChild(renderer.domElement);

  controls = new THREE.FirstPersonControls(camera, renderer.domElement);
  controls.movementSpeed = 200;
  controls.lookSpeed = 0.15;
  controls.noFly = false;
  //controls.lookVertical = false;
  controls.lon = 270; //Note: Think circles (but positive is clockwise) - so starts off at right side
  controls.lat = -45;
  controls.target = new THREE.Vector3(0, 0, 0);
  controls.enabled = true;

  window.addEventListener("resize", handleWindowResize, false); //Handle resizing
}

// Handle window resizing
function handleWindowResize() {
  HEIGHT = window.innerHeight;
  WIDTH = window.innerWidth;
  renderer.setSize(WIDTH, HEIGHT);
  camera.aspect = WIDTH / HEIGHT;
  camera.updateProjectionMatrix();
}
THREE.DirectionalLight.prototype.addSphere = function() {
  this.sphere = new THREE.Mesh(
    new THREE.SphereGeometry(10, 16, 16),
    new THREE.MeshBasicMaterial({ color: this.color })
  );
  this.sphere.scale.set(1, 1, 1);
  this.add(this.sphere);
};

THREE.HemisphereLight.prototype.addSphere = function() {
  this.sphere = new THREE.Mesh(
    new THREE.SphereGeometry(10, 16, 16),
    new THREE.MeshBasicMaterial({ color: this.color })
  );
  this.sphere.scale.set(1, 1, 1);
  this.add(this.sphere);
};

function createLights() {
  //HemisphereLight (skyColour, groundColor, intensity);
  hemisphereLight = new THREE.HemisphereLight(0xd9f2f7, 0xeff6f7, 0.1);
  hemisphereLight.position.set(-50, 0, 0);
  //hemisphereLight.addSphere();
  //DirectionalLight (color, intensity)

  shadowLight = new THREE.DirectionalLight(0xffffff, 0.8);
  //shadowLight.position.set(1, 0, 0); // Set the direction of the light
  shadowLight.position.set(0, 100, 50);
  //shadowLight.addSphere();

  shadowLight.castShadow = true; // Allow shadow casting

  //define the visible area of the projected shadowLight
  shadowLight.shadow.camera.left = -400;
  shadowLight.shadow.camera.right = 400;
  shadowLight.shadow.camera.top = 400;
  shadowLight.shadow.camera.bottom = -400;
  shadowLight.shadow.camera.near = 1;
  shadowLight.shadow.camera.far = 1000;

  //Define resolution of the shadow - higher = better
  shadowLight.shadow.mapSize.width = 1024;
  shadowLight.shadow.mapSize.height = 1024;

  //Ambient lighting
  //An ambient light modifies the global colour of a scene and makes the shadows softer
  ambientLight = new THREE.AmbientLight(0xcff9d1, 0.2);

  //Activate lights - just add them to the scene
  scene.add(hemisphereLight);

  scene.add(shadowLight);
  scene.add(ambientLight);
}

//Adding a cube to the scene ---
//Cube object definiton
myCube = function(isUsingMaterial) {
  this.mesh = new THREE.Object3D();

  var cubeGeom = new THREE.BoxGeometry(45, 45, 45); //BoxGeometry(size-x, size-y, size-z)

  var cubeMat;
  if (isUsingMaterial == false) {
    //Regular flat shading material
    cubeMat = new THREE.MeshPhongMaterial({
      color: Colors.brown,
      flatShading: true
    });
  } else {
    //Material using loaded texture
    cubeMat = new THREE.MeshStandardMaterial({
      map: textures.stone.texture,
      bumpMap: textures.stoneBump.texture,
      bumpScale: 0.0003
    });
    // Repeat the pattern
    cubeMat.map.wrapS = THREE.RepeatWrapping;
    cubeMat.map.wrapT = THREE.RepeatWrapping;
    cubeMat.map.repeat.set(2, 2); //Value = # of repetitions in (x = s) / (y = t)
    cubeMat.bumpMap.wrapS = THREE.RepeatWrapping;
    cubeMat.bumpMap.wrapT = THREE.RepeatWrapping;
    cubeMat.bumpMap.repeat.set(2, 2);
  }

  var cubeMesh = new THREE.Mesh(cubeGeom, cubeMat); //create mesh
  this.mesh.add(cubeMesh); //add mesh to Object3D() container

  this.mesh.receiveShadow = true;
  this.mesh.castShadow = true;
};

//Creating cube and setting position
function createCubes() {
  cube = new myCube(false);
  cube.mesh.position.set(125, 0, 0);
  cube.mesh.rotation.set(Math.PI / 4, Math.PI / 4, 0);

  scene.add(cube.mesh);

  cube2 = new myCube(true);
  cube2.mesh.position.set(-125, 0, 0);
  cube2.mesh.rotation.set(Math.PI / 4, Math.PI / 4, 0);

  scene.add(cube2.mesh);
}

function handleMouseMove(event) {
  var tx = -1 + event.clientX / WIDTH * 2; //Convert mouse position to values from -1 to 1

  var ty = 1 - event.clientY / HEIGHT * 2; //2D y-axis goes opposite direction of 3D y-axis

  mousePos = { x: tx, y: ty };
}

//given v on a scale from vmin to vmax, where does the value lie in interval tmin to tmax?
//i.e.  (v interval) -> (t interval) mapping
//Used for updateCameraFov()
function normalize(v, vmin, vmax, tmin, tmax) {
  var nv = Math.max(Math.min(v, vmax), vmin);
  var dv = vmax - vmin;
  var pc = (nv - vmin) / dv;
  var dt = tmax - tmin;
  var tv = tmin + pc * dt;
  return tv;
}

function makeTextSprite(message, parameters) {
  if (parameters === undefined) parameters = {};

  var fontface = parameters.hasOwnProperty("fontface")
    ? parameters["fontface"]
    : "Arial";

  var fontsize = parameters.hasOwnProperty("fontsize")
    ? parameters["fontsize"]
    : 18;

  var borderThickness = parameters.hasOwnProperty("borderThickness")
    ? parameters["borderThickness"]
    : 4;

  var borderColor = parameters.hasOwnProperty("borderColor")
    ? parameters["borderColor"]
    : { r: 0, g: 0, b: 0, a: 1.0 };

  var backgroundColor = parameters.hasOwnProperty("backgroundColor")
    ? parameters["backgroundColor"]
    : { r: 255, g: 255, b: 255, a: 1.0 };
  var spriteAlignment = 10;
  var canvas = document.createElement("canvas");
  canvas.width = 256;
  canvas.height = 128;

  var context = canvas.getContext("2d");
  context.font = "Bold " + fontsize + "px " + fontface;

  // get size data (height depends only on font size)
  var metrics = context.measureText(message);
  var textWidth = metrics.width;

  // background color
  context.fillStyle =
    "rgba(" +
    backgroundColor.r +
    "," +
    backgroundColor.g +
    "," +
    backgroundColor.b +
    "," +
    backgroundColor.a +
    ")";
  // border color
  context.strokeStyle =
    "rgba(" +
    borderColor.r +
    "," +
    borderColor.g +
    "," +
    borderColor.b +
    "," +
    borderColor.a +
    ")";
  context.lineWidth = borderThickness;
  roundRect(
    context,
    borderThickness / 2,
    borderThickness / 2,
    textWidth + borderThickness,
    fontsize * 1.4 + borderThickness,
    6
  );
  // 1.4 is extra height factor for text below baseline: g,j,p,q.

  // text color
  context.fillStyle = "rgba(0, 0, 0, 1.0)";
  context.fillText(message, borderThickness, fontsize + borderThickness);

  // canvas contents will be used for a texture
  var texture = new THREE.Texture(canvas);
  texture.needsUpdate = true;
  var spriteMaterial = new THREE.SpriteMaterial({ map: texture });
  var sprite = new THREE.Sprite(spriteMaterial);
  sprite.scale.set(100, 50, 1.0);
  return sprite;
}

// function for drawing rounded rectangles
function roundRect(ctx, x, y, w, h, r) {
  ctx.beginPath();
  ctx.moveTo(x + r, y);
  ctx.lineTo(x + w - r, y);
  ctx.quadraticCurveTo(x + w, y, x + w, y + r);
  ctx.lineTo(x + w, y + h - r);
  ctx.quadraticCurveTo(x + w, y + h, x + w - r, y + h);
  ctx.lineTo(x + r, y + h);
  ctx.quadraticCurveTo(x, y + h, x, y + h - r);
  ctx.lineTo(x, y + r);
  ctx.quadraticCurveTo(x, y, x + r, y);
  ctx.closePath();
  ctx.fill();
  ctx.stroke();
}

function showGeoVertices(geometry, position) {
  for (var i = 0; i < geometry.vertices.length; i++) {
    var spritey = makeTextSprite(i, {
      fontsize: 12,
      backgroundColor: { r: 255, g: 100, b: 100, a: 1 }
    });
    spritey.position.set(
      geometry.vertices[i].x + position.x,
      geometry.vertices[i].y + position.y,
      geometry.vertices[i].z + position.z
    );
    scene.add(spritey);
  }
}

var lastPressed = 0;

var keyboard = new THREEx.KeyboardState();
//Render loop
function loop() {
  var delta = clock.getDelta();
  controls.update(delta);
  lastPressed += delta;

  if (keyboard.pressed("T") && lastPressed >= 0.5) {
    lastPressed = 0;
    controls.enabled = !controls.enabled;
  }

  renderer.render(scene, camera); // render the scene

  window.requestAnimationFrame(loop); // call the loop function again
}

Tree = function(position) {
  var treeMaterial = new THREE.MeshPhongMaterial({
    color: "#c1ffc6",
    flatShading: true
  });
  var wireframeMaterial = new THREE.MeshBasicMaterial({
    color: 0x00ee00,
    wireframe: true,
    transparent: true
  });

  var multiMaterial = [treeMaterial];

  var cylinderGeo = new THREE.CylinderGeometry(15, 30, 100, 6, 4);

  //showGeoVertices(cylinderGeo, position);
  for (var i = 6; i <= 17; i++) {
    cylinderGeo.vertices[i].set(
      cylinderGeo.vertices[i].x + -10 + 20 * Math.random(),
      cylinderGeo.vertices[i].y + -10 + 20 * Math.random(),
      cylinderGeo.vertices[i].z + -10 + 20 * Math.random()
    );
  }

  //var shape = THREE.SceneUtils.createMultiMaterialObject(
  //		cylinderGeo, treeMaterial );
  var shape = new THREE.Mesh(cylinderGeo, treeMaterial);

  shape.position.set(position.x, position.y, position.z);
  shape.receiveShadow = true;
  shape.castShadow = true;

  var cylinderGeo = new THREE.CylinderGeometry(10, 10, 140, 6, 4);
  var trunkMaterial = new THREE.MeshPhongMaterial({
    color: "#e2dec9",
    flatShading: true
  });

  var trunk = new THREE.Mesh(cylinderGeo, trunkMaterial);
  trunk.position.set(0, -80, 0);
  trunk.castShadow = true;
  trunk.receiveShadow = true;

  shape.add(trunk);
  scene.add(shape);
  this.mesh = shape;
};

Cloud = function() {
  //Create empty container that will hold different parts of the Cloud
  this.mesh = new THREE.Object3D();

  //create a cube geometry;
  //this shape will be duplicated to create the cloud:
  //BoxGeometry(width, height, depth)
  var geom = new THREE.BoxGeometry(20, 20, 20);

  //Single white material
  var mat = new THREE.MeshPhongMaterial({
    color: 0xd9f2f7
  });

  //duplicate the geometry a random number of times
  var numBlocks = 3 + Math.floor(Math.random() * 3); //3-6 blocks = 1 Cloud
  for (var i = 0; i < numBlocks; i++) {
    //create the mesh by cloninng the geometry
    var m = new THREE.Mesh(geom, mat);

    //set the postion and the rotation of each cube randomly
    m.position.x = i * 15;
    m.position.y = Math.random() * 10;
    m.position.z = Math.random() * 10;
    m.rotation.z = Math.random() * Math.PI * 2;
    m.rotation.y = Math.random() * Math.PI * 2;

    //Set size of cube randomly - from 0.1 to 0.9
    var s = 0.1 + Math.random() * 0.9;
    m.scale.set(s, s, s);

    //allow each cube to cast and to receive shadows
    m.castShadow = true;
    m.receiveShadow = true;

    //Add the cube to the container we first created
    this.mesh.add(m);
  }
};

function createObjects() {
  /*
	var planeGeometry = new THREE.PlaneGeometry(100,100,1,1);
	var plane = new THREE.Mesh( planeGeometry, new THREE.MeshPhongMaterial( {color: "#e8e8e8"}) );
	plane.position.set( 0, -50, 0 );
	plane.scale.set(10,10,10);
	plane.rotation.set(-Math.PI/2, 0, 0);


	scene.add(plane);
	*/
  var numOfTrees = 20;

  for (var i = 0; i < numOfTrees; i++) {
    var angle = 2 * Math.PI * i / numOfTrees;
    var radius = 200;
    var sinTheta = radius * Math.sin(angle);
    var cosTheta = radius * Math.cos(angle);
    //var tree = new Tree( new THREE.Vector3(cosTheta, 50,sinTheta));
    var tree = new Tree(
      new THREE.Vector3(
        -400 + Math.random() * 800,
        1000,
        -400 + Math.random() * 800
      )
    );

    raycaster.set(tree.mesh.position, new THREE.Vector3(0, -1, 0));
    var intersects = raycaster.intersectObject(terrain, true);
    if (intersects.length > 0) {
      //tree.mesh.lookAt(intersects[0].face.normal);
      tree.mesh.position.copy(
        intersects[0].point.add(intersects[0].face.normal.multiplyScalar(90))
      );

      //console.log("Hit2");
    }
  }

  for (var i = 0; i < numOfTrees; i++) {
    var cloud = new Cloud();
    cloud.mesh.position.set(
      -400 + Math.random() * 800,
      500,
      -400 + Math.random() * 800
    );
    cloud.mesh.rotation.set(
      -Math.PI / 4 + Math.random() * Math.PI / 2,
      Math.PI * 2 / Math.random(),
      0
    );

    cloud.mesh.scale.set(1, 1, 1);
    scene.add(cloud.mesh);

    raycaster.set(cloud.mesh.position, new THREE.Vector3(0, -1, 0));
    var intersects = raycaster.intersectObject(terrain, true);
    if (intersects.length > 0) {
      //tree.mesh.lookAt(intersects[0].face.normal);
      cloud.mesh.position.copy(
        intersects[0].point.add(
          new THREE.Vector3(0, 100 + Math.random() * 300, 0)
        )
      );

      //console.log("Hit2");
    }
  }

  //var tree = new Tree(new THREE.Vector3(0, 50,0));
}

init();
              
            
!
999px

Console