function init() { class Point { constructor(x, y) { this.x = x; this.y = y; } add(that) { return new Point( this.x + that.x, this.y + that.y ); } sub(that) { return new Point( this.x - that.x, this.y - that.y ); } dist(that) { let a = this.x - that.x; let b = this.y - that.y; return Math.sqrt(a * a + b * b) } } function vectorLength(a, b) { let v1 = new THREE.Vector3(); v1.set(a.x, a.y, a.z); let v2 = new THREE.Vector3(); v2.set(b.x, b.y, b.z); return v1.sub(v2).length(); } class Face { a; b; c; d; springs = []; constructor(a, b, c, d) { this.a = a; this.b = b; this.c = c; this.d = d; } } class Spring { restLength; currentLength; index1; index2; constructor(vertices, index1, index2) { this.index1 = index1; this.index2 = index2; let length = vectorLength(vertices[index1], vertices[index2]); this.restLength = length; this.currentLength = length; } } class Cloth { VertexWeight = 1; geometry = new THREE.Geometry(); faces = []; vertexWeights = []; createBasic(width, height, numPointsWidth, numPointsHeight) { let vertices = []; let faces = []; let stepWidth = width / (numPointsWidth - 1); let stepHeight = height / (numPointsHeight - 1); for (let y = 0; y < numPointsHeight; y++) { for (let x = 0; x < numPointsWidth; x++) { vertices.push( new THREE.Vector3(x * stepWidth, height - y * stepHeight, 0) ); } } function getVertexIndex(x, y) { return y * numPointsWidth + x; } for (let y = 0; y < numPointsHeight - 1; y++) { for (let x = 0; x < numPointsWidth - 1; x++) { let newFace = new Face( getVertexIndex(x, y), getVertexIndex(x, y + 1), getVertexIndex(x + 1, y), getVertexIndex(x + 1, y + 1), ); newFace.springs.push(new Spring(vertices, getVertexIndex(x, y), getVertexIndex(x + 1, y))); newFace.springs.push(new Spring(vertices, getVertexIndex(x, y), getVertexIndex(x, y + 1))); newFace.springs.push(new Spring(vertices, getVertexIndex(x, y), getVertexIndex(x + 1, y + 1))); newFace.springs.push(new Spring(vertices, getVertexIndex(x + 1, y), getVertexIndex(x, y + 1))); newFace.springs.push(new Spring(vertices, getVertexIndex(x + 1, y), getVertexIndex(x + 1, y + 1))); newFace.springs.push(new Spring(vertices, getVertexIndex(x, y + 1), getVertexIndex(x + 1, y + 1))); faces.push(newFace); } } this.createExplicit(vertices, faces); } createExplicit(vertices, faces) { for (let i in vertices) { this.geometry.vertices.push(vertices[i]); this.vertexWeights.push(0); } for (let i in faces) { let face = faces[i]; this.faces.push(face); this.geometry.faces.push(new THREE.Face3( face.a, face.b, face.c )); this.geometry.faces.push(new THREE.Face3( face.c, face.b, face.d )); let xLength = vectorLength(this.geometry.vertices[face.b], this.geometry.vertices[face.a]); let yLength = vectorLength(this.geometry.vertices[face.c], this.geometry.vertices[face.a]); let weight = xLength * yLength / 2; xLength = vectorLength(this.geometry.vertices[face.b], this.geometry.vertices[face.d]); yLength = vectorLength(this.geometry.vertices[face.c], this.geometry.vertices[face.d]); weight += xLength * yLength / 2; this.vertexWeights[face.a] += weight / 4; this.vertexWeights[face.b] += weight / 4; this.vertexWeights[face.c] += weight / 4; this.vertexWeights[face.d] += weight / 4; } this.geometry.computeBoundingSphere(); } createDebugMesh(scene) { function addLine(from, to, color) { let geometry = new THREE.Geometry(); geometry.vertices.push(from); geometry.vertices.push(to); let material = new THREE.LineBasicMaterial( { color: color, linewidth: 10 } ); let line = new THREE.Line(geometry, material); line.renderOrder = 1; scene.add(line); } function addPoint(point, color) { const geometry = new THREE.SphereGeometry( 0.05, 32, 32 ); const material = new THREE.MeshBasicMaterial( { color: color } ); const sphere = new THREE.Mesh( geometry, material ); sphere.position.set(point.x, point.y, point.z); scene.add( sphere ); } let lineColor = 0x000000; let pointColor = 0xff00000; for (let i in this.faces) { let face = this.faces[i]; addLine(this.geometry.vertices[face.a], this.geometry.vertices[face.b], lineColor); addLine(this.geometry.vertices[face.a], this.geometry.vertices[face.c], lineColor); addLine(this.geometry.vertices[face.a], this.geometry.vertices[face.d], lineColor); addLine(this.geometry.vertices[face.b], this.geometry.vertices[face.c], lineColor); addLine(this.geometry.vertices[face.b], this.geometry.vertices[face.d], lineColor); addLine(this.geometry.vertices[face.c], this.geometry.vertices[face.d], lineColor); addPoint(this.geometry.vertices[face.a], pointColor); addPoint(this.geometry.vertices[face.b], pointColor); addPoint(this.geometry.vertices[face.c], pointColor); addPoint(this.geometry.vertices[face.d], pointColor); } } } let mousePos = new Point(); const canvasSpace = 200; const scene = new THREE.Scene(); const camera = new THREE.PerspectiveCamera(75, window.innerWidth / (window.innerHeight - canvasSpace), 0.1, 1000); const renderer = new THREE.WebGLRenderer(); renderer.setSize(window.innerWidth, window.innerHeight - canvasSpace); document.getElementById("threejscontainer").appendChild(renderer.domElement); const directionalLight = new THREE.DirectionalLight(0xffffff, 1); scene.add(directionalLight); let cloth = new Cloth(); cloth.createBasic(10, 10, 5, 5); cloth.createDebugMesh(scene); const material = new THREE.MeshBasicMaterial({ color: 0x0000ff }); const mesh = new THREE.Mesh(cloth.geometry, material); scene.add(mesh); camera.position.y = 5; camera.position.z = 10; //camera.lookAt(new THREE.Vector3(1, 0, 0)); function animate(dt) { requestAnimationFrame(animate); renderer.render(scene, camera); } let canvas = document.getElementsByTagName("canvas")[0]; let resize = function () { w = window.innerWidth; h = window.innerHeight - 200; canvas.width = w; canvas.height = h; } window.onresize = resize; resize(); if (canvas.getContext) { animate(performance.now()); } canvas.onmousemove = (evt) => { mousePos.x = evt.clientX; mousePos.y = evt.clientY; }; }