Laplacian smoothing

Interactive example using Three.js and three-mesh-halfedge.

by mvgician

HTML

<script type="importmap">
	{
		"imports": {
			"three": "https://cdn.jsdelivr.net/npm/[email protected]/build/three.module.js",
      "three/addons/": "https://cdn.jsdelivr.net/npm/[email protected]/examples/jsm/",
      "three-mesh-halfedge": "https://cdn.jsdelivr.net/npm/[email protected]/build/index.esm.js"
      }
	}
</script>

CSS

body {
  padding: 0;
  margin: 0;
}

#wrapper {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  margin: auto;
  overflow: hidden;
}

.primary{
  color: #ddc000;
}

.secondary{
  color: #f78e20;
}

.tertiary{
  color: #e21;
}

JavaScript

import * as THREE from 'three'
import { HalfedgeDS, Vertex } from 'three-mesh-halfedge'
import { GUI } from 'three/addons/libs/lil-gui.module.min.js'
import { OrbitControls } from 'three/addons/controls/OrbitControls.js'
import { mergeVertices } from 'three/addons/utils/BufferGeometryUtils.js'
import { FBXLoader } from 'three/addons/loaders/FBXLoader.js'

// renderer
const width = window.innerWidth
const height = window.innerHeight
const renderer = new THREE.WebGLRenderer({ antialias: true })
renderer.setPixelRatio(3) // smoother (and thinner) lines
renderer.setSize(width, height)
document.body.appendChild(renderer.domElement)

// camera
const camera = new THREE.PerspectiveCamera(30, window.innerWidth / window.innerHeight, 0.1, 20)
camera.position.set(3, 2, 4)
const controls = new OrbitControls(camera, renderer.domElement)
controls.enablePan = false
controls.enableZoom = true
controls.maxDistance = 10
controls.minDistance = 1

// scene
const scene = new THREE.Scene()
scene.background = new THREE.Color(0xffffff)

// gui
const gui = new GUI() // create gui
const shapes = {
  Box: merge(new THREE.BoxGeometry(350, 350, 350, 5, 5, 5)),
  Cylinder: merge(new THREE.CylinderGeometry(200, 200, 350, 32, 8)),
  Bunny: await loadStanfordBunny()
}

// loader
function loadStanfordBunny () {
  return new Promise((resolve, reject) => {
    const loader = new FBXLoader()
    loader.load('https://threejs.org/examples/models/fbx/stanford-bunny.fbx', function (object) {
      const model = object.children[0]
      resolve(merge(model.geometry))
    },
    undefined,
    function (error) {
      reject(error)
    })
  })
}

const parameters = {
  shape: 'Box',
  smooth,
  reset
}

gui.add(parameters, 'shape', Object.keys(shapes)).onChange(initializeMesh)
gui.add(parameters, 'smooth')
gui.add(parameters, 'reset')

// utils
let mesh, wireframe
const struct = new HalfedgeDS()
const vector = new THREE.Vector3()
const material = new THREE.MeshNormalMaterial()
const wireframeMaterial = new...