<!DOCTYPE html>
<html>
<head>
<title>CS 86 example: House</title>
<script type="importmap">
{
"imports": {
"three": "https://unpkg.com/three@0.181.0/build/three.module.js",
"three/addons/": "https://unpkg.com/three@0.181.0/examples/jsm/"
}
}
</script>
<script type="module">
class SceneGraph extends THREE.Scene {
constructor () {
super ()
// House, with appropriate origin
let h = new House ()
h.position.set (-8, -6, -10)
this.add (h)
}
}
/*
* Foley & van Dam textbook house,
* a subtree with 7 nodes, one each wall or surface
*/
class House extends THREE.Object3D { //*1 Our constants
constructor () { //*1
super ()
/*
* Basic dimensions of the house, as constants
*/
House.X1HOUSE = 0; // Corner //*1
House.Y1HOUSE = 0; //*1
House.Z1HOUSE = 0; //*1
House.X2HOUSE = 16; // Oppos corner of main body, not peak //*1
House.Y2HOUSE = 10; //*1
House.Z2HOUSE = -24; //*1
House.XPEAKHOUSE = 8; // Top center of peak //*1
House.YPEAKHOUSE = 16; //*1
/*
* We'll keep our own master list of the 10 vertices of the house
*/
let v = []
v[0] = new THREE.Vector3 (House.X1HOUSE, House.Y1HOUSE, House.Z1HOUSE); // Coords of vertex 0 //*2 Our vertices
v[1] = new THREE.Vector3 (House.X2HOUSE, House.Y1HOUSE, House.Z1HOUSE); // Coords of vertex 1 //*2
v[2] = new THREE.Vector3 (House.X2HOUSE, House.Y2HOUSE, House.Z1HOUSE); //*2
v[3] = new THREE.Vector3 (House.XPEAKHOUSE, House.YPEAKHOUSE, House.Z1HOUSE); //*2
v[4] = new THREE.Vector3 (House.X1HOUSE, House.Y2HOUSE, House.Z1HOUSE); //*2
v[5] = new THREE.Vector3 (House.X1HOUSE, House.Y1HOUSE, House.Z2HOUSE); //*2
v[6] = new THREE.Vector3 (House.X2HOUSE, House.Y1HOUSE, House.Z2HOUSE); //*2
v[7] = new THREE.Vector3 (House.X2HOUSE, House.Y2HOUSE, House.Z2HOUSE); //*2
v[8] = new THREE.Vector3 (House.XPEAKHOUSE, House.YPEAKHOUSE, House.Z2HOUSE); //*2
v[9] = new THREE.Vector3 (House.X1HOUSE, House.Y2HOUSE, House.Z2HOUSE); //*2
/*
* Front wall of house, made up of 3 triangles, all yellow
*/
let frontGeom = new THREE.BufferGeometry () //*3 Front wall
frontGeom.setFromPoints ([ //*3
// Vertices for the first triangle = front wall bottom right triangle,
// chosen from the master list
v[0], v[1], v[2], //*3
// Vertices for the second triangle = front wall top left triangle
v[2], v[4], v[0], //*3
// Front pediment
v[2], v[3], v[4] //*3
]);
// For lighting calculcations
frontGeom.computeVertexNormals(); //*5 Need normal vectors for lighting
// Create the actual Mesh and add it to House
this.add (new THREE.Mesh(frontGeom, //*3
new THREE.MeshPhongMaterial ({ color: "yellow" }))) //*3
/*
* Right wall of house, 2 triangles, green
*/
let rightGeom = new THREE.BufferGeometry ();
rightGeom.setFromPoints ([
v[1], v[6], v[7], //*4 Right wall
v[7], v[2], v[1] //*4
])
rightGeom.computeVertexNormals();
this.add (new THREE.Mesh (rightGeom, //*4
new THREE.MeshPhongMaterial ({ color: "green" }))) //*4
/*
* Left wall, blue
*/
let leftGeom = new THREE.BufferGeometry ()
leftGeom.setFromPoints ([
v[0], v[4], v[9],
v[9], v[5], v[0]
])
leftGeom.computeVertexNormals();
this.add (new THREE.Mesh (leftGeom,
new THREE.MeshPhongMaterial ({ color: "blue" })))
/*
* Back wall of house, made up of 3 triangles, yellow
*/
let backGeom = new THREE.BufferGeometry ()
backGeom.setFromPoints ([
v[5], v[9], v[7],
v[7], v[6], v[5],
v[7], v[9], v[8]
])
backGeom.computeVertexNormals();
this.add (new THREE.Mesh (backGeom,
new THREE.MeshPhongMaterial ({ color: "yellow" })))
/*
* Bottom (floor), red
*/
let bottomGeom = new THREE.BufferGeometry ()
bottomGeom.setFromPoints ([
v[0], v[5], v[6],
v[6], v[1], v[0]
])
bottomGeom.computeVertexNormals();
this.add (new THREE.Mesh (bottomGeom,
new THREE.MeshPhongMaterial ({ color: "red" })))
/*
* Left roof, red
*/
let lroofGeom = new THREE.BufferGeometry ()
lroofGeom.setFromPoints ([
v[4], v[3], v[8],
v[8], v[9], v[4]
])
lroofGeom.computeVertexNormals();
this.add (new THREE.Mesh (lroofGeom,
new THREE.MeshPhongMaterial ({ color: "red" })))
/*
* Right roof, magenta
*/
let rroofGeom = new THREE.BufferGeometry ()
rroofGeom.setFromPoints ([
v[2], v[7], v[8],
v[8], v[3], v[2]
])
rroofGeom.computeVertexNormals();
this.add (new THREE.Mesh (rroofGeom,
new THREE.MeshPhongMaterial ({ color: "magenta" })))
}
}
/*
* An object to hold the lights, same as previous
*/
class Lights {
constructor (scene) {
// Uses typical lighting setup, like portrait or TV studio...
// Main (key) light, directional,
// from 45 deg. user's right, above, bright white
this.mainLight = new THREE.DirectionalLight ("white", 2 * Math.PI)
this.mainLight.position.set (1, 0.5, 1)
scene.add (this.mainLight)
// Fill light, directional, from 45deg. user's left,
// white, half as bright
this.fillLight = new THREE.DirectionalLight ("white", 1 * Math.PI)
this.fillLight.position.set (-1, 0, 1 )
scene.add (this.fillLight)
// Ambient light, white, still less bright
this.ambientLight = new THREE.AmbientLight ("white", 0.1 * Math.PI)
scene.add (this.ambientLight);
}
}
/*
* The rest of this is the same boilerplate as previously
*/
import * as THREE from "three";
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
class Main {
constructor () {
// Set up window for 3D
this.renderer = new THREE.WebGLRenderer( { antialias: true } );
this.renderer.setClearColor( new THREE.Color ("lightgrey"))
this.renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( this.renderer.domElement );
// Create our scene
this.scene = new SceneGraph();
// Add our lights to the scene, we keep in a separate class
new Lights(this.scene);
// Create the camera
this.camera = new THREE.PerspectiveCamera( 60, window.innerWidth / window.innerHeight, 1, 1000 );
this.camera.position.z = 5;
// Create a camera contol
new OrbitControls( this.camera, this.renderer.domElement );
// Start animation loop
this.animate()
// In case window is resized
// use () form of function definition cause need "this"
// but don't need "event" arg
window.onresize = () => this.onResize()
}
/*
* Render the scene
*/
render() {
this.renderer.render( this.scene, this.camera );
}
/*
* Animation loop
*/
animate () {
requestAnimationFrame (() => {this.animate()});
this.render()
}
// In case window is resized
onResize () {
this.renderer.setSize( window.innerWidth, window.innerHeight );
this.camera.aspect = window.innerWidth / window.innerHeight;
this.camera.updateProjectionMatrix();
this.render()
}
}
window.onload = () => new Main ()
</script>
</head>
</html>