tp/src/standalone/train.js (10880B)
1 import * as THREE from 'three'; 2 import * as dat from 'dat.gui'; 3 import * as BufferGeometryUtils from 'three/addons/utils/BufferGeometryUtils.js'; 4 5 import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'; 6 7 let scene, camera, renderer, container, terrainMaterial, instancedTrees; 8 9 let time = 0.0; 10 11 function onResize() { 12 camera.aspect = container.offsetWidth / container.offsetHeight; 13 camera.updateProjectionMatrix(); 14 renderer.setSize(container.offsetWidth, container.offsetHeight); 15 } 16 17 function setupThreeJs() { 18 scene = new THREE.Scene(); 19 container = document.getElementById('mainContainer'); 20 21 renderer = new THREE.WebGLRenderer(); 22 renderer.setClearColor(0x606060); 23 container.appendChild(renderer.domElement); 24 25 camera = new THREE.PerspectiveCamera(35, window.innerWidth / window.innerHeight, 0.1, 1000); 26 camera.position.set(-50, 60, 50); 27 camera.lookAt(0, 0, 0); 28 29 const controls = new OrbitControls(camera, renderer.domElement); 30 31 const ambientLight = new THREE.AmbientLight(0xAAAAAA); 32 scene.add(ambientLight); 33 34 const hemisphereLight = new THREE.HemisphereLight(0xffffff, 0x000000, 0.25); 35 scene.add(hemisphereLight); 36 37 const directionalLight = new THREE.DirectionalLight(0xffffff, 1); 38 directionalLight.position.set(100, 100, 100); 39 scene.add(directionalLight); 40 41 const directionalLightHelper = new THREE.DirectionalLightHelper(directionalLight, 5); 42 // scene.add(directionalLightHelper); 43 44 const gridHelper = new THREE.GridHelper(50, 20); 45 scene.add(gridHelper); 46 47 const axesHelper = new THREE.AxesHelper(5); 48 scene.add(axesHelper); 49 50 window.addEventListener('resize', onResize); 51 onResize(); 52 } 53 54 const steamChamberLen = 10; 55 const steamChamberRad = 2.50; 56 const steamChamberEndRad = steamChamberRad+0.375; 57 const steamChamberEndLen = 2.50; 58 const cabinLen = 5; 59 const cabinHeight = 6; 60 const cabinRoofHeight = 3; 61 const cabinWallThickness = 0.375; 62 const wheelRad = 1.475; 63 const chassisHeight = 2.5; 64 const wheelThickness = 0.425; 65 const chassisOffset = 1.245; 66 const wheelOffset = -0.45; 67 const steamCylindersLen = 4; 68 const crankLen = 12; 69 const crankOffset = 1.50; 70 const crankWidth = 0.25; 71 72 let crankLeft, crankRight; 73 74 function buildCabinRoof() { 75 console.log('Building train cabin roof'); 76 const geometry = new THREE.BoxGeometry(6, cabinWallThickness, 6); 77 return geometry; 78 } 79 80 function buildCabin() { 81 console.log('Building train cabin'); 82 83 let cabin = []; 84 85 const cabinFront = new THREE.BoxGeometry( 86 steamChamberRad*2, 87 cabinWallThickness, 88 cabinHeight); 89 90 cabinFront.translate(0, cabinLen/2, -cabinHeight/2); 91 cabin.push(cabinFront); 92 93 const cabinLeft = new THREE.BoxGeometry( 94 steamChamberRad*2, 95 cabinWallThickness, 96 cabinHeight); 97 98 cabinLeft.rotateZ(Math.PI/2); 99 cabinLeft.translate( 100 steamChamberRad-cabinWallThickness/2, 101 cabinWallThickness/2, 102 -cabinHeight/2); 103 104 cabin.push(cabinLeft); 105 106 const cabinRight = new THREE.BoxGeometry( 107 steamChamberRad*2, 108 cabinWallThickness, 109 cabinHeight); 110 111 cabinRight.rotateZ(Math.PI/2); 112 cabinRight.translate( 113 -steamChamberRad+cabinWallThickness/2, 114 cabinWallThickness/2, 115 -cabinHeight/2); 116 117 cabin.push(cabinRight); 118 119 const g1 = new THREE.BoxGeometry( 120 cabinWallThickness, cabinWallThickness, cabinRoofHeight); 121 122 g1.rotateZ(Math.PI/2); 123 g1.translate( 124 -steamChamberRad+(cabinWallThickness/2), 125 -steamChamberRad+cabinWallThickness, 126 -cabinHeight-cabinRoofHeight/2); 127 128 cabin.push(g1); 129 130 const g2 = new THREE.BoxGeometry( 131 cabinWallThickness, cabinWallThickness, cabinRoofHeight); 132 133 g2.rotateZ(Math.PI/2); 134 g2.translate( 135 steamChamberRad-cabinWallThickness/2, 136 steamChamberRad, 137 -cabinHeight-cabinRoofHeight/2); 138 139 cabin.push(g2); 140 141 const g3 = new THREE.BoxGeometry( 142 cabinWallThickness, cabinWallThickness, cabinRoofHeight); 143 144 g3.rotateZ(Math.PI/2); 145 g3.translate( 146 steamChamberRad-cabinWallThickness/2, 147 -steamChamberRad+cabinWallThickness, 148 -cabinHeight-cabinRoofHeight/2); 149 150 cabin.push(g3); 151 152 const g4 = new THREE.BoxGeometry( 153 cabinWallThickness, cabinWallThickness, cabinRoofHeight); 154 155 g4.rotateZ(Math.PI/2); 156 g4.translate( 157 -steamChamberRad+cabinWallThickness/2, 158 steamChamberRad, 159 -cabinHeight-cabinRoofHeight/2); 160 161 cabin.push(g4); 162 163 const geometry = BufferGeometryUtils.mergeGeometries(cabin); 164 geometry.rotateX(Math.PI/2); 165 return geometry; 166 } 167 168 function buildChamber() { 169 let geometries = []; 170 171 const steamChamber = new THREE.CylinderGeometry(steamChamberRad, 172 steamChamberRad, steamChamberLen, 32); 173 174 geometries.push(steamChamber); 175 176 const steamChamberEnd = new THREE.CylinderGeometry(steamChamberEndRad, 177 steamChamberEndRad, steamChamberEndLen, 32); 178 179 steamChamberEnd.translate(0,steamChamberLen/2 + steamChamberEndLen/2,0); 180 geometries.push(steamChamberEnd); 181 182 const floor = new THREE.BoxGeometry( 183 steamChamberRad*2, steamChamberLen + steamChamberEndLen + cabinLen, 1.0); 184 floor.translate(0, -steamChamberEndLen/2, steamChamberRad); 185 geometries.push(floor); 186 187 const chamberPipeLen = 4; 188 const chamberPipe = new THREE.CylinderGeometry(0.55, 0.55, chamberPipeLen, 32); 189 chamberPipe.translate(0, -(steamChamberRad + chamberPipeLen/2)+1.0, 190 -(steamChamberLen+steamChamberEndLen)/2); 191 192 chamberPipe.rotateX(Math.PI/2); 193 geometries.push(chamberPipe); 194 195 const geometry = BufferGeometryUtils.mergeGeometries(geometries); 196 geometry.rotateX(Math.PI/2); 197 geometry.translate(0, steamChamberRad+0.25, 0); 198 return geometry; 199 } 200 201 function buildTrainWheel() { 202 const wheel = new THREE.CylinderGeometry(wheelRad, wheelRad, wheelThickness); 203 wheel.rotateZ(Math.PI/2); 204 205 const wheelBolt = new THREE.CylinderGeometry(wheelRad, wheelRad, wheelThickness); 206 wheelBolt.rotateZ(Math.PI/2); 207 208 const wheelsMaterial = new THREE.MeshPhongMaterial({ 209 color: 0x393939, 210 side: THREE.DoubleSide, 211 shininess: 100.0 212 }); 213 214 return new THREE.Mesh(wheel, wheelsMaterial) 215 } 216 217 function buildTrainAxe(material) { 218 const axeGeometry = new THREE.CylinderGeometry(0.325, 0.325, 5); 219 axeGeometry.rotateZ(Math.PI/2); 220 221 const axeMaterial = new THREE.MeshPhongMaterial({ 222 color: 0x7A7F80, 223 side: THREE.DoubleSide, 224 shininess: 100.0 225 }); 226 227 return new THREE.Mesh(axeGeometry, axeMaterial); 228 } 229 230 function buildTrainChassis() { 231 const chassis = new THREE.BoxGeometry(3.5, 2.5, steamChamberLen+steamChamberEndLen+cabinLen); 232 return chassis; 233 } 234 235 function buildTrain() { 236 console.log('Building train'); 237 const train = new THREE.Group(); 238 239 const chassisGeometry = buildTrainChassis(); 240 const chassisMaterial = new THREE.MeshPhongMaterial({ 241 color: 0x7A7F80, 242 side: THREE.DoubleSide, 243 shininess: 100.0 244 }); 245 246 const chassis = new THREE.Mesh(chassisGeometry, chassisMaterial); 247 train.add(chassis); 248 249 const chamberGeometry = buildChamber(); 250 const chamberMaterial = new THREE.MeshPhongMaterial({ 251 color: 0xFA1A09, 252 side: THREE.DoubleSide, 253 shininess: 100.0 254 }); 255 256 const chamber = new THREE.Mesh(chamberGeometry, chamberMaterial); 257 chassis.add(chamber); 258 chamber.position.set(0, (chassisHeight + cabinWallThickness)/2, chassisOffset); 259 260 const cabinGeometry = buildCabin(); 261 const cabin = new THREE.Mesh(cabinGeometry, chamberMaterial); 262 chassis.add(cabin); 263 cabin.position.set(0, 264 (chassisHeight + cabinWallThickness)/2, 265 -steamChamberLen+(cabinLen/2)+chassisOffset); 266 267 const cabinRoofGeometry = buildCabinRoof(); 268 const roofMaterial = new THREE.MeshPhongMaterial({ 269 color: 0xFBEC50, 270 side: THREE.DoubleSide, 271 shininess: 100.0 272 }); 273 274 const cabinRoof = new THREE.Mesh(cabinRoofGeometry, roofMaterial); 275 cabin.add(cabinRoof); 276 cabinRoof.position.set(0, cabinHeight+cabinRoofHeight+cabinWallThickness/2, 0); 277 278 const a1 = buildTrainAxe(); 279 chassis.add(a1); 280 281 const a2 = buildTrainAxe(); 282 chassis.add(a2); 283 284 const a3 = buildTrainAxe(chassisMaterial); 285 chassis.add(a3); 286 287 a1.position.set(0, wheelOffset, -0.60); 288 a2.position.set(0, wheelOffset, -0.60+wheelRad*2.5); 289 a3.position.set(0, wheelOffset, -0.60-wheelRad*2.5); 290 291 const cylinderLeft = new THREE.CylinderGeometry(1.25, 1.5, steamCylindersLen); 292 cylinderLeft.rotateX(Math.PI/2); 293 cylinderLeft.translate(steamChamberRad-0.25, -.25, steamChamberLen-steamCylindersLen/1.5); 294 295 const cylinderRight = new THREE.CylinderGeometry(1.25, 1.5, steamCylindersLen); 296 cylinderRight.rotateX(Math.PI/2); 297 cylinderRight.translate(-steamChamberRad+0.25, -.25, steamChamberLen-steamCylindersLen/1.5); 298 299 const cylindersGeometry = BufferGeometryUtils.mergeGeometries([cylinderRight, cylinderLeft]); 300 const cylindersMaterial = new THREE.MeshPhongMaterial({ 301 color: 0x393939, 302 side: THREE.DoubleSide, 303 shininess: 100.0 304 }); 305 306 chassis.add(new THREE.Mesh(cylindersGeometry, cylindersMaterial)); 307 chassis.position.set(0,-2,-2.75); 308 309 const w1 = buildTrainWheel(); 310 w1.position.set(steamChamberRad-wheelThickness/2.1,0,0); 311 a1.add(w1); 312 313 const w2 = buildTrainWheel(); 314 w2.position.set(-steamChamberRad+wheelThickness/2.1,0,0); 315 a1.add(w2); 316 317 const w3 = buildTrainWheel(); 318 w3.position.set(steamChamberRad-wheelThickness/2.1,0,0); 319 a2.add(w3); 320 321 const w4 = buildTrainWheel(); 322 w4.position.set(-steamChamberRad+wheelThickness/2.1,0,); 323 a2.add(w4); 324 325 const w5 = buildTrainWheel(); 326 w5.position.set(steamChamberRad-wheelThickness/2.1,0,0); 327 a3.add(w5); 328 329 const w6 = buildTrainWheel(); 330 w6.position.set(-steamChamberRad+wheelThickness/2.1,0,0); 331 a3.add(w6); 332 333 const crankGeometry = new THREE.BoxGeometry(crankWidth, 0.5, crankLen); 334 335 crankRight = new THREE.Mesh(crankGeometry, chassisMaterial); 336 //crankRight.position.set(steamChamberRad, wheelOffset, crankOffset); 337 338 crankLeft = new THREE.Mesh(crankGeometry, chassisMaterial); 339 //crankLeft.position.set(-steamChamberRad, wheelOffset, crankOffset); 340 341 chassis.add(crankLeft); 342 chassis.add(crankRight); 343 344 chassis.translateY(-wheelOffset); 345 346 const lightRad = 1.10; 347 const lightGeometry = new THREE.CylinderGeometry(lightRad, lightRad, 1, 32); 348 lightGeometry.rotateX(Math.PI/2); 349 350 const lightMaterial = new THREE.MeshPhongMaterial({ 351 color: 0x393939, 352 side: THREE.DoubleSide, 353 shininess: 100.0, 354 emissive: 0xf6d32d 355 }); 356 357 const light = new THREE.Mesh(lightGeometry, lightMaterial); 358 train.add(light) 359 light.position.set(0, 360 steamChamberRad+chassisOffset-lightRad/2-.30, 361 (steamChamberLen+steamChamberEndLen)/2-.3); 362 363 train.position.set(0, 2, 0); 364 return train; 365 } 366 367 function buildScene() { 368 console.log('Building scene'); 369 370 const train = buildTrain(); 371 scene.add(train); 372 } 373 374 function onTextureLoaded(key, texture) { 375 texture.wrapS = texture.wrapT = THREE.RepeatWrapping; 376 textures[key].object = texture; 377 console.log('Texture `' + key + '` loaded'); 378 } 379 380 function mainLoop() { 381 time += 0.05; 382 383 requestAnimationFrame(mainLoop); 384 385 crankLeft.position.set(-steamChamberRad-crankWidth/2, 386 wheelOffset + 0.70*(Math.sin(time*Math.PI/2)), 387 crankOffset - 0.70*(Math.cos(time*Math.PI/2))); 388 389 crankRight.position.set(steamChamberRad+crankWidth/2, 390 wheelOffset + 0.70*(Math.sin(time*Math.PI/2)), 391 crankOffset - 0.70*(Math.cos(time*Math.PI/2))); 392 393 renderer.render(scene, camera); 394 } 395 396 function main() { 397 buildScene(); 398 mainLoop(); 399 } 400 401 setupThreeJs(); 402 main();