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