Examples : CSG with a GLTF
Description
Demonstrates CSG Operations on a glTF.
GSGMesh GitHub Repository : https://github.com/Sean-Bradley/THREE-CSGMesh
This examples demonstrates,
- Traversing a gTLF and extracting just the required geometry.
- CSG Subtract, Union and Intersect Operations on a geometry from a glTF model.
- Translating a CSG Geometry along an axis before applying the operation.
- Using the THREE.GridHelper.
Code
./src/server/server.ts
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | import express from "express" import path from "path" import http from "http" const port: number = 3000 class App { private server: http.Server private port: number constructor(port: number) { this.port = port const app = express() app.use(express.static(path.join(__dirname, '../client'))) app.use('/build/three.module.js', express.static(path.join(__dirname, '../../node_modules/three/build/three.module.js'))) app.use('/jsm/controls/OrbitControls', express.static(path.join(__dirname, '../../node_modules/three/examples/jsm/controls/OrbitControls.js'))) app.use('/jsm/loaders/GLTFLoader', express.static(path.join(__dirname, '../../node_modules/three/examples/jsm/loaders/GLTFLoader.js'))) app.use('/jsm/libs/stats.module', express.static(path.join(__dirname, '../../node_modules/three/examples/jsm/libs/stats.module.js'))) this.server = new http.Server(app); } public Start() { this.server.listen(this.port, () => { console.log( `Server listening on port ${this.port}.` ) }) } } new App(port).Start() |
./src/client/client.ts
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 | import * as THREE from '/build/three.module.js' import { OrbitControls } from '/jsm/controls/OrbitControls' import Stats from '/jsm/libs/stats.module' import { GLTFLoader } from '/jsm/loaders/GLTFLoader' import CSG from './utils/CSGMesh.js' const scene: THREE.Scene = new THREE.Scene() const gridHelper = new THREE.GridHelper(10, 10, 0xaec6cf, 0xaec6cf); scene.add(gridHelper); const light1 = new THREE.PointLight(); light1.position.set(5, 10, 5) scene.add(light1); const camera: THREE.PerspectiveCamera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000) camera.position.x = 0 camera.position.y = 3 camera.position.z = 3 const renderer: THREE.WebGLRenderer = new THREE.WebGLRenderer() renderer.setSize(window.innerWidth, window.innerHeight) document.body.appendChild(renderer.domElement) const controls = new OrbitControls(camera, renderer.domElement) controls.enableDamping = true controls.target.set(0, 1.5, 0) const envTexture = new THREE.CubeTextureLoader().load(["img/px_25.jpg", "img/nx_25.jpg", "img/py_25.jpg", "img/ny_25.jpg", "img/pz_25.jpg", "img/nz_25.jpg"]); envTexture.mapping = THREE.CubeReflectionMapping; const material = new THREE.MeshPhysicalMaterial({ color: 0xb2ffc8, envMap: envTexture, metalness: .5, roughness: .1, transparent: true, opacity: .5, //transmission: .9, side: THREE.DoubleSide, flatShading: true }); const cubeMesh = new THREE.Mesh(new THREE.BoxGeometry(1, 2, 2), new THREE.MeshPhongMaterial({ color: 0xff0000 })); cubeMesh.position.set(-2, 1.5, -3) scene.add(cubeMesh) let modelsReady = false let cubeMonkeyMeshIntersect: THREE.Mesh let cubeMonkeyMeshSubtract: THREE.Mesh let cubeMonkeyMeshUnion: THREE.Mesh const loader = new GLTFLoader() loader.load( 'models/monkey.glb', function (gltf) { gltf.scene.traverse(function (child) { if ((child as THREE.Mesh).isMesh) { if ((child as THREE.Mesh).name === "Suzanne") { const mesh = new THREE.Mesh((child as THREE.Mesh).geometry.clone(), new THREE.MeshPhongMaterial({ color: 0x00ff00 })); mesh.position.set(2, 1.5, -3) mesh.geometry.scale(1.15, 1.15, 1.15) scene.add(mesh)//adding only the monkey mesh to the scene and ignoring everything else inside the gltf const cubeCSG = CSG.fromGeometry(cubeMesh.geometry.clone().translate(-.5, 0, 0)); const monkeyMeshCSG = CSG.fromMesh(mesh); const cubeMonkeyMeshIntersectCSG = cubeCSG.intersect(monkeyMeshCSG.clone()); cubeMonkeyMeshIntersect = CSG.toMesh(cubeMonkeyMeshIntersectCSG, new THREE.Matrix4()); cubeMonkeyMeshIntersect.material = material cubeMonkeyMeshIntersect.position.set(-3, 1.5, 0) scene.add(cubeMonkeyMeshIntersect) const cubeMonkeyMeshSubtractCSG = cubeCSG.subtract(monkeyMeshCSG.clone()); cubeMonkeyMeshSubtract = CSG.toMesh(cubeMonkeyMeshSubtractCSG, new THREE.Matrix4()); cubeMonkeyMeshSubtract.material = material cubeMonkeyMeshSubtract.position.set(0, 1.5, 0) scene.add(cubeMonkeyMeshSubtract) const cubeMonkeyMeshUnionCSG = cubeCSG.union(monkeyMeshCSG.clone()); cubeMonkeyMeshUnion = CSG.toMesh(cubeMonkeyMeshUnionCSG, new THREE.Matrix4()); cubeMonkeyMeshUnion.material = material cubeMonkeyMeshUnion.position.set(3, 1.5, 0) scene.add(cubeMonkeyMeshUnion) modelsReady = true } } }) }, (xhr) => { console.log((xhr.loaded / xhr.total * 100) + '% loaded') }, (error) => { console.log(error); } ); window.addEventListener('resize', onWindowResize, false) function onWindowResize() { camera.aspect = window.innerWidth / window.innerHeight camera.updateProjectionMatrix() renderer.setSize(window.innerWidth, window.innerHeight) } const stats = Stats() document.body.appendChild(stats.dom) var animate = function () { requestAnimationFrame(animate) controls.update() if (modelsReady) { cubeMonkeyMeshIntersect.rotation.y += .005 cubeMonkeyMeshSubtract.rotation.y += .005 cubeMonkeyMeshUnion.rotation.y += .005 } render() stats.update() }; function render() { renderer.render(scene, camera) } animate(); |