<!DOCTYPE html> <html> <head> <title>Comp 86 example: House</title> <script type="importmap"> { "imports": { "three": "https://unpkg.com/three@0.156.0/build/three.module.js", "three/addons/": "https://unpkg.com/three@0.156.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", 1 * 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", 0.5 * 2*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.05 * 2*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>