<!DOCTYPE html> <html> <head> <title>Comp 86 example: Robot, without animation</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() // First robot, position = WC origin to robot local origin let r = new Robot () //*1 Add two robots to scene graph r.position.set (0, -5, -10) //*1 this.add (r) //*1 // A second robot, different local origin r = new Robot () //*1 r.position.set (-5, -5, -20) //*1 r.rotation.set (0, -20 * Math.PI/180, 0) //*1 this.add (r) //*1 } } /* * We make a Robot out of other library objects */ class Robot extends THREE.Object3D { constructor () { super () /* * Initialize some Materials we will be using */ let platform = new THREE.MeshPhongMaterial ({ color: 0x4c4c33 }) //*2 Some stock materials let pants = new THREE.MeshPhongMaterial ({ color: 0x0c0c4c }) //*2 let shirt = new THREE.MeshPhongMaterial ({ color: 0xffcc80 }) //*2 let skin = new THREE.MeshPhongMaterial ({ color: 0xffd9b3, shininess: 60 }) //*2 /* * The base */ // The bottom flat part of base let base = new THREE.Mesh ( //*3 Base new THREE.BoxGeometry (1.5, 1, 1.25), platform) //*3 // (transform = local offset for center of box, ie 0.5 radius) base.position.set (0, 1/2., 0) //*3 this.add (base) //*3 // The column let column = new THREE.Mesh ( //*4 Column new THREE.CylinderGeometry (.4, .4, 2.5, 32, 32, false), pants) //*4 column.position.set (0, 1.0 + 2.5/2.0, 0) //*4 this.add (column) //*4 /* * Upper body */ // Just a tranform node, no geometry // ie root node for the rest of the upper body, // and then work from its local origin let upperbody = new THREE.Object3D () //*5 Upper body transform only upperbody.position.set (0, 1+2.5, 0) //*5 this.add (upperbody) //*5 // Trunk let trunk = new THREE.Mesh ( //*6 Trunk, added to upperbody not root new THREE.BoxGeometry (2, 3, 2), shirt) //*6 trunk.position.set (0, 3/2., 0) //*6 upperbody.add (trunk) //*6 // Head let head = new THREE.Mesh ( //*7 Head new THREE.BoxGeometry (1, 1, 1), skin) //*7 head.position.set (0, 3 + 1/2., 0) //*7 upperbody.add (head) //*7 // Right armhand let rightArmHand = new ArmHand (shirt, skin) //*8 Right armhand, separate object rightArmHand.position.set (-1.25, 3, 0) //*8 upperbody.add (rightArmHand) //*8 // Left armhand let leftArmHand = new ArmHand (shirt, skin) //*9 Left armhand, reuse object leftArmHand.position.set (1.25, 3, 0) //*9 leftArmHand.rotation.set (0, Math.PI, 0) //*9 upperbody.add (leftArmHand) //*9 } } /* * Separate object that makes the Arm + Hand assembly */ class ArmHand extends THREE.Object3D { //*8 constructor (shirt, skin) { super () // Arm let arm = new THREE.Mesh ( //*8 new THREE.BoxGeometry (0.5, 2.5, 0.5), shirt) //*8 arm.position.set (0, -1.25, 0) //*8 this.add (arm) // Finger let finger = new THREE.Mesh ( //*8 new THREE.BoxGeometry (0.1, .5, .5), skin) //*8 finger.position.set (-0.25+0.05, -2.5-0.25, 0) //*8 this.add (finger) // Thumb let thumb = new THREE.Mesh ( //*8 new THREE.BoxGeometry (0.1, .5, .5), skin) //*8 thumb.position.set (0.25-0.1-0.05, -2.5-0.25, 0.) //*8 this.add (thumb) } } /* * 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>