]> gitweb.ps.run Git - cloth_sim/blob - Scripts/main.js
comments
[cloth_sim] / Scripts / main.js
1 \r
2 \r
3 function addLights(scene){\r
4   scene.add(new THREE.AmbientLight(0x222222));\r
5 \r
6   const light1 = new THREE.PointLight(0xffffff, 1, 50);\r
7   light1.position.set(15, 1, 40);\r
8   scene.add(light1);\r
9 \r
10   const light2 = new THREE.PointLight(0xffffff, 1, 50);\r
11   light2.position.set(-15, 0, 40);\r
12   scene.add(light2);\r
13 \r
14   const light3 = new THREE.PointLight(0xffffff, 1, 50);\r
15   light3.position.set(0, -1, 40);\r
16   scene.add(light3);\r
17 }\r
18 \r
19 /**\r
20  * setup THREE JS Scene, Camera and Renderer\r
21  */\r
22 function setup_scene(canvasSpace) {\r
23   const scene = new THREE.Scene();\r
24   const camera = new THREE.PerspectiveCamera(75, window.innerWidth / (window.innerHeight - canvasSpace), 0.1, 1000);\r
25   const renderer = new THREE.WebGLRenderer();\r
26   /** size canvas to leave some space for UI */\r
27   renderer.setSize(window.innerWidth, window.innerHeight - canvasSpace);\r
28   renderer.antialias = true;\r
29   /** embed canvas in HTML */\r
30   document.getElementById("threejscontainer").appendChild(renderer.domElement);\r
31 \r
32   /** add orbit controls */\r
33   const controls = new THREE.OrbitControls(camera, renderer.domElement);\r
34   controls.target.set(0, 0, 0);\r
35   controls.update();\r
36 \r
37   /** add scene background */\r
38   const loader = new THREE.TextureLoader();\r
39   const texture = loader.load(\r
40     'Textures/tears_of_steel_bridge_2k.jpg',\r
41     () => {\r
42       const rt = new THREE.WebGLCubeRenderTarget(texture.image.height);\r
43       rt.fromEquirectangularTexture(renderer, texture);\r
44       scene.background = rt;\r
45     });\r
46 \r
47   /** add flag pole */\r
48   const geometry = new THREE.CylinderGeometry(0.02, 0.02, 5, 32);\r
49   const material = new THREE.MeshStandardMaterial({color: 0xffffff});\r
50   const cylinder = new THREE.Mesh(geometry, material);\r
51   cylinder.position.set(-0.5, -2.25, 0);\r
52   scene.add(cylinder);\r
53 \r
54   /** add global light */\r
55   const directionalLight = new THREE.DirectionalLight(0xffffff, 1);\r
56   scene.add(directionalLight);\r
57 \r
58   /** position camera */\r
59   camera.position.z = 2;\r
60   addLights(scene);\r
61   return [scene, camera, renderer];\r
62 }\r
63 \r
64 /** call "init" when document is fully loaded */\r
65 document.body.onload = init;\r
66 \r
67 function init() {\r
68   let mousePos = new THREE.Vector2();\r
69   \r
70   /**\r
71    * Space left empty under canvas\r
72    * for UI elements\r
73    */\r
74   const canvasSpace = 200;\r
75 \r
76   /** Constant Frame Time */\r
77   const frameTime = 1000.0 / 200.0;\r
78 \r
79   /** Setup scene */\r
80   let [scene, camera, renderer] = setup_scene(canvasSpace);\r
81   \r
82   const loader = new THREE.TextureLoader();\r
83   //Red color: 0xC70039\r
84 \r
85   /** Create cloth and generate mesh */\r
86   const cloth = new Cloth(1, 0.5, 20, 20);\r
87   const clothGeometry = cloth.generateGeometry();\r
88   const clothMaterial = new THREE.MeshStandardMaterial({ map: loader.load('Textures/hsrm2.png'), color: 0xffffff, side: THREE.DoubleSide, flatShading: false });\r
89   //const clothMaterial = new THREE.MeshStandardMaterial({ color: 0xC70039, side: THREE.DoubleSide, flatShading: false });\r
90   const clothMesh = new THREE.Mesh(clothGeometry, clothMaterial);\r
91   scene.add(clothMesh);\r
92   \r
93   /** Register wind checkbox */\r
94   document.getElementById("windToggle").checked = options.wind;\r
95   document.getElementById("windToggle").onchange = (e) => {\r
96     options.wind = e.target.checked;\r
97   };\r
98 \r
99   let raycaster = new THREE.Raycaster();\r
100   let intersects;\r
101   let windKeyDown = false;\r
102   let dragKeyDown = false;\r
103   let draggedIndex = -1;\r
104   /**\r
105    * function called every frame\r
106    * @param {number} dt - time passed since last frame in ms\r
107    */\r
108   function animate(dt) {\r
109     /** run simulation step */\r
110     cloth.simulate(dt / 1000);\r
111 \r
112     cloth.updateGeometry(clothGeometry);\r
113 \r
114     /** raycast from camera to cursor */\r
115     raycaster.setFromCamera(new THREE.Vector2((mousePos.x / w) * 2 - 1, ((h - mousePos.y) / h) * 2 - 1), camera);\r
116     intersects = raycaster.intersectObject(clothMesh);\r
117 \r
118     /** provide user interaction */\r
119     if (intersects.length > 0) {\r
120       if (windKeyDown)\r
121         cloth.blow(camera.position, intersects);\r
122       /** "grab" vertex index */\r
123       if (dragKeyDown && draggedIndex == -1)\r
124         draggedIndex = intersects[0].face.a;\r
125     }\r
126     if (dragKeyDown && draggedIndex != -1)\r
127       cloth.drag(calculateMousePosToWorld(mousePos), draggedIndex);\r
128 \r
129     /** queue next frame */\r
130     setTimeout(() => {\r
131       animate(frameTime);\r
132     }, frameTime);\r
133 \r
134     renderer.render(scene, camera);\r
135   }\r
136 \r
137 \r
138   /** add callback for window resize */\r
139   let canvas = document.getElementsByTagName("canvas")[0];\r
140   let w = window.innerWidth;\r
141   let h = window.innerHeight - canvasSpace;\r
142   let resize = function () {\r
143     w = window.innerWidth;\r
144     h = window.innerHeight - canvasSpace;\r
145     canvas.width = w;\r
146     canvas.height = h;\r
147   }\r
148   window.onresize = resize;\r
149   resize();\r
150 \r
151   /**\r
152    * if canvas has been successfully initialized\r
153    * start rendering\r
154    */\r
155   if (canvas.getContext) {\r
156     animate(frameTime);\r
157   }\r
158 \r
159   \r
160 \r
161   /** add mouse move callback */\r
162   canvas.onmousemove = (evt) => {\r
163     mousePos.x = evt.clientX;\r
164     mousePos.y = evt.clientY;\r
165   };\r
166 \r
167   /**\r
168    * Prevent context menu while blowing wind\r
169    */\r
170   canvas.addEventListener('contextmenu', function(evt) { \r
171     evt.preventDefault();\r
172   }, false);\r
173 \r
174   /** register key events */\r
175   document.onkeydown = (evt) => {\r
176     if (evt.code === "KeyW")\r
177       windKeyDown = true;\r
178     if (evt.code === "KeyD")\r
179       dragKeyDown = true;\r
180   };\r
181 \r
182   document.onkeyup = (evt) => {\r
183     if (evt.code === "KeyW")\r
184       windKeyDown = false;\r
185     if (evt.code === "KeyD") {\r
186       dragKeyDown = false;\r
187       draggedIndex = -1;\r
188     }\r
189   };\r
190 \r
191   /** helper function to turn mouse position into 3D coordinates */\r
192   function calculateMousePosToWorld(mousePos){\r
193     var vec = new THREE.Vector3(); // create once and reuse\r
194     var pos = new THREE.Vector3(); // create once and reuse\r
195 \r
196     vec.set(\r
197       (mousePos.x / window.innerWidth) * 2 - 1,\r
198     - (mousePos.y / window.innerHeight) * 2 + 1,\r
199       0.5);\r
200 \r
201     vec.unproject(camera);\r
202 \r
203     vec.sub(camera.position).normalize();\r
204 \r
205     var distance = - camera.position.z / vec.z;\r
206 \r
207     pos.copy(camera.position).add(vec.multiplyScalar(distance));\r
208     return pos;\r
209   }\r
210 }