| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width,user-scalable=0"> |
| <title>Cloth | three.js exsamples</title> |
| <link rel="stylesheet" href="./common/css/sanitize.css"> |
| <style> |
| html, body { |
| padding: 0; |
| margin: 0; |
| font-family: Helvetica, Arial, sans-serif; |
| font-weight: 400; |
| font-size: 14px; |
| line-height: 1.8em; |
| color: #333; |
| background-color: #fff; |
| } |
| </style> |
| </head> |
| <body> |
| <div id="js_box"></div> |
| <script src="three.js"></script> |
| <script> |
| (function(modules) { |
| |
| var installedModules = {}; |
| |
| |
| function __webpack_require__(moduleId) { |
| |
| |
| if(installedModules[moduleId]) |
| return installedModules[moduleId].exports; |
| |
| |
| var module = installedModules[moduleId] = { |
| exports: {}, |
| id: moduleId, |
| loaded: false |
| }; |
| |
| |
| modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); |
| |
| |
| module.loaded = true; |
| |
| |
| return module.exports; |
| } |
| |
| |
| |
| __webpack_require__.m = modules; |
| |
| |
| __webpack_require__.c = installedModules; |
| |
| |
| __webpack_require__.p = ""; |
| |
| |
| return __webpack_require__(0); |
| }) |
| |
| ([ |
| |
| function(module, exports, __webpack_require__) { |
| |
| module.exports = __webpack_require__(2); |
| |
| |
| }, |
| |
| function(module, exports) { |
| |
| "use strict"; |
| |
| Object.defineProperty(exports, "__esModule", { |
| value: true |
| }); |
| |
| var _createClass = function () { |
| function defineProperties(target, props) { |
| for (var i = 0; i < props.length; i++) { |
| var descriptor = props[i];descriptor.enumerable = descriptor.enumerable || false;descriptor.configurable = true;if ("value" in descriptor) descriptor.writable = true;Object.defineProperty(target, descriptor.key, descriptor); |
| } |
| }return function (Constructor, protoProps, staticProps) { |
| if (protoProps) defineProperties(Constructor.prototype, protoProps);if (staticProps) defineProperties(Constructor, staticProps);return Constructor; |
| }; |
| }(); |
| |
| function _classCallCheck(instance, Constructor) { |
| if (!(instance instanceof Constructor)) { |
| throw new TypeError("Cannot call a class as a function"); |
| } |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| var Cloth = function () { |
| function Cloth(segmentX, segmentY, distance, paramFunc) { |
| _classCallCheck(this, Cloth); |
| |
| this.segmentX = segmentX; |
| this.segmentY = segmentY; |
| this.distance = distance; |
| this.paramFunc = paramFunc; |
| this.geometry = new THREE.ParametricGeometry(this.paramFunc, this.segmentX, this.segmentY); |
| this.windForce = new THREE.Vector3(0, 0, 0); |
| this.tmpForce = new THREE.Vector3(); |
| this.lastTime = null; |
| this.particles = []; |
| this.constrains = []; |
| var u, v; |
| for (v = 0; v <= this.segmentY; v++) { |
| for (u = 0; u <= this.segmentX; u++) { |
| this.particles.push(new ClothParticle(u / this.segmentX, v / this.segmentY, 0, 0.1, this.paramFunc)); |
| } |
| } |
| for (v = 0; v < this.segmentY; v++) { |
| for (u = 0; u < this.segmentX; u++) { |
| this.constrains.push([this.particles[this.getIndex_(u, v)], this.particles[this.getIndex_(u, v + 1)], this.distance]); |
| this.constrains.push([this.particles[this.getIndex_(u, v)], this.particles[this.getIndex_(u + 1, v)], this.distance]); |
| } |
| } |
| for (u = this.segmentX, v = 0; v < this.segmentY; v++) { |
| this.constrains.push([this.particles[this.getIndex_(u, v)], this.particles[this.getIndex_(u, v + 1)], this.distance]); |
| } |
| for (v = this.segmentY, u = 0; u < this.segmentX; u++) { |
| this.constrains.push([this.particles[this.getIndex_(u, v)], this.particles[this.getIndex_(u + 1, v)], this.distance]); |
| } |
| } |
| |
| _createClass(Cloth, [{ |
| key: "getGeometry", |
| value: function getGeometry() { |
| return this.geometry; |
| } |
| }, { |
| key: "windSimulate", |
| value: function windSimulate(time) { |
| if (!this.lastTime) { |
| this.lastTime = time; |
| return; |
| } |
| this.windForce.set(10, 0, 5).normalize().multiplyScalar(300); |
| var i = 0, |
| max; |
| for (i = 0, max = this.particles.length; i < max; i = i + 1) { |
| this.geometry.vertices[i].copy(this.particles[i].position); |
| } |
| this.geometry.computeFaceNormals(); |
| this.geometry.computeVertexNormals(); |
| this.geometry.normalsNeedUpdate = true; |
| this.geometry.verticesNeedUpdate = true; |
| var faces = this.geometry.faces; |
| for (i = 0, max = faces.length; i < max; i = i + 1) { |
| this.tmpForce.copy(faces[i].normal).normalize().multiplyScalar(faces[i].normal.dot(this.windForce)); |
| this.particles[faces[i].a].addForce(this.tmpForce); |
| this.particles[faces[i].b].addForce(this.tmpForce); |
| this.particles[faces[i].c].addForce(this.tmpForce); |
| } |
| for (i = 0, max = this.particles.length; i < max; i = i + 1) { |
| this.particles[i].addForce(new THREE.Vector3(0, -(981 * 1.4), 0).multiplyScalar(0.01)); |
| this.particles[i].integrate(18 / 1000 * (18 / 1000)); |
| } |
| for (i = 0, max = this.constrains.length; i < max; i = i + 1) { |
| this.satisifyConstrains_(this.constrains[i][0], this.constrains[i][1], this.constrains[i][2]); |
| } |
| for (i = 0, max = this.particles.length; i < max; i = i + 1) { |
| if (i % (this.segmentX + 1) == 0) { |
| this.particles[i].position.copy(this.particles[i].original); |
| this.particles[i].previous.copy(this.particles[i].original); |
| } |
| } |
| } |
| }, { |
| key: "getIndex_", |
| value: function getIndex_(u, v) { |
| return u + v * (this.segmentX + 1); |
| } |
| }, { |
| key: "satisifyConstrains_", |
| value: function satisifyConstrains_(p1, p2, distance) { |
| var diff = new THREE.Vector3(); |
| diff.subVectors(p2.position, p1.position); |
| var currentDist = diff.length(); |
| if (currentDist == 0) return; |
| var correction = diff.multiplyScalar(1 - distance / currentDist); |
| var correctionHalf = correction.multiplyScalar(0.5); |
| p1.position.add(correctionHalf); |
| p2.position.sub(correctionHalf); |
| } |
| }]); |
| |
| return Cloth; |
| }(); |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| exports.default = Cloth; |
| |
| var ClothParticle = exports.ClothParticle = function () { |
| function ClothParticle(x, y, z, mass, paramFunc) { |
| _classCallCheck(this, ClothParticle); |
| |
| this.position = paramFunc(x, y); |
| this.previous = paramFunc(x, y); |
| this.original = paramFunc(x, y); |
| this.mass = 1 / mass; |
| this.vector = new THREE.Vector3(0, 0, 0); |
| this.tmp = new THREE.Vector3(); |
| this.tmp2 = new THREE.Vector3(); |
| } |
| |
| _createClass(ClothParticle, [{ |
| key: "addForce", |
| value: function addForce(force) { |
| this.vector.add(this.tmp2.copy(force).multiplyScalar(this.mass)); |
| } |
| }, { |
| key: "integrate", |
| value: function integrate(timesq) { |
| var newPos = this.tmp.subVectors(this.position, this.previous); |
| newPos.multiplyScalar(0.95).add(this.position); |
| newPos.add(this.vector.multiplyScalar(timesq)); |
| this.tmp = this.previous; |
| this.previous = this.position; |
| this.position = newPos; |
| this.vector.set(0, 0, 0); |
| } |
| }]); |
| |
| return ClothParticle; |
| }(); |
| |
| }, |
| |
| function(module, exports, __webpack_require__) { |
| |
| "use strict"; |
| |
| var _Cloth = __webpack_require__(1); |
| |
| var _Cloth2 = _interopRequireDefault(_Cloth); |
| |
| function _interopRequireDefault(obj) { |
| return obj && obj.__esModule ? obj : { default: obj }; |
| } |
| |
| var DISTANCE = 25; |
| var SEGMENTS_X = 10; |
| var SEGMENTS_Y = 10; |
| var WIDTH = DISTANCE * SEGMENTS_X; |
| var HEIGHT = DISTANCE * SEGMENTS_Y; |
| |
| |
| var renderer = new THREE.WebGLRenderer({ antialias: true }); |
| renderer.setSize(800, 600); |
| renderer.setClearColor(0x000000); |
| document.getElementById("js_box").appendChild(renderer.domElement); |
| |
| |
| var scene = new THREE.Scene(); |
| scene.fog = new THREE.Fog(0x000000, 500, 5000); |
| |
| |
| var camera = new THREE.PerspectiveCamera(30, 800 / 600, 1, 5000); |
| camera.position.z = 1500; |
| scene.add(camera); |
| |
| |
| var light = new THREE.DirectionalLight(0xffffff, 2); |
| light.position.set(50, 200, 100); |
| light.castShadow = false; |
| scene.add(light); |
| scene.add(new THREE.AmbientLight(0x333333)); |
| |
| |
| var loader = new THREE.TextureLoader(); |
| var clothTexture = loader.load("cloth.png"); |
| clothTexture.wrapS = THREE.RepeatWrapping; |
| clothTexture.wrapT = THREE.RepeatWrapping; |
| clothTexture.anisotropy = 16; |
| var clothMaterial = new THREE.MeshPhongMaterial({ specular: 0x000000, map: clothTexture, side: THREE.DoubleSide }); |
| var cloth = new _Cloth2.default(SEGMENTS_X, SEGMENTS_Y, DISTANCE, function (u, v) { |
| var x = (u - 0.5) * DISTANCE * SEGMENTS_X; |
| var y = (v + 0.5) * DISTANCE * SEGMENTS_Y; |
| var z = 0; |
| return new THREE.Vector3(x, y, z); |
| }); |
| var clothMeth = new THREE.Mesh(cloth.getGeometry(), clothMaterial); |
| clothMeth.position.set(WIDTH / 2, -HEIGHT / 2, 0); |
| scene.add(clothMeth); |
| |
| |
| var poleGeometry = new THREE.CylinderGeometry(5, 5, 500, 10, 0, true); |
| var poleMaterial = new THREE.MeshPhongMaterial({ color: 0x999999, specular: 0xffffff }); |
| var poleMesh = new THREE.Mesh(poleGeometry, poleMaterial); |
| poleMesh.position.x = 0; |
| poleMesh.position.y = 0; |
| scene.add(poleMesh); |
| |
| |
| function animate() { |
| requestAnimationFrame(animate); |
| cloth.windSimulate(Date.now()); |
| camera.lookAt(scene.position); |
| renderer.render(scene, camera); |
| } |
| animate(); |
| |
| } |
| ]);</script> |
| </body> |
| </html> |