Spaces:
Sleeping
Sleeping
Pim Schreurs commited on
Commit ·
8194362
1
Parent(s): 33be52b
Modernize the whole codebase; improve rendering
Browse filesPreviously it was hacked together and using an out-of-date three.js
version. Besides a cleaner codebase this commit also includes a better
bloom (based on the UnrealBloom which can be found in three.js's
examples) and fixes some rendering issues on some mobile devices.
This view is limited to 50 files because it contains too many changes. See raw diff
- .babelrc +6 -0
- .editorconfig +9 -0
- .eslintignore +1 -0
- .eslintrc.js +16 -0
- .gitignore +2 -0
- accretion_disk.png → assets/accretion_disk.png +0 -0
- galaxy1.png → assets/galaxy1.png +0 -0
- galaxy2.png → assets/galaxy2.png +0 -0
- saturn.jpg → assets/saturn.jpg +0 -0
- saturnrings.png → assets/saturnrings.png +0 -0
- index.html +10 -373
- js/Detector.js +0 -68
- js/Player.js +0 -153
- js/Simulation.js +0 -345
- js/controls/DeviceOrientationControls.js +0 -88
- js/controls/KeyboardControls.js +0 -324
- js/controls/MobileDeviceControls.js +0 -89
- js/main.js +0 -13
- js/postprocessing/BloomPass.js +0 -115
- js/postprocessing/ConvolutionShader.js +0 -101
- js/postprocessing/CopyShader.js +0 -46
- js/postprocessing/EffectComposer.js +0 -135
- js/postprocessing/MaskPass.js +0 -86
- js/postprocessing/RenderPass.js +0 -51
- js/postprocessing/ShaderPass.js +0 -58
- js/three.min.js +0 -0
- package-lock.json +0 -0
- package.json +50 -0
- src/Player.js +130 -0
- src/Simulation.js +192 -0
- src/SimulationRenderer.js +231 -0
- src/Teleporter.js +55 -0
- src/Ui.js +68 -0
- src/controls/ControlsBase.js +10 -0
- src/controls/ControlsManager.js +52 -0
- src/controls/DeviceOrientationControls.js +64 -0
- src/controls/KeyboardControls.js +333 -0
- src/controls/MobileDeviceControls.js +37 -0
- src/controls/TouchControls.js +80 -0
- src/main.js +11 -0
- src/postprocessing/CopyShader.js +14 -0
- src/postprocessing/EffectComposer.js +66 -0
- src/postprocessing/LuminosityHighPassShader.js +21 -0
- src/postprocessing/Pass.js +30 -0
- src/postprocessing/RenderPass.js +23 -0
- src/postprocessing/UnrealBloomPass.js +298 -0
- src/shaders/bloomComposite.glsl +24 -0
- src/shaders/copy.glsl +9 -0
- src/shaders/luminosityHighPass.glsl +20 -0
- src/shaders/seperableBlur.glsl +26 -0
.babelrc
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"presets": [
|
| 3 |
+
["env", { "modules": false }],
|
| 4 |
+
"stage-3"
|
| 5 |
+
]
|
| 6 |
+
}
|
.editorconfig
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
root = true
|
| 2 |
+
|
| 3 |
+
[*]
|
| 4 |
+
charset = utf-8
|
| 5 |
+
indent_style = space
|
| 6 |
+
indent_size = 2
|
| 7 |
+
end_of_line = lf
|
| 8 |
+
insert_final_newline = true
|
| 9 |
+
trim_trailing_whitespace = true
|
.eslintignore
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
/dist/
|
.eslintrc.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
module.exports = {
|
| 2 |
+
root: true,
|
| 3 |
+
parserOptions: {
|
| 4 |
+
parser: 'babel-eslint'
|
| 5 |
+
},
|
| 6 |
+
env: {
|
| 7 |
+
browser: true
|
| 8 |
+
},
|
| 9 |
+
extends: [
|
| 10 |
+
'standard'
|
| 11 |
+
],
|
| 12 |
+
rules: {
|
| 13 |
+
'brace-style': ['error', 'stroustrup'],
|
| 14 |
+
'padded-blocks': ['error', { classes: 'always' }]
|
| 15 |
+
}
|
| 16 |
+
}
|
.gitignore
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
node_modules
|
| 2 |
+
dist
|
accretion_disk.png → assets/accretion_disk.png
RENAMED
|
File without changes
|
galaxy1.png → assets/galaxy1.png
RENAMED
|
File without changes
|
galaxy2.png → assets/galaxy2.png
RENAMED
|
File without changes
|
saturn.jpg → assets/saturn.jpg
RENAMED
|
File without changes
|
saturnrings.png → assets/saturnrings.png
RENAMED
|
File without changes
|
index.html
CHANGED
|
@@ -3,7 +3,7 @@
|
|
| 3 |
<head>
|
| 4 |
<title>Interstellar - Interactive wormhole & black hole</title>
|
| 5 |
<meta charset="utf-8">
|
| 6 |
-
<meta name="viewport" content="width=device-width
|
| 7 |
<style>
|
| 8 |
body {
|
| 9 |
background:#000;
|
|
@@ -19,13 +19,14 @@
|
|
| 19 |
}
|
| 20 |
|
| 21 |
#webgl-error {
|
| 22 |
-
display:
|
| 23 |
text-align: center;
|
| 24 |
position: fixed;
|
| 25 |
left: 50%;
|
| 26 |
top: 50%;
|
| 27 |
-webkit-transform: translate(-50%, -50%);
|
| 28 |
transform: translate(-50%, -50%);
|
|
|
|
| 29 |
}
|
| 30 |
|
| 31 |
#webgl-error a {
|
|
@@ -145,6 +146,11 @@
|
|
| 145 |
|
| 146 |
<body>
|
| 147 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 148 |
<div id="info">
|
| 149 |
<h3 class="no-pointer-events">Controls:</h3>
|
| 150 |
<div id="keyboard-controls" class="no-pointer-events">
|
|
@@ -172,7 +178,7 @@
|
|
| 172 |
</div>
|
| 173 |
|
| 174 |
<div id="version">
|
| 175 |
-
<a target="_blank" href="https://github.com/sirxemic/Interstellar">version
|
| 176 |
</div>
|
| 177 |
|
| 178 |
<div class="ui-toggle">
|
|
@@ -182,376 +188,7 @@
|
|
| 182 |
<div id="container"></div>
|
| 183 |
<div id="loading">Loading...</div>
|
| 184 |
|
| 185 |
-
<script
|
| 186 |
-
#define ID_SATURN 0
|
| 187 |
-
#define ID_SATURN_RING 1
|
| 188 |
-
#define ID_GALAXY1 2
|
| 189 |
-
#define ID_GALAXY2 3
|
| 190 |
-
#define ID_BLACKHOLE 4
|
| 191 |
-
#define ID_BLACKHOLE_DISK 5
|
| 192 |
-
#define ID_PLANET 6
|
| 193 |
-
|
| 194 |
-
varying vec4 rayDir;
|
| 195 |
-
|
| 196 |
-
uniform vec3 lightDirection;
|
| 197 |
-
|
| 198 |
-
uniform vec3 planetDiffuse;
|
| 199 |
-
uniform vec3 planetSpecular;
|
| 200 |
-
|
| 201 |
-
uniform vec4 wormhole;
|
| 202 |
-
uniform float wormholeGravityRatio;
|
| 203 |
-
uniform vec4 blackhole;
|
| 204 |
-
|
| 205 |
-
uniform vec4 saturn;
|
| 206 |
-
uniform vec4 planet;
|
| 207 |
-
|
| 208 |
-
uniform vec4 blackholeDisk;
|
| 209 |
-
uniform vec4 saturnRings;
|
| 210 |
-
|
| 211 |
-
uniform sampler2D texSaturn;
|
| 212 |
-
uniform sampler2D texSaturnRings;
|
| 213 |
-
uniform sampler2D texGalaxy1;
|
| 214 |
-
uniform sampler2D texGalaxy2;
|
| 215 |
-
uniform sampler2D texAccretionDisk;
|
| 216 |
-
|
| 217 |
-
uniform float worldSize;
|
| 218 |
-
uniform int startGalaxy;
|
| 219 |
-
|
| 220 |
-
const float lightSpeed = 0.2;
|
| 221 |
-
|
| 222 |
-
const float INFINITY = 1000000.0;
|
| 223 |
-
const float GALAXY_EDGE = 10000.0;
|
| 224 |
-
|
| 225 |
-
const float EPSILON = 0.0001;
|
| 226 |
-
const float r2 = 0.0625;
|
| 227 |
-
|
| 228 |
-
const float PI = 3.14159265359;
|
| 229 |
-
const float TWOPI = 6.28318530718;
|
| 230 |
-
|
| 231 |
-
float gravityWormhole = wormhole.w * lightSpeed * lightSpeed;
|
| 232 |
-
float gravityBlackhole = blackhole.w * lightSpeed * lightSpeed;
|
| 233 |
-
|
| 234 |
-
vec3 saturnColor(vec3 pos)
|
| 235 |
-
{
|
| 236 |
-
vec2 uv = vec2(
|
| 237 |
-
0.5 + atan(pos.z, pos.x) / TWOPI,
|
| 238 |
-
0.5 - asin(pos.y) / PI
|
| 239 |
-
);
|
| 240 |
-
return texture2D(texSaturn, uv).rgb;
|
| 241 |
-
}
|
| 242 |
-
|
| 243 |
-
vec3 panoramaColor(float n, vec3 pos)
|
| 244 |
-
{
|
| 245 |
-
vec2 uv = vec2(
|
| 246 |
-
0.5 - atan(pos.z, pos.x) / TWOPI,
|
| 247 |
-
0.5 - asin(pos.y) / PI
|
| 248 |
-
);
|
| 249 |
-
if (n < 0.5) return texture2D(texGalaxy1, uv).rgb;
|
| 250 |
-
else return texture2D(texGalaxy2, uv).rgb;
|
| 251 |
-
}
|
| 252 |
-
|
| 253 |
-
vec3 accretionDiskColor(vec3 pos)
|
| 254 |
-
{
|
| 255 |
-
pos = pos - blackhole.xyz;
|
| 256 |
-
float dist = length(pos);
|
| 257 |
-
|
| 258 |
-
float r1 = length(blackholeDisk.xyz);
|
| 259 |
-
float r2 = blackholeDisk.w;
|
| 260 |
-
|
| 261 |
-
// Important! Scale radii according to black hole
|
| 262 |
-
float v = clamp((dist - r1) / (r2 - r1), 0.0, 1.0);
|
| 263 |
-
|
| 264 |
-
vec3 base = cross(blackholeDisk.xyz, vec3(0.0, 0.0, 1.0));
|
| 265 |
-
float angle = acos(dot(normalize(base), normalize(pos)));
|
| 266 |
-
if (dot(cross(base, pos), blackholeDisk.xyz) < 0.0) angle = -angle;
|
| 267 |
-
|
| 268 |
-
float u = 0.5 - angle / TWOPI;
|
| 269 |
-
return texture2D(texAccretionDisk, vec2(u, v)).rgb;
|
| 270 |
-
}
|
| 271 |
-
|
| 272 |
-
float sphereDistance(vec3 rayPosition, vec3 rayDirection, vec4 sphere)
|
| 273 |
-
{
|
| 274 |
-
vec3 v;
|
| 275 |
-
float p, d;
|
| 276 |
-
v = rayPosition - sphere.xyz;
|
| 277 |
-
p = dot(rayDirection, v);
|
| 278 |
-
d = p * p + sphere.w * sphere.w - dot(v, v);
|
| 279 |
-
|
| 280 |
-
return d < 0.0 ? -1.0 : -p - sqrt(d);
|
| 281 |
-
}
|
| 282 |
-
|
| 283 |
-
vec4 saturnRingColor(vec3 pos)
|
| 284 |
-
{
|
| 285 |
-
pos = pos - saturn.xyz;
|
| 286 |
-
|
| 287 |
-
float r1 = length(saturnRings.xyz);
|
| 288 |
-
float r2 = saturnRings.w;
|
| 289 |
-
|
| 290 |
-
// Important! Scale radii according to saturn
|
| 291 |
-
float v = clamp((length(pos) - r1) / (r2 - r1), 0.0, 1.0);
|
| 292 |
-
|
| 293 |
-
vec4 color = texture2D(texSaturnRings, vec2(0.5, v));
|
| 294 |
-
|
| 295 |
-
float objectDistance = sphereDistance(saturn.xyz + pos, lightDirection, saturn);
|
| 296 |
-
if (objectDistance > 0.0)
|
| 297 |
-
{
|
| 298 |
-
color.rgb *= 0.01;
|
| 299 |
-
}
|
| 300 |
-
color.rgb *= color.a;
|
| 301 |
-
|
| 302 |
-
return vec4(color.rgb, -color.a);
|
| 303 |
-
}
|
| 304 |
-
|
| 305 |
-
float ringDistance(vec3 rayPosition, vec3 rayDirection, vec3 center, vec4 definition)
|
| 306 |
-
{
|
| 307 |
-
float r1 = length(definition.xyz);
|
| 308 |
-
float r2 = definition.w;
|
| 309 |
-
vec3 normal = definition.xyz / r1;
|
| 310 |
-
|
| 311 |
-
float denominator = dot(rayDirection, normal);
|
| 312 |
-
float constant = -dot(center, normal);
|
| 313 |
-
float distanceToCenter;
|
| 314 |
-
if (abs(denominator) < EPSILON)
|
| 315 |
-
{
|
| 316 |
-
return -1.0;
|
| 317 |
-
}
|
| 318 |
-
else
|
| 319 |
-
{
|
| 320 |
-
float t = -(dot(rayPosition, normal) + constant) / denominator;
|
| 321 |
-
if (t < 0.0) return -1.0;
|
| 322 |
-
|
| 323 |
-
vec3 intersection = rayPosition + t * rayDirection;
|
| 324 |
-
distanceToCenter = length(intersection - center);
|
| 325 |
-
if (distanceToCenter >= r1 && distanceToCenter <= r2)
|
| 326 |
-
{
|
| 327 |
-
return t;
|
| 328 |
-
}
|
| 329 |
-
return -1.0;
|
| 330 |
-
}
|
| 331 |
-
}
|
| 332 |
-
|
| 333 |
-
vec3 computeShading(vec3 light, vec3 view, vec3 normal, vec3 diffuse, vec3 specular, vec3 ambient)
|
| 334 |
-
{
|
| 335 |
-
float lambertian = max(dot(light, normal), 0.0);
|
| 336 |
-
vec3 reflectDir = reflect(-light, normal);
|
| 337 |
-
float specAngle = max(dot(reflectDir, view), 0.0);
|
| 338 |
-
float specularAmount = pow(specAngle, 4.0);
|
| 339 |
-
return ambient + lambertian * diffuse + specularAmount * specular;
|
| 340 |
-
}
|
| 341 |
-
|
| 342 |
-
void testDistance(int i, float distance, inout float currentDistance, inout int currentObject)
|
| 343 |
-
{
|
| 344 |
-
if (distance >= EPSILON && distance < currentDistance)
|
| 345 |
-
{
|
| 346 |
-
currentDistance = distance;
|
| 347 |
-
currentObject = i;
|
| 348 |
-
}
|
| 349 |
-
}
|
| 350 |
-
|
| 351 |
-
vec3 raytrace(vec3 rayPosition, vec3 rayDirection)
|
| 352 |
-
{
|
| 353 |
-
float currentDistance = INFINITY;
|
| 354 |
-
int currentObject = -1, prevObject = -1;
|
| 355 |
-
float currentGalaxy = float(startGalaxy);
|
| 356 |
-
vec3 currentPosition;
|
| 357 |
-
vec3 normal;
|
| 358 |
-
|
| 359 |
-
float stepSize, rayDistance;
|
| 360 |
-
vec3 gravityVector, rayAccel;
|
| 361 |
-
float objectDistance;
|
| 362 |
-
|
| 363 |
-
vec4 color = vec4(0.0, 0.0, 0.0, 1.0);
|
| 364 |
-
|
| 365 |
-
for (int i = 0; i < 100; i++)
|
| 366 |
-
{
|
| 367 |
-
currentDistance = INFINITY;
|
| 368 |
-
|
| 369 |
-
// Bend the light towards the wormhole
|
| 370 |
-
gravityVector = wormhole.xyz - rayPosition;
|
| 371 |
-
rayDistance = length(gravityVector);
|
| 372 |
-
|
| 373 |
-
// 0.86: rate of smaller steps when approaching wormhole
|
| 374 |
-
stepSize = rayDistance - wormhole.w * 0.86;
|
| 375 |
-
|
| 376 |
-
rayDistance -= wormhole.w * (1.0 - wormholeGravityRatio);
|
| 377 |
-
|
| 378 |
-
float amount = wormholeGravityRatio / rayDistance;
|
| 379 |
-
rayAccel = normalize(gravityVector) * gravityWormhole * amount * amount;
|
| 380 |
-
|
| 381 |
-
if (currentGalaxy > 0.5)
|
| 382 |
-
{
|
| 383 |
-
// Bend the light towards the black hole
|
| 384 |
-
gravityVector = blackhole.xyz - rayPosition;
|
| 385 |
-
rayDistance = length(gravityVector);
|
| 386 |
-
|
| 387 |
-
// 0.05: rate of smaller steps when approaching blackhole
|
| 388 |
-
stepSize = min(stepSize, rayDistance - blackhole.w * 0.05);
|
| 389 |
-
|
| 390 |
-
// rayAccel += normalize(gravityVector) * gravityBlackhole / (rayDistance * rayDistance)
|
| 391 |
-
rayAccel += gravityVector * gravityBlackhole / (rayDistance * rayDistance * rayDistance);
|
| 392 |
-
}
|
| 393 |
-
|
| 394 |
-
if (length(rayAccel) > lightSpeed)
|
| 395 |
-
{
|
| 396 |
-
rayAccel = normalize(rayAccel) * lightSpeed;
|
| 397 |
-
}
|
| 398 |
-
|
| 399 |
-
rayDirection = normalize(rayDirection * lightSpeed + rayAccel * stepSize);
|
| 400 |
-
|
| 401 |
-
if (stepSize <= 0.005)
|
| 402 |
-
{
|
| 403 |
-
currentObject = -1;
|
| 404 |
-
break;
|
| 405 |
-
}
|
| 406 |
-
|
| 407 |
-
if (currentGalaxy < 0.5)
|
| 408 |
-
{
|
| 409 |
-
objectDistance = sphereDistance(rayPosition, rayDirection, saturn);
|
| 410 |
-
testDistance(ID_SATURN, objectDistance, currentDistance, currentObject);
|
| 411 |
-
|
| 412 |
-
objectDistance = ringDistance(rayPosition, rayDirection, saturn.xyz, saturnRings);
|
| 413 |
-
testDistance(ID_SATURN_RING, objectDistance, currentDistance, currentObject);
|
| 414 |
-
|
| 415 |
-
testDistance(ID_GALAXY1, GALAXY_EDGE, currentDistance, currentObject);
|
| 416 |
-
}
|
| 417 |
-
else
|
| 418 |
-
{
|
| 419 |
-
objectDistance = sphereDistance(rayPosition, rayDirection, planet);
|
| 420 |
-
testDistance(ID_PLANET, objectDistance, currentDistance, currentObject);
|
| 421 |
-
|
| 422 |
-
// Test against a bit smaller sphere due to precision errors
|
| 423 |
-
objectDistance = sphereDistance(rayPosition, rayDirection, vec4(blackhole.xyz, blackhole.w * 0.93));
|
| 424 |
-
testDistance(ID_BLACKHOLE, objectDistance, currentDistance, currentObject);
|
| 425 |
-
|
| 426 |
-
objectDistance = ringDistance(rayPosition, rayDirection, blackhole.xyz, blackholeDisk);
|
| 427 |
-
testDistance(ID_BLACKHOLE_DISK, objectDistance, currentDistance, currentObject);
|
| 428 |
-
|
| 429 |
-
testDistance(ID_GALAXY2, GALAXY_EDGE, currentDistance, currentObject);
|
| 430 |
-
}
|
| 431 |
-
|
| 432 |
-
rayDistance = lightSpeed * stepSize;
|
| 433 |
-
|
| 434 |
-
// Check if we hit any object, and if so, stop integrating
|
| 435 |
-
if (currentObject != -1 && currentDistance <= rayDistance)
|
| 436 |
-
{
|
| 437 |
-
// But if it's something transparent, get its color, and continue
|
| 438 |
-
if (currentObject == ID_BLACKHOLE_DISK)
|
| 439 |
-
{
|
| 440 |
-
currentPosition = rayPosition + rayDirection * currentDistance;
|
| 441 |
-
color.rgb += accretionDiskColor(currentPosition).rgb * color.a;
|
| 442 |
-
currentObject = -1;
|
| 443 |
-
prevObject = ID_BLACKHOLE_DISK;
|
| 444 |
-
}
|
| 445 |
-
else if (currentObject == ID_SATURN_RING)
|
| 446 |
-
{
|
| 447 |
-
currentPosition = rayPosition + rayDirection * currentDistance;
|
| 448 |
-
if (prevObject != ID_SATURN_RING)
|
| 449 |
-
{
|
| 450 |
-
color += saturnRingColor(currentPosition);
|
| 451 |
-
}
|
| 452 |
-
currentObject = -1;
|
| 453 |
-
prevObject = ID_SATURN_RING;
|
| 454 |
-
|
| 455 |
-
// Ensure we don't overstep and go through Saturn
|
| 456 |
-
rayDistance = min(rayDistance, 0.9 * (length(saturnRings.xyz) - saturn.w));
|
| 457 |
-
}
|
| 458 |
-
else
|
| 459 |
-
{
|
| 460 |
-
break;
|
| 461 |
-
}
|
| 462 |
-
}
|
| 463 |
-
|
| 464 |
-
float d = sphereDistance(rayPosition, rayDirection, wormhole);
|
| 465 |
-
if (d > 0.0 && d < rayDistance)
|
| 466 |
-
{ // Ray goes through wormhole
|
| 467 |
-
currentGalaxy = 1.0 - currentGalaxy;
|
| 468 |
-
vec3 intersection = rayPosition + rayDirection * d;
|
| 469 |
-
gravityVector = normalize(intersection - wormhole.xyz);
|
| 470 |
-
|
| 471 |
-
rayPosition = 2.0 * wormhole.xyz - intersection;
|
| 472 |
-
rayDirection = -reflect(rayDirection, gravityVector);
|
| 473 |
-
|
| 474 |
-
rayPosition += rayDirection * d;
|
| 475 |
-
}
|
| 476 |
-
else
|
| 477 |
-
{
|
| 478 |
-
rayPosition += rayDirection * rayDistance;
|
| 479 |
-
}
|
| 480 |
-
}
|
| 481 |
-
|
| 482 |
-
currentPosition = rayPosition + rayDirection * currentDistance;
|
| 483 |
-
|
| 484 |
-
if (currentObject == ID_GALAXY1 || currentObject == ID_GALAXY2)
|
| 485 |
-
{
|
| 486 |
-
color.rgb += panoramaColor(currentGalaxy, rayDirection) * color.a;
|
| 487 |
-
}
|
| 488 |
-
else if (currentObject == ID_SATURN)
|
| 489 |
-
{
|
| 490 |
-
normal = (currentPosition - saturn.xyz) / saturn.w;
|
| 491 |
-
|
| 492 |
-
vec3 diffuse = saturnColor(normal);
|
| 493 |
-
vec3 specular = vec3(0.0);
|
| 494 |
-
vec3 ambient = diffuse * 0.02;
|
| 495 |
-
|
| 496 |
-
float objectDistance = ringDistance(currentPosition, lightDirection, saturn.xyz, saturnRings);
|
| 497 |
-
if (objectDistance > 0.0)
|
| 498 |
-
{
|
| 499 |
-
diffuse *= 1.0 + saturnRingColor(currentPosition + lightDirection * objectDistance).a;
|
| 500 |
-
}
|
| 501 |
-
|
| 502 |
-
color.rgb += computeShading(lightDirection, -rayDirection, normal, diffuse, specular, ambient) * color.a;
|
| 503 |
-
}
|
| 504 |
-
else if (currentObject == ID_PLANET)
|
| 505 |
-
{
|
| 506 |
-
normal = (currentPosition - planet.xyz) / planet.w;
|
| 507 |
-
|
| 508 |
-
// light direction for black hole-orbiting planet is towards the blackhole
|
| 509 |
-
vec3 lightDirection2 = normalize(blackhole.xyz - planet.xyz);
|
| 510 |
-
|
| 511 |
-
vec3 diffuse = planetDiffuse;
|
| 512 |
-
vec3 specular = planetSpecular;
|
| 513 |
-
vec3 ambient = vec3(0.0);
|
| 514 |
-
color.rgb += computeShading(lightDirection2, -rayDirection, normal, diffuse, specular, ambient) * color.a;
|
| 515 |
-
}
|
| 516 |
-
|
| 517 |
-
return color.rgb;
|
| 518 |
-
}
|
| 519 |
-
|
| 520 |
-
void main()
|
| 521 |
-
{
|
| 522 |
-
gl_FragColor = vec4(raytrace(cameraPosition, normalize(rayDir.xyz)), 1.0);
|
| 523 |
-
}
|
| 524 |
-
</script>
|
| 525 |
-
<script type="x-shader/x-vertex" id="vertexShader">
|
| 526 |
-
uniform mat4 rayMatrix;
|
| 527 |
-
|
| 528 |
-
varying vec4 rayDir;
|
| 529 |
-
|
| 530 |
-
void main() {
|
| 531 |
-
rayDir = rayMatrix * vec4(position.xy, 1.0, 0.0);
|
| 532 |
-
|
| 533 |
-
gl_Position = vec4(position.xy, 0.0, 1.0);
|
| 534 |
-
}
|
| 535 |
-
</script>
|
| 536 |
-
|
| 537 |
-
<script src="js/three.min.js"></script>
|
| 538 |
-
|
| 539 |
-
<script src="js/postprocessing/ConvolutionShader.js"></script>
|
| 540 |
-
<script src="js/postprocessing/CopyShader.js"></script>
|
| 541 |
-
<script src="js/postprocessing/EffectComposer.js"></script>
|
| 542 |
-
<script src="js/postprocessing/ShaderPass.js"></script>
|
| 543 |
-
<script src="js/postprocessing/MaskPass.js"></script>
|
| 544 |
-
<script src="js/postprocessing/RenderPass.js"></script>
|
| 545 |
-
<script src="js/postprocessing/BloomPass.js"></script>
|
| 546 |
-
|
| 547 |
-
<script src="js/controls/DeviceOrientationControls.js"></script>
|
| 548 |
-
<script src="js/controls/KeyboardControls.js"></script>
|
| 549 |
-
<script src="js/controls/MobileDeviceControls.js"></script>
|
| 550 |
-
|
| 551 |
-
<script src="js/Detector.js"></script>
|
| 552 |
-
<script src="js/Player.js"></script>
|
| 553 |
-
<script src="js/Simulation.js"></script>
|
| 554 |
-
<script src="js/main.js"></script>
|
| 555 |
|
| 556 |
</body>
|
| 557 |
</html>
|
|
|
|
| 3 |
<head>
|
| 4 |
<title>Interstellar - Interactive wormhole & black hole</title>
|
| 5 |
<meta charset="utf-8">
|
| 6 |
+
<meta name="viewport" content="width=device-width">
|
| 7 |
<style>
|
| 8 |
body {
|
| 9 |
background:#000;
|
|
|
|
| 19 |
}
|
| 20 |
|
| 21 |
#webgl-error {
|
| 22 |
+
display: none;
|
| 23 |
text-align: center;
|
| 24 |
position: fixed;
|
| 25 |
left: 50%;
|
| 26 |
top: 50%;
|
| 27 |
-webkit-transform: translate(-50%, -50%);
|
| 28 |
transform: translate(-50%, -50%);
|
| 29 |
+
z-index: 1;
|
| 30 |
}
|
| 31 |
|
| 32 |
#webgl-error a {
|
|
|
|
| 146 |
|
| 147 |
<body>
|
| 148 |
|
| 149 |
+
<div id="webgl-error">
|
| 150 |
+
Your graphics card does not seem to support <a href="http://khronos.org/webgl/wiki/Getting_a_WebGL_Implementation">WebGL</a><br>
|
| 151 |
+
Find out how to get it <a href="http://get.webgl.org/">here</a>
|
| 152 |
+
</div>
|
| 153 |
+
|
| 154 |
<div id="info">
|
| 155 |
<h3 class="no-pointer-events">Controls:</h3>
|
| 156 |
<div id="keyboard-controls" class="no-pointer-events">
|
|
|
|
| 178 |
</div>
|
| 179 |
|
| 180 |
<div id="version">
|
| 181 |
+
<a target="_blank" href="https://github.com/sirxemic/Interstellar">version 2.0.0</a>
|
| 182 |
</div>
|
| 183 |
|
| 184 |
<div class="ui-toggle">
|
|
|
|
| 188 |
<div id="container"></div>
|
| 189 |
<div id="loading">Loading...</div>
|
| 190 |
|
| 191 |
+
<script src="dist/build.js"></script>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 192 |
|
| 193 |
</body>
|
| 194 |
</html>
|
js/Detector.js
DELETED
|
@@ -1,68 +0,0 @@
|
|
| 1 |
-
/**
|
| 2 |
-
* @author alteredq / http://alteredqualia.com/
|
| 3 |
-
* @author mr.doob / http://mrdoob.com/
|
| 4 |
-
*/
|
| 5 |
-
|
| 6 |
-
var Detector = {
|
| 7 |
-
|
| 8 |
-
canvas: !! window.CanvasRenderingContext2D,
|
| 9 |
-
webgl: ( function () { try { var canvas = document.createElement( 'canvas' ); return !! ( window.WebGLRenderingContext && ( canvas.getContext( 'webgl' ) || canvas.getContext( 'experimental-webgl' ) ) ); } catch( e ) { return false; } } )(),
|
| 10 |
-
workers: !! window.Worker,
|
| 11 |
-
fileapi: window.File && window.FileReader && window.FileList && window.Blob,
|
| 12 |
-
|
| 13 |
-
getWebGLErrorMessage: function () {
|
| 14 |
-
|
| 15 |
-
var element = document.createElement( 'div' );
|
| 16 |
-
element.id = 'webgl-error-message';
|
| 17 |
-
/*
|
| 18 |
-
element.style.fontFamily = 'monospace';
|
| 19 |
-
element.style.fontSize = '13px';
|
| 20 |
-
element.style.fontWeight = 'normal';
|
| 21 |
-
element.style.textAlign = 'center';
|
| 22 |
-
element.style.background = '#fff';
|
| 23 |
-
element.style.color = '#000';
|
| 24 |
-
element.style.padding = '1.5em';
|
| 25 |
-
element.style.width = '400px';
|
| 26 |
-
element.style.margin = '5em auto 0';
|
| 27 |
-
*/
|
| 28 |
-
|
| 29 |
-
if ( ! this.webgl ) {
|
| 30 |
-
|
| 31 |
-
element.innerHTML = window.WebGLRenderingContext ? [
|
| 32 |
-
'Your graphics card does not seem to support <a href="http://khronos.org/webgl/wiki/Getting_a_WebGL_Implementation">WebGL</a>.<br />',
|
| 33 |
-
'Find out how to get it <a href="http://get.webgl.org/">here</a>.'
|
| 34 |
-
].join( '\n' ) : [
|
| 35 |
-
'Your browser does not seem to support <a href="http://khronos.org/webgl/wiki/Getting_a_WebGL_Implementation">WebGL</a>.<br/>',
|
| 36 |
-
'Find out how to get it <a href="http://get.webgl.org/">here</a>.'
|
| 37 |
-
].join( '\n' );
|
| 38 |
-
|
| 39 |
-
}
|
| 40 |
-
|
| 41 |
-
return element;
|
| 42 |
-
|
| 43 |
-
},
|
| 44 |
-
|
| 45 |
-
addGetWebGLMessage: function ( parameters ) {
|
| 46 |
-
|
| 47 |
-
var parent, id, element;
|
| 48 |
-
|
| 49 |
-
parameters = parameters || {};
|
| 50 |
-
|
| 51 |
-
parent = parameters.parent !== undefined ? parameters.parent : document.body;
|
| 52 |
-
id = parameters.id !== undefined ? parameters.id : 'oldie';
|
| 53 |
-
|
| 54 |
-
element = Detector.getWebGLErrorMessage();
|
| 55 |
-
element.id = id;
|
| 56 |
-
|
| 57 |
-
parent.appendChild( element );
|
| 58 |
-
|
| 59 |
-
}
|
| 60 |
-
|
| 61 |
-
};
|
| 62 |
-
|
| 63 |
-
// browserify support
|
| 64 |
-
if ( typeof module === 'object' ) {
|
| 65 |
-
|
| 66 |
-
module.exports = Detector;
|
| 67 |
-
|
| 68 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
js/Player.js
DELETED
|
@@ -1,153 +0,0 @@
|
|
| 1 |
-
function Player()
|
| 2 |
-
{
|
| 3 |
-
this.object = new THREE.Object3D;
|
| 4 |
-
this.eyes = new THREE.OrthographicCamera(-1, 1, -1, 1, 0, 1);
|
| 5 |
-
|
| 6 |
-
this.velocity = new THREE.Vector3(0, 0, 0);
|
| 7 |
-
this.eyeAngularVelocity = new THREE.Vector3;
|
| 8 |
-
|
| 9 |
-
this.object.add(this.eyes);
|
| 10 |
-
|
| 11 |
-
this.galaxy = 0;
|
| 12 |
-
|
| 13 |
-
this.controls = [];
|
| 14 |
-
|
| 15 |
-
this.teleportTargets = [];
|
| 16 |
-
}
|
| 17 |
-
|
| 18 |
-
Player.prototype = {
|
| 19 |
-
|
| 20 |
-
addTeleportTarget: function(target) {
|
| 21 |
-
this.teleportTargets.push(target)
|
| 22 |
-
},
|
| 23 |
-
|
| 24 |
-
lookAt: function(position)
|
| 25 |
-
{
|
| 26 |
-
// this.object.lookAt makes it look in the exact opposite direction, for some reason
|
| 27 |
-
var lookAtMatrix = new THREE.Matrix4();
|
| 28 |
-
lookAtMatrix.lookAt(this.object.position, position, this.object.up);
|
| 29 |
-
this.object.quaternion.setFromRotationMatrix(lookAtMatrix);
|
| 30 |
-
this.object.quaternion.multiply(this.eyes.quaternion.clone().inverse());
|
| 31 |
-
},
|
| 32 |
-
|
| 33 |
-
handleInput: function()
|
| 34 |
-
{
|
| 35 |
-
for (var i = 0; i < this.controls.length; i++)
|
| 36 |
-
{
|
| 37 |
-
this.controls[i].update();
|
| 38 |
-
}
|
| 39 |
-
},
|
| 40 |
-
|
| 41 |
-
step: (function() {
|
| 42 |
-
|
| 43 |
-
var prevPosition = new THREE.Vector3(),
|
| 44 |
-
newVelocity = new THREE.Vector3(),
|
| 45 |
-
acceleration = new THREE.Vector3(),
|
| 46 |
-
gravityVector = new THREE.Vector3(),
|
| 47 |
-
direction = new THREE.Vector3(),
|
| 48 |
-
intersection = new THREE.Vector3(),
|
| 49 |
-
axis = new THREE.Vector3(),
|
| 50 |
-
rotation = new THREE.Quaternion(),
|
| 51 |
-
temp = new THREE.Vector3(),
|
| 52 |
-
ray = new THREE.Ray();
|
| 53 |
-
|
| 54 |
-
return function(delta) {
|
| 55 |
-
var wormholePosition = Simulation.wormholePositionSize,
|
| 56 |
-
wormholeSize = Simulation.wormholePositionSize.w,
|
| 57 |
-
wormholeGravityRatio = Simulation.wormholeGravityRatio,
|
| 58 |
-
wormholeSphere = new THREE.Sphere(wormholePosition, wormholeSize);
|
| 59 |
-
|
| 60 |
-
if (this.velocity.lengthSq() > 0.00001)
|
| 61 |
-
{
|
| 62 |
-
prevPosition.copy(this.object.position);
|
| 63 |
-
|
| 64 |
-
// 1. Compute wormhole curvature/gravity.
|
| 65 |
-
gravityVector.subVectors(wormholePosition, prevPosition);
|
| 66 |
-
var rayDistance = gravityVector.length() - wormholeSize * (1 - wormholeGravityRatio);
|
| 67 |
-
var amount = wormholeGravityRatio / rayDistance;
|
| 68 |
-
acceleration.copy(gravityVector.normalize()).multiplyScalar(wormholeSize * this.velocity.lengthSq() * amount * amount);
|
| 69 |
-
|
| 70 |
-
// Apply curvature to velocity
|
| 71 |
-
newVelocity.copy(this.velocity).add(acceleration.multiplyScalar(delta));
|
| 72 |
-
|
| 73 |
-
// Adjust new velocity (keep magnitude of old velocity)
|
| 74 |
-
newVelocity.normalize().multiplyScalar(this.velocity.length());
|
| 75 |
-
|
| 76 |
-
// Update the player accordingly
|
| 77 |
-
this.object.position.addVectors(prevPosition, newVelocity.multiplyScalar(delta));
|
| 78 |
-
|
| 79 |
-
// ...and orientation
|
| 80 |
-
rotation.setFromUnitVectors(this.velocity.normalize(), newVelocity.normalize());
|
| 81 |
-
this.object.quaternion.multiplyQuaternions(rotation, this.object.quaternion);
|
| 82 |
-
|
| 83 |
-
this.velocity.copy(newVelocity);
|
| 84 |
-
|
| 85 |
-
// 2. Check if we're going through the wormhole
|
| 86 |
-
direction.copy(this.velocity).normalize();
|
| 87 |
-
|
| 88 |
-
ray.set(prevPosition, direction);
|
| 89 |
-
|
| 90 |
-
var distanceTravelledSq = direction.subVectors(this.object.position, prevPosition).lengthSq();
|
| 91 |
-
|
| 92 |
-
var at = ray.intersectSphere(wormholeSphere, intersection);
|
| 93 |
-
if (at && at.distanceToSquared(prevPosition) <= distanceTravelledSq)
|
| 94 |
-
{
|
| 95 |
-
// Rotate 180 degrees around axis pointing at exit point
|
| 96 |
-
axis.subVectors(intersection, wormholePosition).normalize();
|
| 97 |
-
rotation.setFromAxisAngle(axis, Math.PI);
|
| 98 |
-
this.object.quaternion.multiplyQuaternions(rotation, this.object.quaternion);
|
| 99 |
-
this.velocity.reflect(axis).multiplyScalar(-1);
|
| 100 |
-
|
| 101 |
-
// Set new position a tiny bit outside mirrored intersection point
|
| 102 |
-
this.object.position.copy(wormholePosition).add(temp.subVectors(wormholePosition, intersection).multiplyScalar(1.0001));
|
| 103 |
-
|
| 104 |
-
this.galaxy = 1 - this.galaxy;
|
| 105 |
-
}
|
| 106 |
-
}
|
| 107 |
-
|
| 108 |
-
rotation.set( this.eyeAngularVelocity.x * delta, this.eyeAngularVelocity.y * delta, this.eyeAngularVelocity.z * delta, 1 ).normalize();
|
| 109 |
-
this.eyes.quaternion.multiply( rotation );
|
| 110 |
-
};
|
| 111 |
-
})(),
|
| 112 |
-
|
| 113 |
-
update: function(delta)
|
| 114 |
-
{
|
| 115 |
-
this.handleInput();
|
| 116 |
-
this.step(delta);
|
| 117 |
-
|
| 118 |
-
// Object isn't actually part of a rendered scene, so we need to call this manually
|
| 119 |
-
this.object.updateMatrixWorld(true);
|
| 120 |
-
},
|
| 121 |
-
|
| 122 |
-
getClosestTeleportIndex: function() {
|
| 123 |
-
var minDistance = Infinity;
|
| 124 |
-
var result = null;
|
| 125 |
-
for (var i = 0; i < this.teleportTargets.length; i++) {
|
| 126 |
-
var distance;
|
| 127 |
-
|
| 128 |
-
if (this.teleportTargets[i].galaxy != this.galaxy) {
|
| 129 |
-
distance =
|
| 130 |
-
this.object.position.distanceTo(Simulation.wormholePositionSize) +
|
| 131 |
-
this.teleportTargets[i].position.distanceTo(Simulation.wormholePositionSize);
|
| 132 |
-
} else {
|
| 133 |
-
distance = this.object.position.distanceTo(this.teleportTargets[i].position);
|
| 134 |
-
}
|
| 135 |
-
|
| 136 |
-
if (distance < minDistance) {
|
| 137 |
-
minDistance = distance;
|
| 138 |
-
result = i;
|
| 139 |
-
}
|
| 140 |
-
}
|
| 141 |
-
return result;
|
| 142 |
-
},
|
| 143 |
-
|
| 144 |
-
teleport: function() {
|
| 145 |
-
var nextIndex = (this.getClosestTeleportIndex() + 1) % this.teleportTargets.length;
|
| 146 |
-
var teleportTarget = this.teleportTargets[nextIndex];
|
| 147 |
-
|
| 148 |
-
this.object.position.copy(teleportTarget.position);
|
| 149 |
-
this.lookAt(teleportTarget.lookAt);
|
| 150 |
-
this.galaxy = teleportTarget.galaxy;
|
| 151 |
-
}
|
| 152 |
-
|
| 153 |
-
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
js/Simulation.js
DELETED
|
@@ -1,345 +0,0 @@
|
|
| 1 |
-
var Simulation = {
|
| 2 |
-
init: function init()
|
| 3 |
-
{
|
| 4 |
-
this.initScene();
|
| 5 |
-
this.initGL();
|
| 6 |
-
this.initDom();
|
| 7 |
-
this.initPlayer();
|
| 8 |
-
},
|
| 9 |
-
|
| 10 |
-
initGL: function()
|
| 11 |
-
{
|
| 12 |
-
var self = this;
|
| 13 |
-
|
| 14 |
-
// Init THREE.js stuff
|
| 15 |
-
this.renderer = new THREE.WebGLRenderer();
|
| 16 |
-
this.renderer.setSize(window.innerWidth, window.innerHeight);
|
| 17 |
-
this.renderer.sortObjects = false;
|
| 18 |
-
|
| 19 |
-
this.renderer.autoClear = false;
|
| 20 |
-
|
| 21 |
-
this.quadScene = new THREE.Scene();
|
| 22 |
-
this.quadCam = new THREE.OrthographicCamera(-1, 1, 1, -1, 0, 1);
|
| 23 |
-
|
| 24 |
-
this.quadScene.add(new THREE.Mesh(new THREE.PlaneBufferGeometry(2, 2), new THREE.ShaderMaterial({
|
| 25 |
-
uniforms: this.uniforms,
|
| 26 |
-
|
| 27 |
-
vertexShader: document.getElementById("vertexShader").textContent,
|
| 28 |
-
fragmentShader: document.getElementById("fragmentShader").textContent,
|
| 29 |
-
})));
|
| 30 |
-
|
| 31 |
-
this.renderPasses = [
|
| 32 |
-
new THREE.RenderPass(this.quadScene, this.quadCam),
|
| 33 |
-
new THREE.BloomPass(1.25),
|
| 34 |
-
new THREE.ShaderPass(THREE.CopyShader)
|
| 35 |
-
];
|
| 36 |
-
|
| 37 |
-
this.renderPasses[this.renderPasses.length - 1].renderToScreen = true;
|
| 38 |
-
|
| 39 |
-
this.composer = new THREE.EffectComposer(this.renderer);
|
| 40 |
-
|
| 41 |
-
this.renderPasses.forEach(function(pass) {
|
| 42 |
-
self.composer.addPass(pass);
|
| 43 |
-
});
|
| 44 |
-
|
| 45 |
-
this.rayMatrix = new THREE.Matrix4();
|
| 46 |
-
},
|
| 47 |
-
|
| 48 |
-
initDom: function()
|
| 49 |
-
{
|
| 50 |
-
var self = this;
|
| 51 |
-
|
| 52 |
-
// Init some DOM stuff
|
| 53 |
-
this.container = document.getElementById("container");
|
| 54 |
-
this.container.appendChild(this.renderer.domElement);
|
| 55 |
-
|
| 56 |
-
var uiToggle = document.querySelector(".ui-toggle input");
|
| 57 |
-
|
| 58 |
-
var onUIToggle = function() {
|
| 59 |
-
if (uiToggle.checked) {
|
| 60 |
-
document.body.classList.add("no-ui");
|
| 61 |
-
}
|
| 62 |
-
else {
|
| 63 |
-
document.body.classList.remove("no-ui");
|
| 64 |
-
}
|
| 65 |
-
uiToggle.blur();
|
| 66 |
-
};
|
| 67 |
-
|
| 68 |
-
uiToggle.addEventListener("change", onUIToggle);
|
| 69 |
-
|
| 70 |
-
onUIToggle();
|
| 71 |
-
|
| 72 |
-
var updateResolution = function()
|
| 73 |
-
{
|
| 74 |
-
var size = parseInt(document.querySelector("[name=resolution]:checked").value),
|
| 75 |
-
width = Math.floor(window.innerWidth / size),
|
| 76 |
-
height = Math.floor(window.innerHeight / size);
|
| 77 |
-
|
| 78 |
-
self.composer.setSize(width, height);
|
| 79 |
-
};
|
| 80 |
-
|
| 81 |
-
this.zoom = 1;
|
| 82 |
-
|
| 83 |
-
var onWindowResize = function()
|
| 84 |
-
{
|
| 85 |
-
self.renderer.setSize(window.innerWidth, window.innerHeight);
|
| 86 |
-
|
| 87 |
-
self.updateView();
|
| 88 |
-
|
| 89 |
-
updateResolution();
|
| 90 |
-
};
|
| 91 |
-
|
| 92 |
-
var onWheel = function(e) {
|
| 93 |
-
e.preventDefault();
|
| 94 |
-
|
| 95 |
-
var delta = e.delta || (e.deltaX + e.deltaY + e.deltaZ);
|
| 96 |
-
if (delta < 0)
|
| 97 |
-
{
|
| 98 |
-
self.zoom *= 1.06;
|
| 99 |
-
}
|
| 100 |
-
else
|
| 101 |
-
{
|
| 102 |
-
self.zoom /= 1.06;
|
| 103 |
-
}
|
| 104 |
-
|
| 105 |
-
self.updateView();
|
| 106 |
-
};
|
| 107 |
-
|
| 108 |
-
window.addEventListener("resize", onWindowResize, false);
|
| 109 |
-
onWindowResize();
|
| 110 |
-
|
| 111 |
-
document.querySelector("#resolution").addEventListener("change", function(event) {
|
| 112 |
-
updateResolution();
|
| 113 |
-
event.target.blur();
|
| 114 |
-
}, false);
|
| 115 |
-
|
| 116 |
-
window.addEventListener("wheel", onWheel, false);
|
| 117 |
-
},
|
| 118 |
-
|
| 119 |
-
initScene: function()
|
| 120 |
-
{
|
| 121 |
-
this.wormholePositionSize = new THREE.Vector4(10, 0.0, -32, 0.8);
|
| 122 |
-
this.blackholePositionSize = new THREE.Vector4(0.0, -250.0, 250.0, 12.5);
|
| 123 |
-
this.saturnPositionSize = new THREE.Vector4(-14, 5, -40, 8.0);
|
| 124 |
-
this.planetPositionSize = new THREE.Vector4(7.6, 62, -50, 0.08);
|
| 125 |
-
|
| 126 |
-
this.planetPositionSize.x += this.blackholePositionSize.x;
|
| 127 |
-
this.planetPositionSize.y += this.blackholePositionSize.y;
|
| 128 |
-
this.planetPositionSize.z += this.blackholePositionSize.z;
|
| 129 |
-
|
| 130 |
-
this.teleportTargets = [
|
| 131 |
-
{ position: new THREE.Vector3(10, -307, 454), lookAt: this.blackholePositionSize, galaxy: 1 },
|
| 132 |
-
{ position: new THREE.Vector3(7.2, -188, 199.6), lookAt: this.planetPositionSize, galaxy: 1 },
|
| 133 |
-
{ position: new THREE.Vector3(12.4, 3.3, -35.1), lookAt: this.wormholePositionSize, galaxy: 1 },
|
| 134 |
-
{ position: new THREE.Vector3(9.8, -4.6, -3.1), lookAt: this.wormholePositionSize, galaxy: 0 }
|
| 135 |
-
];
|
| 136 |
-
|
| 137 |
-
// Ring definition - xyz is normal going through ring. Its magnitude determines inner radius.
|
| 138 |
-
// w component determines outer radius
|
| 139 |
-
this.blackholeDisk = new THREE.Vector4(-12, 12, 6, 150.0);
|
| 140 |
-
this.saturnRings = new THREE.Vector4(0, 9.22, 0, 17.1);
|
| 141 |
-
|
| 142 |
-
var numTexturesLoaded = 0;
|
| 143 |
-
var textureCount = 0;
|
| 144 |
-
var updateProgress = function() {
|
| 145 |
-
numTexturesLoaded++;
|
| 146 |
-
if (numTexturesLoaded == textureCount)
|
| 147 |
-
{
|
| 148 |
-
var el = document.getElementById("loading");
|
| 149 |
-
el.parentElement.removeChild(el);
|
| 150 |
-
Simulation.inited = true;
|
| 151 |
-
}
|
| 152 |
-
};
|
| 153 |
-
|
| 154 |
-
this.wormholeGravityRatio = 0.25;
|
| 155 |
-
|
| 156 |
-
this.uniforms = {
|
| 157 |
-
"wormhole": { type: "v4", value: this.wormholePositionSize },
|
| 158 |
-
"wormholeGravityRatio": { type: "f", value: this.wormholeGravityRatio },
|
| 159 |
-
// 1 = like a black hole but with the mouth at the event horizon (big gravitational well)
|
| 160 |
-
// 0 = completely flat space (no gravity at all)
|
| 161 |
-
"blackhole": { type: "v4", value: this.blackholePositionSize },
|
| 162 |
-
|
| 163 |
-
"saturn": { type: "v4", value: this.saturnPositionSize },
|
| 164 |
-
"planet": { type: "v4", value: this.planetPositionSize },
|
| 165 |
-
|
| 166 |
-
"blackholeDisk": { type: "v4", value: this.blackholeDisk },
|
| 167 |
-
"saturnRings": { type: "v4", value: this.saturnRings },
|
| 168 |
-
|
| 169 |
-
"planetDiffuse": { type: "v3", value: new THREE.Vector3(0.58,0.85,0.96) },
|
| 170 |
-
"planetSpecular": { type: "v3", value: new THREE.Vector3(0.1,0.1,0.1) },
|
| 171 |
-
"texSaturn": { type: "t", value: THREE.ImageUtils.loadTexture("saturn.jpg", null, updateProgress) },
|
| 172 |
-
"texSaturnRings": { type: "t", value: THREE.ImageUtils.loadTexture("saturnrings.png", null, updateProgress) },
|
| 173 |
-
"texGalaxy1": { type: "t", value: THREE.ImageUtils.loadTexture("galaxy1.png", null, updateProgress) },
|
| 174 |
-
"texGalaxy2": { type: "t", value: THREE.ImageUtils.loadTexture("galaxy2.png", null, updateProgress) },
|
| 175 |
-
"texAccretionDisk": { type: "t", value: THREE.ImageUtils.loadTexture("accretion_disk.png", null, updateProgress) },
|
| 176 |
-
|
| 177 |
-
"lightDirection": { type: "v3", value: (new THREE.Vector3(-4, 2, 3)).normalize() },
|
| 178 |
-
|
| 179 |
-
"rayMatrix": { type: "m4", value: new THREE.Matrix4() },
|
| 180 |
-
|
| 181 |
-
"startGalaxy": { type: "i", value: 0 },
|
| 182 |
-
"cameraPosition": { type: "v3" },
|
| 183 |
-
};
|
| 184 |
-
|
| 185 |
-
for (var uniform in this.uniforms)
|
| 186 |
-
{
|
| 187 |
-
if (this.uniforms[uniform].type == "t") textureCount++;
|
| 188 |
-
}
|
| 189 |
-
|
| 190 |
-
this.uniforms.texAccretionDisk.value.wrapS = THREE.RepeatWrapping
|
| 191 |
-
|
| 192 |
-
// Some entities to calculate with
|
| 193 |
-
this.wormholeSphere = new THREE.Sphere(this.wormholePositionSize, this.wormholePositionSize.w);
|
| 194 |
-
},
|
| 195 |
-
|
| 196 |
-
updateView: function()
|
| 197 |
-
{
|
| 198 |
-
var vx, vy;
|
| 199 |
-
if (window.innerWidth > window.innerHeight)
|
| 200 |
-
{
|
| 201 |
-
vx = 1;
|
| 202 |
-
vy = window.innerHeight / window.innerWidth;
|
| 203 |
-
}
|
| 204 |
-
else
|
| 205 |
-
{
|
| 206 |
-
vx = window.innerWidth / window.innerHeight;
|
| 207 |
-
vy = 1;
|
| 208 |
-
}
|
| 209 |
-
|
| 210 |
-
this.rayMatrix.set(vx, 0, 0, 0,
|
| 211 |
-
0, vy, 0, 0,
|
| 212 |
-
0, 0, -this.zoom, 0,
|
| 213 |
-
0, 0, 0, 1);
|
| 214 |
-
|
| 215 |
-
if (this.mobileDeviceControls)
|
| 216 |
-
{
|
| 217 |
-
this.mobileDeviceControls.fovy = 2 * Math.atan(vy / this.zoom);
|
| 218 |
-
}
|
| 219 |
-
|
| 220 |
-
THREE.BloomPass.blurX.set( 1 / (512 * vx), 0.0 );
|
| 221 |
-
THREE.BloomPass.blurY.set( 0.0, 1 / (512 * vy) );
|
| 222 |
-
},
|
| 223 |
-
|
| 224 |
-
initPlayer: function()
|
| 225 |
-
{
|
| 226 |
-
var self = this;
|
| 227 |
-
|
| 228 |
-
this.player = new Player;
|
| 229 |
-
this.player.lookAt(this.wormholePositionSize);
|
| 230 |
-
|
| 231 |
-
for (var i = 0; i < this.teleportTargets.length; i++) {
|
| 232 |
-
this.player.addTeleportTarget(this.teleportTargets[i]);
|
| 233 |
-
}
|
| 234 |
-
|
| 235 |
-
// Add keyboard controls to the player
|
| 236 |
-
this.keyboardControls = new KeyboardControls(this.player, this.container);
|
| 237 |
-
this.keyboardControls.movementSpeed = 1;
|
| 238 |
-
this.keyboardControls.rollSpeed = Math.PI / 3;
|
| 239 |
-
this.keyboardControls.autoForward = false;
|
| 240 |
-
this.keyboardControls.dragToLook = false;
|
| 241 |
-
this.keyboardControls.connect();
|
| 242 |
-
|
| 243 |
-
this.player.controls.push(this.keyboardControls);
|
| 244 |
-
|
| 245 |
-
// Add mobile device controls (touch + accelerometer) to the player
|
| 246 |
-
this.mobileDeviceControls = new MobileDeviceControls(this.player, this.container);
|
| 247 |
-
this.mobileDeviceControls.movementSpeed = 1.3;
|
| 248 |
-
|
| 249 |
-
this.player.controls.push(this.mobileDeviceControls);
|
| 250 |
-
|
| 251 |
-
// Pretty sure we don't need mobile device controls when a keyboard event is triggered.
|
| 252 |
-
var keypress = function(event) {
|
| 253 |
-
|
| 254 |
-
if (event.charCode == 32)
|
| 255 |
-
{
|
| 256 |
-
self.keyboardControls.dragToLook = !self.keyboardControls.dragToLook;
|
| 257 |
-
}
|
| 258 |
-
else if (event.keyCode == 27)
|
| 259 |
-
{
|
| 260 |
-
self.keyboardControls.dragToLook = true;
|
| 261 |
-
}
|
| 262 |
-
|
| 263 |
-
if (self.keyboardControls.dragToLook)
|
| 264 |
-
{
|
| 265 |
-
self.keyboardControls.moveState.yawLeft = 0;
|
| 266 |
-
self.keyboardControls.moveState.pitchDown = 0;
|
| 267 |
-
}
|
| 268 |
-
|
| 269 |
-
self.mobileDeviceControls.disconnect();
|
| 270 |
-
|
| 271 |
-
document.body.classList.remove("mobile-device");
|
| 272 |
-
};
|
| 273 |
-
|
| 274 |
-
window.addEventListener("keypress", keypress, false);
|
| 275 |
-
|
| 276 |
-
var deviceListener = function(event) {
|
| 277 |
-
if (event.alpha === null) return;
|
| 278 |
-
|
| 279 |
-
self.mobileDeviceControls.connect();
|
| 280 |
-
|
| 281 |
-
// The player will probably not be looking with their device in the right direction, so fix that
|
| 282 |
-
requestAnimationFrame(function () {
|
| 283 |
-
self.player.object.quaternion.multiply(self.player.eyes.quaternion.clone().inverse())
|
| 284 |
-
})
|
| 285 |
-
|
| 286 |
-
window.removeEventListener("deviceorientation", deviceListener, false);
|
| 287 |
-
|
| 288 |
-
document.body.classList.add("mobile-device");
|
| 289 |
-
};
|
| 290 |
-
|
| 291 |
-
window.addEventListener("deviceorientation", deviceListener, false);
|
| 292 |
-
|
| 293 |
-
this.updateView();
|
| 294 |
-
},
|
| 295 |
-
|
| 296 |
-
step: function()
|
| 297 |
-
{
|
| 298 |
-
if (this.inited)
|
| 299 |
-
{
|
| 300 |
-
this.update();
|
| 301 |
-
}
|
| 302 |
-
this.render();
|
| 303 |
-
},
|
| 304 |
-
|
| 305 |
-
start: function()
|
| 306 |
-
{
|
| 307 |
-
var self = this;
|
| 308 |
-
|
| 309 |
-
this.clock = new THREE.Clock();
|
| 310 |
-
|
| 311 |
-
function animate()
|
| 312 |
-
{
|
| 313 |
-
requestAnimationFrame(animate);
|
| 314 |
-
|
| 315 |
-
self.step();
|
| 316 |
-
}
|
| 317 |
-
|
| 318 |
-
animate();
|
| 319 |
-
},
|
| 320 |
-
|
| 321 |
-
update: function()
|
| 322 |
-
{
|
| 323 |
-
var delta = this.clock.getDelta();
|
| 324 |
-
|
| 325 |
-
// TODO: figure out why delta can become so small
|
| 326 |
-
if (delta < 0.001)
|
| 327 |
-
{
|
| 328 |
-
delta = 0.001;
|
| 329 |
-
}
|
| 330 |
-
|
| 331 |
-
this.player.update(delta);
|
| 332 |
-
},
|
| 333 |
-
|
| 334 |
-
render: function()
|
| 335 |
-
{
|
| 336 |
-
this.uniforms.rayMatrix.value.makeRotationFromQuaternion(this.player.eyes.getWorldQuaternion());
|
| 337 |
-
this.uniforms.rayMatrix.value.multiply(this.rayMatrix);
|
| 338 |
-
|
| 339 |
-
this.uniforms.cameraPosition.value = this.player.eyes.getWorldPosition();
|
| 340 |
-
this.uniforms.startGalaxy.value = this.player.galaxy;
|
| 341 |
-
|
| 342 |
-
this.renderer.clear();
|
| 343 |
-
this.composer.render();
|
| 344 |
-
},
|
| 345 |
-
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
js/controls/DeviceOrientationControls.js
DELETED
|
@@ -1,88 +0,0 @@
|
|
| 1 |
-
/**
|
| 2 |
-
* @author richt / http://richt.me
|
| 3 |
-
* @author WestLangley / http://github.com/WestLangley
|
| 4 |
-
*
|
| 5 |
-
* W3C Device Orientation control (http://w3c.github.io/deviceorientation/spec-source-orientation.html)
|
| 6 |
-
*/
|
| 7 |
-
|
| 8 |
-
THREE.DeviceOrientationControls = function ( object ) {
|
| 9 |
-
|
| 10 |
-
var scope = this;
|
| 11 |
-
|
| 12 |
-
this.object = object;
|
| 13 |
-
this.object.rotation.reorder( "YXZ" );
|
| 14 |
-
|
| 15 |
-
this.enabled = true;
|
| 16 |
-
|
| 17 |
-
var onDeviceOrientationChangeEvent = function ( event ) {
|
| 18 |
-
|
| 19 |
-
scope.deviceOrientation = event;
|
| 20 |
-
|
| 21 |
-
};
|
| 22 |
-
|
| 23 |
-
var onScreenOrientationChangeEvent = function () {
|
| 24 |
-
|
| 25 |
-
scope.screenOrientation = window.orientation || 0;
|
| 26 |
-
|
| 27 |
-
};
|
| 28 |
-
|
| 29 |
-
// The angles alpha, beta and gamma form a set of intrinsic Tait-Bryan angles of type Z-X'-Y''
|
| 30 |
-
|
| 31 |
-
var setObjectQuaternion = function () {
|
| 32 |
-
|
| 33 |
-
var zee = new THREE.Vector3( 0, 0, 1 );
|
| 34 |
-
|
| 35 |
-
var euler = new THREE.Euler();
|
| 36 |
-
|
| 37 |
-
var q0 = new THREE.Quaternion();
|
| 38 |
-
|
| 39 |
-
var q1 = new THREE.Quaternion( - Math.sqrt( 0.5 ), 0, 0, Math.sqrt( 0.5 ) ); // - PI/2 around the x-axis
|
| 40 |
-
|
| 41 |
-
return function ( quaternion, alpha, beta, gamma, orient ) {
|
| 42 |
-
|
| 43 |
-
euler.set( beta, alpha, - gamma, 'YXZ' ); // 'ZXY' for the device, but 'YXZ' for us
|
| 44 |
-
|
| 45 |
-
quaternion.setFromEuler( euler ); // orient the device
|
| 46 |
-
|
| 47 |
-
quaternion.multiply( q1 ); // camera looks out the back of the device, not the top
|
| 48 |
-
|
| 49 |
-
quaternion.multiply( q0.setFromAxisAngle( zee, - orient ) ); // adjust for screen orientation
|
| 50 |
-
|
| 51 |
-
}
|
| 52 |
-
|
| 53 |
-
}();
|
| 54 |
-
|
| 55 |
-
this.connect = function() {
|
| 56 |
-
|
| 57 |
-
onScreenOrientationChangeEvent(); // run once on load
|
| 58 |
-
|
| 59 |
-
window.addEventListener( 'orientationchange', onScreenOrientationChangeEvent, false );
|
| 60 |
-
window.addEventListener( 'deviceorientation', onDeviceOrientationChangeEvent, false );
|
| 61 |
-
|
| 62 |
-
scope.enabled = true;
|
| 63 |
-
|
| 64 |
-
};
|
| 65 |
-
|
| 66 |
-
this.disconnect = function() {
|
| 67 |
-
|
| 68 |
-
window.removeEventListener( 'orientationchange', onScreenOrientationChangeEvent, false );
|
| 69 |
-
window.removeEventListener( 'deviceorientation', onDeviceOrientationChangeEvent, false );
|
| 70 |
-
|
| 71 |
-
scope.enabled = false;
|
| 72 |
-
|
| 73 |
-
};
|
| 74 |
-
|
| 75 |
-
this.update = function () {
|
| 76 |
-
|
| 77 |
-
if ( scope.enabled === false || !scope.deviceOrientation || scope.deviceOrientation.alpha === null ) return;
|
| 78 |
-
|
| 79 |
-
var alpha = scope.deviceOrientation.alpha ? THREE.Math.degToRad( scope.deviceOrientation.alpha ) : 0; // Z
|
| 80 |
-
var beta = scope.deviceOrientation.beta ? THREE.Math.degToRad( scope.deviceOrientation.beta ) : 0; // X'
|
| 81 |
-
var gamma = scope.deviceOrientation.gamma ? THREE.Math.degToRad( scope.deviceOrientation.gamma ) : 0; // Y''
|
| 82 |
-
var orient = scope.screenOrientation ? THREE.Math.degToRad( scope.screenOrientation ) : 0; // O
|
| 83 |
-
|
| 84 |
-
setObjectQuaternion( scope.object.quaternion, alpha, beta, gamma, orient );
|
| 85 |
-
|
| 86 |
-
};
|
| 87 |
-
|
| 88 |
-
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
js/controls/KeyboardControls.js
DELETED
|
@@ -1,324 +0,0 @@
|
|
| 1 |
-
function KeyboardControls(player, element)
|
| 2 |
-
{
|
| 3 |
-
this.player = player;
|
| 4 |
-
this.teleport = this.player.teleport.bind(this.player);
|
| 5 |
-
|
| 6 |
-
element.setAttribute('tabindex', -1);
|
| 7 |
-
|
| 8 |
-
this.movementSpeedMultiplier = 1;
|
| 9 |
-
this.movementSpeed = 1.0;
|
| 10 |
-
this.rollSpeed = 0.005;
|
| 11 |
-
|
| 12 |
-
this.dragToLook = false;
|
| 13 |
-
this.autoForward = false;
|
| 14 |
-
|
| 15 |
-
// Internals
|
| 16 |
-
this.tmpQuaternion = new THREE.Quaternion();
|
| 17 |
-
|
| 18 |
-
this.mouseStatus = 0;
|
| 19 |
-
|
| 20 |
-
this.moveState = {
|
| 21 |
-
up: 0,
|
| 22 |
-
down: 0,
|
| 23 |
-
left: 0,
|
| 24 |
-
right: 0,
|
| 25 |
-
forward: 0,
|
| 26 |
-
back: 0,
|
| 27 |
-
pitchUp: 0,
|
| 28 |
-
pitchDown: 0,
|
| 29 |
-
yawLeft: 0,
|
| 30 |
-
yawRight: 0,
|
| 31 |
-
rollLeft: 0,
|
| 32 |
-
rollRight: 0,
|
| 33 |
-
};
|
| 34 |
-
|
| 35 |
-
this.moveVector = new THREE.Vector3( 0, 0, 0 );
|
| 36 |
-
this.rotationVector = new THREE.Vector3( 0, 0, 0 );
|
| 37 |
-
|
| 38 |
-
var self = this;
|
| 39 |
-
|
| 40 |
-
function keydown(event)
|
| 41 |
-
{
|
| 42 |
-
if (event.altKey)
|
| 43 |
-
{
|
| 44 |
-
return;
|
| 45 |
-
}
|
| 46 |
-
|
| 47 |
-
switch (event.keyCode)
|
| 48 |
-
{
|
| 49 |
-
case 16: // Shift
|
| 50 |
-
self.movementSpeedMultiplier = 10;
|
| 51 |
-
break;
|
| 52 |
-
|
| 53 |
-
case 87: // W
|
| 54 |
-
self.moveState.forward = 1;
|
| 55 |
-
break;
|
| 56 |
-
|
| 57 |
-
case 83: // S
|
| 58 |
-
self.moveState.back = 1;
|
| 59 |
-
break;
|
| 60 |
-
|
| 61 |
-
case 65: // A
|
| 62 |
-
self.moveState.left = 1;
|
| 63 |
-
break;
|
| 64 |
-
|
| 65 |
-
case 68: // D
|
| 66 |
-
self.moveState.right = 1;
|
| 67 |
-
break;
|
| 68 |
-
|
| 69 |
-
case 82: // R
|
| 70 |
-
self.moveState.up = 1;
|
| 71 |
-
break;
|
| 72 |
-
|
| 73 |
-
case 70: // F
|
| 74 |
-
self.moveState.down = 1;
|
| 75 |
-
break;
|
| 76 |
-
|
| 77 |
-
case 38: // Up
|
| 78 |
-
self.moveState.pitchUp = 1;
|
| 79 |
-
break;
|
| 80 |
-
|
| 81 |
-
case 40: // Down
|
| 82 |
-
self.moveState.pitchDown = 1;
|
| 83 |
-
break;
|
| 84 |
-
|
| 85 |
-
case 37: // Left
|
| 86 |
-
self.moveState.yawLeft = 1;
|
| 87 |
-
break;
|
| 88 |
-
|
| 89 |
-
case 39: // Right
|
| 90 |
-
self.moveState.yawRight = 1;
|
| 91 |
-
break;
|
| 92 |
-
|
| 93 |
-
case 81: // Q
|
| 94 |
-
self.moveState.rollLeft = 1;
|
| 95 |
-
break;
|
| 96 |
-
|
| 97 |
-
case 69: // E
|
| 98 |
-
self.moveState.rollRight = 1;
|
| 99 |
-
break;
|
| 100 |
-
|
| 101 |
-
case 84: // T
|
| 102 |
-
self.teleport();
|
| 103 |
-
break;
|
| 104 |
-
}
|
| 105 |
-
|
| 106 |
-
updateMovementVector();
|
| 107 |
-
updateRotationVector();
|
| 108 |
-
};
|
| 109 |
-
|
| 110 |
-
function keyup(event)
|
| 111 |
-
{
|
| 112 |
-
switch (event.keyCode)
|
| 113 |
-
{
|
| 114 |
-
case 16: // Shift
|
| 115 |
-
self.movementSpeedMultiplier = 1;
|
| 116 |
-
break;
|
| 117 |
-
|
| 118 |
-
case 87: // W
|
| 119 |
-
self.moveState.forward = 0;
|
| 120 |
-
break;
|
| 121 |
-
|
| 122 |
-
case 83: // S
|
| 123 |
-
self.moveState.back = 0;
|
| 124 |
-
break;
|
| 125 |
-
|
| 126 |
-
case 65: // A
|
| 127 |
-
self.moveState.left = 0;
|
| 128 |
-
break;
|
| 129 |
-
|
| 130 |
-
case 68: // D
|
| 131 |
-
self.moveState.right = 0;
|
| 132 |
-
break;
|
| 133 |
-
|
| 134 |
-
case 82: // R
|
| 135 |
-
self.moveState.up = 0;
|
| 136 |
-
break;
|
| 137 |
-
|
| 138 |
-
case 70: // F
|
| 139 |
-
self.moveState.down = 0;
|
| 140 |
-
break;
|
| 141 |
-
|
| 142 |
-
case 38: // Up
|
| 143 |
-
self.moveState.pitchUp = 0;
|
| 144 |
-
break;
|
| 145 |
-
|
| 146 |
-
case 40: // Down
|
| 147 |
-
self.moveState.pitchDown = 0;
|
| 148 |
-
break;
|
| 149 |
-
|
| 150 |
-
case 37: // Left
|
| 151 |
-
self.moveState.yawLeft = 0;
|
| 152 |
-
break;
|
| 153 |
-
|
| 154 |
-
case 39: // Right
|
| 155 |
-
self.moveState.yawRight = 0;
|
| 156 |
-
break;
|
| 157 |
-
|
| 158 |
-
case 81: // Q
|
| 159 |
-
self.moveState.rollLeft = 0;
|
| 160 |
-
break;
|
| 161 |
-
|
| 162 |
-
case 69: // E
|
| 163 |
-
self.moveState.rollRight = 0;
|
| 164 |
-
break;
|
| 165 |
-
}
|
| 166 |
-
|
| 167 |
-
updateMovementVector();
|
| 168 |
-
updateRotationVector();
|
| 169 |
-
}
|
| 170 |
-
|
| 171 |
-
function mousedown(event)
|
| 172 |
-
{
|
| 173 |
-
if ( element !== document )
|
| 174 |
-
{
|
| 175 |
-
element.focus();
|
| 176 |
-
}
|
| 177 |
-
|
| 178 |
-
event.preventDefault();
|
| 179 |
-
event.stopPropagation();
|
| 180 |
-
|
| 181 |
-
if (self.dragToLook)
|
| 182 |
-
{
|
| 183 |
-
self.mouseStatus++;
|
| 184 |
-
}
|
| 185 |
-
else
|
| 186 |
-
{
|
| 187 |
-
switch (event.button)
|
| 188 |
-
{
|
| 189 |
-
case 0:
|
| 190 |
-
self.moveState.forward = 1;
|
| 191 |
-
break;
|
| 192 |
-
|
| 193 |
-
case 2:
|
| 194 |
-
self.moveState.back = 1;
|
| 195 |
-
break;
|
| 196 |
-
}
|
| 197 |
-
|
| 198 |
-
updateMovementVector();
|
| 199 |
-
}
|
| 200 |
-
}
|
| 201 |
-
|
| 202 |
-
function mousemove(event)
|
| 203 |
-
{
|
| 204 |
-
if (self.dragToLook && self.mouseStatus == 0)
|
| 205 |
-
{
|
| 206 |
-
return;
|
| 207 |
-
}
|
| 208 |
-
|
| 209 |
-
var container = getContainerDimensions();
|
| 210 |
-
var halfWidth = container.size[0] / 2;
|
| 211 |
-
var halfHeight = container.size[1] / 2;
|
| 212 |
-
|
| 213 |
-
self.moveState.yawLeft = - ((event.pageX - container.offset[0]) - halfWidth ) / halfWidth;
|
| 214 |
-
self.moveState.pitchDown = ((event.pageY - container.offset[1]) - halfHeight) / halfHeight;
|
| 215 |
-
|
| 216 |
-
updateRotationVector();
|
| 217 |
-
}
|
| 218 |
-
|
| 219 |
-
function mouseup(event)
|
| 220 |
-
{
|
| 221 |
-
event.preventDefault();
|
| 222 |
-
event.stopPropagation();
|
| 223 |
-
|
| 224 |
-
if ( self.dragToLook )
|
| 225 |
-
{
|
| 226 |
-
self.mouseStatus --;
|
| 227 |
-
self.moveState.yawLeft = self.moveState.pitchDown = 0;
|
| 228 |
-
}
|
| 229 |
-
else
|
| 230 |
-
{
|
| 231 |
-
switch (event.button)
|
| 232 |
-
{
|
| 233 |
-
case 0:
|
| 234 |
-
self.moveState.forward = 0;
|
| 235 |
-
break;
|
| 236 |
-
|
| 237 |
-
case 2:
|
| 238 |
-
self.moveState.back = 0;
|
| 239 |
-
break;
|
| 240 |
-
}
|
| 241 |
-
|
| 242 |
-
updateMovementVector();
|
| 243 |
-
}
|
| 244 |
-
|
| 245 |
-
updateRotationVector();
|
| 246 |
-
};
|
| 247 |
-
|
| 248 |
-
function updateMovementVector()
|
| 249 |
-
{
|
| 250 |
-
var forward = (self.moveState.forward || (self.autoForward && !self.moveState.back)) ? 1 : 0;
|
| 251 |
-
|
| 252 |
-
self.moveVector.x = ( -self.moveState.left + self.moveState.right );
|
| 253 |
-
self.moveVector.y = ( -self.moveState.down + self.moveState.up );
|
| 254 |
-
self.moveVector.z = ( -forward + self.moveState.back );
|
| 255 |
-
};
|
| 256 |
-
|
| 257 |
-
function updateRotationVector()
|
| 258 |
-
{
|
| 259 |
-
self.rotationVector.x = ( -self.moveState.pitchDown + self.moveState.pitchUp );
|
| 260 |
-
self.rotationVector.y = ( -self.moveState.yawRight + self.moveState.yawLeft );
|
| 261 |
-
self.rotationVector.z = ( -self.moveState.rollRight + self.moveState.rollLeft );
|
| 262 |
-
};
|
| 263 |
-
|
| 264 |
-
function getContainerDimensions()
|
| 265 |
-
{
|
| 266 |
-
if (element != document)
|
| 267 |
-
{
|
| 268 |
-
return {
|
| 269 |
-
size: [element.offsetWidth, element.offsetHeight],
|
| 270 |
-
offset: [element.offsetLeft, element.offsetTop]
|
| 271 |
-
};
|
| 272 |
-
}
|
| 273 |
-
else
|
| 274 |
-
{
|
| 275 |
-
return {
|
| 276 |
-
size: [window.innerWidth, window.innerHeight],
|
| 277 |
-
offset: [0, 0]
|
| 278 |
-
};
|
| 279 |
-
}
|
| 280 |
-
}
|
| 281 |
-
|
| 282 |
-
function contextmenu(event)
|
| 283 |
-
{
|
| 284 |
-
event.preventDefault();
|
| 285 |
-
}
|
| 286 |
-
|
| 287 |
-
this.connect = function() {
|
| 288 |
-
updateMovementVector();
|
| 289 |
-
updateRotationVector();
|
| 290 |
-
|
| 291 |
-
element.addEventListener('contextmenu', contextmenu, false);
|
| 292 |
-
|
| 293 |
-
element.addEventListener('mousemove', mousemove, false);
|
| 294 |
-
element.addEventListener('mousedown', mousedown, false);
|
| 295 |
-
element.addEventListener('mouseup', mouseup, false);
|
| 296 |
-
|
| 297 |
-
window.addEventListener('keydown', keydown, false);
|
| 298 |
-
window.addEventListener('keyup', keyup, false);
|
| 299 |
-
|
| 300 |
-
this.enabled = true;
|
| 301 |
-
};
|
| 302 |
-
|
| 303 |
-
this.disconnect = function() {
|
| 304 |
-
element.removeEventListener('contextmenu', contextmenu, false);
|
| 305 |
-
|
| 306 |
-
element.removeEventListener('mousemove', mousemove, false);
|
| 307 |
-
element.removeEventListener('mousedown', mousedown, false);
|
| 308 |
-
element.removeEventListener('mouseup', mouseup, false);
|
| 309 |
-
|
| 310 |
-
window.removeEventListener('keydown', keydown, false);
|
| 311 |
-
window.removeEventListener('keyup', keyup, false);
|
| 312 |
-
|
| 313 |
-
this.enabled = false;
|
| 314 |
-
};
|
| 315 |
-
|
| 316 |
-
this.update = function()
|
| 317 |
-
{
|
| 318 |
-
var moveMult = this.movementSpeed * this.movementSpeedMultiplier;
|
| 319 |
-
var rotMult = this.rollSpeed;
|
| 320 |
-
|
| 321 |
-
this.player.velocity.copy(this.moveVector).multiplyScalar(moveMult).applyQuaternion(this.player.eyes.getWorldQuaternion());
|
| 322 |
-
this.player.eyeAngularVelocity.copy(this.rotationVector).multiplyScalar(rotMult);
|
| 323 |
-
};
|
| 324 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
js/controls/MobileDeviceControls.js
DELETED
|
@@ -1,89 +0,0 @@
|
|
| 1 |
-
function MobileDeviceControls(player, element)
|
| 2 |
-
{
|
| 3 |
-
this.deviceOrientationControls = new THREE.DeviceOrientationControls(player.eyes);
|
| 4 |
-
|
| 5 |
-
this.player = player;
|
| 6 |
-
|
| 7 |
-
this.velocity = new THREE.Vector3(0, 0, 0);
|
| 8 |
-
|
| 9 |
-
var self = this;
|
| 10 |
-
|
| 11 |
-
function teleport(event)
|
| 12 |
-
{
|
| 13 |
-
event.preventDefault();
|
| 14 |
-
|
| 15 |
-
player.teleport();
|
| 16 |
-
}
|
| 17 |
-
|
| 18 |
-
function onTouchEvent(event)
|
| 19 |
-
{
|
| 20 |
-
event.preventDefault();
|
| 21 |
-
|
| 22 |
-
var touches = event.touches;
|
| 23 |
-
|
| 24 |
-
if (touches.length == 0)
|
| 25 |
-
{
|
| 26 |
-
self.velocity.set(0, 0, 0);
|
| 27 |
-
return;
|
| 28 |
-
}
|
| 29 |
-
|
| 30 |
-
var avgX = 0, avgY = 0;
|
| 31 |
-
for (var i = 0; i < touches.length; i++)
|
| 32 |
-
{
|
| 33 |
-
avgX += touches[i].pageX;
|
| 34 |
-
avgY += touches[i].pageY;
|
| 35 |
-
}
|
| 36 |
-
avgX /= touches.length;
|
| 37 |
-
avgY /= touches.length;
|
| 38 |
-
|
| 39 |
-
var vy = Math.tan(0.5 * self.fovy);
|
| 40 |
-
var vx = window.innerWidth / window.innerHeight * vy;
|
| 41 |
-
|
| 42 |
-
self.velocity.set(
|
| 43 |
-
vx * (avgX * 2.0 / window.innerWidth - 1.0),
|
| 44 |
-
-vy * (avgY * 2.0 / window.innerHeight - 1.0),
|
| 45 |
-
-1
|
| 46 |
-
).normalize();
|
| 47 |
-
|
| 48 |
-
self.velocity.multiplyScalar(touches.length > 1 ? 10 : 1);
|
| 49 |
-
}
|
| 50 |
-
|
| 51 |
-
this.connect = function() {
|
| 52 |
-
this.deviceOrientationControls.connect();
|
| 53 |
-
|
| 54 |
-
element.addEventListener( 'touchstart', onTouchEvent, false );
|
| 55 |
-
element.addEventListener( 'touchmove', onTouchEvent, false );
|
| 56 |
-
element.addEventListener( 'touchend', onTouchEvent, false );
|
| 57 |
-
|
| 58 |
-
document.querySelector( '#teleport' ).addEventListener(
|
| 59 |
-
'touchstart',
|
| 60 |
-
teleport,
|
| 61 |
-
false
|
| 62 |
-
);
|
| 63 |
-
|
| 64 |
-
this.enabled = true;
|
| 65 |
-
};
|
| 66 |
-
|
| 67 |
-
this.disconnect = function() {
|
| 68 |
-
this.deviceOrientationControls.disconnect();
|
| 69 |
-
|
| 70 |
-
element.removeEventListener( 'touchstart', onTouchEvent, false );
|
| 71 |
-
element.removeEventListener( 'touchmove', onTouchEvent, false );
|
| 72 |
-
element.removeEventListener( 'touchend', onTouchEvent, false );
|
| 73 |
-
|
| 74 |
-
document.querySelector( '#teleport' ).removeEventListener(
|
| 75 |
-
'touchstart',
|
| 76 |
-
teleport,
|
| 77 |
-
false
|
| 78 |
-
);
|
| 79 |
-
|
| 80 |
-
this.enabled = false;
|
| 81 |
-
};
|
| 82 |
-
|
| 83 |
-
this.update = function () {
|
| 84 |
-
if (this.enabled === false) return;
|
| 85 |
-
|
| 86 |
-
this.player.velocity.copy(this.velocity).applyQuaternion(this.player.eyes.getWorldQuaternion());
|
| 87 |
-
this.deviceOrientationControls.update();
|
| 88 |
-
};
|
| 89 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
js/main.js
DELETED
|
@@ -1,13 +0,0 @@
|
|
| 1 |
-
(function() {
|
| 2 |
-
|
| 3 |
-
if (!Detector.webgl)
|
| 4 |
-
{
|
| 5 |
-
document.body.classList.add("no-ui");
|
| 6 |
-
Detector.addGetWebGLMessage({id: "webgl-error"});
|
| 7 |
-
return;
|
| 8 |
-
}
|
| 9 |
-
|
| 10 |
-
Simulation.init();
|
| 11 |
-
Simulation.start();
|
| 12 |
-
|
| 13 |
-
})();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
js/postprocessing/BloomPass.js
DELETED
|
@@ -1,115 +0,0 @@
|
|
| 1 |
-
/**
|
| 2 |
-
* @author alteredq / http://alteredqualia.com/
|
| 3 |
-
*/
|
| 4 |
-
|
| 5 |
-
THREE.BloomPass = function ( strength, kernelSize, sigma, resolution ) {
|
| 6 |
-
|
| 7 |
-
strength = ( strength !== undefined ) ? strength : 1;
|
| 8 |
-
kernelSize = ( kernelSize !== undefined ) ? kernelSize : 25;
|
| 9 |
-
sigma = ( sigma !== undefined ) ? sigma : 4.0;
|
| 10 |
-
resolution = ( resolution !== undefined ) ? resolution : 256;
|
| 11 |
-
|
| 12 |
-
// render targets
|
| 13 |
-
|
| 14 |
-
var pars = { minFilter: THREE.LinearFilter, magFilter: THREE.LinearFilter, format: THREE.RGBFormat };
|
| 15 |
-
|
| 16 |
-
this.renderTargetX = new THREE.WebGLRenderTarget( resolution, resolution, pars );
|
| 17 |
-
this.renderTargetY = new THREE.WebGLRenderTarget( resolution, resolution, pars );
|
| 18 |
-
|
| 19 |
-
// copy material
|
| 20 |
-
|
| 21 |
-
if ( THREE.CopyShader === undefined )
|
| 22 |
-
console.error( "THREE.BloomPass relies on THREE.CopyShader" );
|
| 23 |
-
|
| 24 |
-
var copyShader = THREE.CopyShader;
|
| 25 |
-
|
| 26 |
-
this.copyUniforms = THREE.UniformsUtils.clone( copyShader.uniforms );
|
| 27 |
-
|
| 28 |
-
this.copyUniforms[ "opacity" ].value = strength;
|
| 29 |
-
|
| 30 |
-
this.materialCopy = new THREE.ShaderMaterial( {
|
| 31 |
-
|
| 32 |
-
uniforms: this.copyUniforms,
|
| 33 |
-
vertexShader: copyShader.vertexShader,
|
| 34 |
-
fragmentShader: copyShader.fragmentShader,
|
| 35 |
-
blending: THREE.AdditiveBlending,
|
| 36 |
-
transparent: true
|
| 37 |
-
|
| 38 |
-
} );
|
| 39 |
-
|
| 40 |
-
// convolution material
|
| 41 |
-
|
| 42 |
-
if ( THREE.ConvolutionShader === undefined )
|
| 43 |
-
console.error( "THREE.BloomPass relies on THREE.ConvolutionShader" );
|
| 44 |
-
|
| 45 |
-
var convolutionShader = THREE.ConvolutionShader;
|
| 46 |
-
|
| 47 |
-
this.convolutionUniforms = THREE.UniformsUtils.clone( convolutionShader.uniforms );
|
| 48 |
-
|
| 49 |
-
this.convolutionUniforms[ "uImageIncrement" ].value = THREE.BloomPass.blurx;
|
| 50 |
-
this.convolutionUniforms[ "cKernel" ].value = THREE.ConvolutionShader.buildKernel( sigma );
|
| 51 |
-
|
| 52 |
-
this.materialConvolution = new THREE.ShaderMaterial( {
|
| 53 |
-
|
| 54 |
-
uniforms: this.convolutionUniforms,
|
| 55 |
-
vertexShader: convolutionShader.vertexShader,
|
| 56 |
-
fragmentShader: convolutionShader.fragmentShader,
|
| 57 |
-
defines: {
|
| 58 |
-
"KERNEL_SIZE_FLOAT": kernelSize.toFixed( 1 ),
|
| 59 |
-
"KERNEL_SIZE_INT": kernelSize.toFixed( 0 )
|
| 60 |
-
}
|
| 61 |
-
|
| 62 |
-
} );
|
| 63 |
-
|
| 64 |
-
this.enabled = true;
|
| 65 |
-
this.needsSwap = false;
|
| 66 |
-
this.clear = false;
|
| 67 |
-
|
| 68 |
-
|
| 69 |
-
this.camera = new THREE.OrthographicCamera( -1, 1, 1, -1, 0, 1 );
|
| 70 |
-
this.scene = new THREE.Scene();
|
| 71 |
-
|
| 72 |
-
this.quad = new THREE.Mesh( new THREE.PlaneBufferGeometry( 2, 2 ), null );
|
| 73 |
-
this.scene.add( this.quad );
|
| 74 |
-
|
| 75 |
-
};
|
| 76 |
-
|
| 77 |
-
THREE.BloomPass.prototype = {
|
| 78 |
-
|
| 79 |
-
render: function ( renderer, writeBuffer, readBuffer, delta, maskActive ) {
|
| 80 |
-
|
| 81 |
-
if ( maskActive ) renderer.context.disable( renderer.context.STENCIL_TEST );
|
| 82 |
-
|
| 83 |
-
// Render quad with blured scene into texture (convolution pass 1)
|
| 84 |
-
|
| 85 |
-
this.quad.material = this.materialConvolution;
|
| 86 |
-
|
| 87 |
-
this.convolutionUniforms[ "tDiffuse" ].value = readBuffer;
|
| 88 |
-
this.convolutionUniforms[ "uImageIncrement" ].value = THREE.BloomPass.blurX;
|
| 89 |
-
|
| 90 |
-
renderer.render( this.scene, this.camera, this.renderTargetX, true );
|
| 91 |
-
|
| 92 |
-
|
| 93 |
-
// Render quad with blured scene into texture (convolution pass 2)
|
| 94 |
-
|
| 95 |
-
this.convolutionUniforms[ "tDiffuse" ].value = this.renderTargetX;
|
| 96 |
-
this.convolutionUniforms[ "uImageIncrement" ].value = THREE.BloomPass.blurY;
|
| 97 |
-
|
| 98 |
-
renderer.render( this.scene, this.camera, this.renderTargetY, true );
|
| 99 |
-
|
| 100 |
-
// Render original scene with superimposed blur to texture
|
| 101 |
-
|
| 102 |
-
this.quad.material = this.materialCopy;
|
| 103 |
-
|
| 104 |
-
this.copyUniforms[ "tDiffuse" ].value = this.renderTargetY;
|
| 105 |
-
|
| 106 |
-
if ( maskActive ) renderer.context.enable( renderer.context.STENCIL_TEST );
|
| 107 |
-
|
| 108 |
-
renderer.render( this.scene, this.camera, readBuffer, this.clear );
|
| 109 |
-
|
| 110 |
-
}
|
| 111 |
-
|
| 112 |
-
};
|
| 113 |
-
|
| 114 |
-
THREE.BloomPass.blurX = new THREE.Vector2( 0.001953125, 0.0 );
|
| 115 |
-
THREE.BloomPass.blurY = new THREE.Vector2( 0.0, 0.001953125 );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
js/postprocessing/ConvolutionShader.js
DELETED
|
@@ -1,101 +0,0 @@
|
|
| 1 |
-
/**
|
| 2 |
-
* @author alteredq / http://alteredqualia.com/
|
| 3 |
-
*
|
| 4 |
-
* Convolution shader
|
| 5 |
-
* ported from o3d sample to WebGL / GLSL
|
| 6 |
-
* http://o3d.googlecode.com/svn/trunk/samples/convolution.html
|
| 7 |
-
*/
|
| 8 |
-
|
| 9 |
-
THREE.ConvolutionShader = {
|
| 10 |
-
|
| 11 |
-
defines: {
|
| 12 |
-
|
| 13 |
-
"KERNEL_SIZE_FLOAT": "25.0",
|
| 14 |
-
"KERNEL_SIZE_INT": "25",
|
| 15 |
-
|
| 16 |
-
},
|
| 17 |
-
|
| 18 |
-
uniforms: {
|
| 19 |
-
|
| 20 |
-
"tDiffuse": { type: "t", value: null },
|
| 21 |
-
"uImageIncrement": { type: "v2", value: new THREE.Vector2( 0.001953125, 0.0 ) },
|
| 22 |
-
"cKernel": { type: "fv1", value: [] }
|
| 23 |
-
|
| 24 |
-
},
|
| 25 |
-
|
| 26 |
-
vertexShader: [
|
| 27 |
-
|
| 28 |
-
"uniform vec2 uImageIncrement;",
|
| 29 |
-
|
| 30 |
-
"varying vec2 vUv;",
|
| 31 |
-
|
| 32 |
-
"void main() {",
|
| 33 |
-
|
| 34 |
-
"vUv = uv - ( ( KERNEL_SIZE_FLOAT - 1.0 ) / 2.0 ) * uImageIncrement;",
|
| 35 |
-
"gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",
|
| 36 |
-
|
| 37 |
-
"}"
|
| 38 |
-
|
| 39 |
-
].join("\n"),
|
| 40 |
-
|
| 41 |
-
fragmentShader: [
|
| 42 |
-
|
| 43 |
-
"uniform float cKernel[ KERNEL_SIZE_INT ];",
|
| 44 |
-
|
| 45 |
-
"uniform sampler2D tDiffuse;",
|
| 46 |
-
"uniform vec2 uImageIncrement;",
|
| 47 |
-
|
| 48 |
-
"varying vec2 vUv;",
|
| 49 |
-
|
| 50 |
-
"void main() {",
|
| 51 |
-
|
| 52 |
-
"vec2 imageCoord = vUv;",
|
| 53 |
-
"vec4 sum = vec4( 0.0, 0.0, 0.0, 0.0 );",
|
| 54 |
-
|
| 55 |
-
"for( int i = 0; i < KERNEL_SIZE_INT; i ++ ) {",
|
| 56 |
-
|
| 57 |
-
"sum += texture2D( tDiffuse, imageCoord ) * cKernel[ i ];",
|
| 58 |
-
"imageCoord += uImageIncrement;",
|
| 59 |
-
|
| 60 |
-
"}",
|
| 61 |
-
|
| 62 |
-
"gl_FragColor = sum;",
|
| 63 |
-
|
| 64 |
-
"}"
|
| 65 |
-
|
| 66 |
-
|
| 67 |
-
].join("\n"),
|
| 68 |
-
|
| 69 |
-
buildKernel: function ( sigma ) {
|
| 70 |
-
|
| 71 |
-
// We lop off the sqrt(2 * pi) * sigma term, since we're going to normalize anyway.
|
| 72 |
-
|
| 73 |
-
function gauss( x, sigma ) {
|
| 74 |
-
|
| 75 |
-
return Math.exp( - ( x * x ) / ( 2.0 * sigma * sigma ) );
|
| 76 |
-
|
| 77 |
-
}
|
| 78 |
-
|
| 79 |
-
var i, values, sum, halfWidth, kMaxKernelSize = 25, kernelSize = 2 * Math.ceil( sigma * 3.0 ) + 1;
|
| 80 |
-
|
| 81 |
-
if ( kernelSize > kMaxKernelSize ) kernelSize = kMaxKernelSize;
|
| 82 |
-
halfWidth = ( kernelSize - 1 ) * 0.5;
|
| 83 |
-
|
| 84 |
-
values = new Array( kernelSize );
|
| 85 |
-
sum = 0.0;
|
| 86 |
-
for ( i = 0; i < kernelSize; ++i ) {
|
| 87 |
-
|
| 88 |
-
values[ i ] = gauss( i - halfWidth, sigma );
|
| 89 |
-
sum += values[ i ];
|
| 90 |
-
|
| 91 |
-
}
|
| 92 |
-
|
| 93 |
-
// normalize the kernel
|
| 94 |
-
|
| 95 |
-
for ( i = 0; i < kernelSize; ++i ) values[ i ] /= sum;
|
| 96 |
-
|
| 97 |
-
return values;
|
| 98 |
-
|
| 99 |
-
}
|
| 100 |
-
|
| 101 |
-
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
js/postprocessing/CopyShader.js
DELETED
|
@@ -1,46 +0,0 @@
|
|
| 1 |
-
/**
|
| 2 |
-
* @author alteredq / http://alteredqualia.com/
|
| 3 |
-
*
|
| 4 |
-
* Full-screen textured quad shader
|
| 5 |
-
*/
|
| 6 |
-
|
| 7 |
-
THREE.CopyShader = {
|
| 8 |
-
|
| 9 |
-
uniforms: {
|
| 10 |
-
|
| 11 |
-
"tDiffuse": { type: "t", value: null },
|
| 12 |
-
"opacity": { type: "f", value: 1.0 }
|
| 13 |
-
|
| 14 |
-
},
|
| 15 |
-
|
| 16 |
-
vertexShader: [
|
| 17 |
-
|
| 18 |
-
"varying vec2 vUv;",
|
| 19 |
-
|
| 20 |
-
"void main() {",
|
| 21 |
-
|
| 22 |
-
"vUv = uv;",
|
| 23 |
-
"gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",
|
| 24 |
-
|
| 25 |
-
"}"
|
| 26 |
-
|
| 27 |
-
].join("\n"),
|
| 28 |
-
|
| 29 |
-
fragmentShader: [
|
| 30 |
-
|
| 31 |
-
"uniform float opacity;",
|
| 32 |
-
|
| 33 |
-
"uniform sampler2D tDiffuse;",
|
| 34 |
-
|
| 35 |
-
"varying vec2 vUv;",
|
| 36 |
-
|
| 37 |
-
"void main() {",
|
| 38 |
-
|
| 39 |
-
"vec4 texel = texture2D( tDiffuse, vUv );",
|
| 40 |
-
"gl_FragColor = opacity * texel;",
|
| 41 |
-
|
| 42 |
-
"}"
|
| 43 |
-
|
| 44 |
-
].join("\n")
|
| 45 |
-
|
| 46 |
-
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
js/postprocessing/EffectComposer.js
DELETED
|
@@ -1,135 +0,0 @@
|
|
| 1 |
-
/**
|
| 2 |
-
* @author alteredq / http://alteredqualia.com/
|
| 3 |
-
*/
|
| 4 |
-
|
| 5 |
-
THREE.EffectComposer = function ( renderer, renderTarget ) {
|
| 6 |
-
|
| 7 |
-
this.renderer = renderer;
|
| 8 |
-
|
| 9 |
-
if ( renderTarget === undefined ) {
|
| 10 |
-
|
| 11 |
-
var width = window.innerWidth || 1;
|
| 12 |
-
var height = window.innerHeight || 1;
|
| 13 |
-
var parameters = { minFilter: THREE.LinearFilter, magFilter: THREE.LinearFilter, format: THREE.RGBFormat, stencilBuffer: false };
|
| 14 |
-
|
| 15 |
-
renderTarget = new THREE.WebGLRenderTarget( width, height, parameters );
|
| 16 |
-
|
| 17 |
-
}
|
| 18 |
-
|
| 19 |
-
this.renderTarget1 = renderTarget;
|
| 20 |
-
this.renderTarget2 = renderTarget.clone();
|
| 21 |
-
|
| 22 |
-
this.writeBuffer = this.renderTarget1;
|
| 23 |
-
this.readBuffer = this.renderTarget2;
|
| 24 |
-
|
| 25 |
-
this.passes = [];
|
| 26 |
-
|
| 27 |
-
if ( THREE.CopyShader === undefined )
|
| 28 |
-
console.error( "THREE.EffectComposer relies on THREE.CopyShader" );
|
| 29 |
-
|
| 30 |
-
this.copyPass = new THREE.ShaderPass( THREE.CopyShader );
|
| 31 |
-
|
| 32 |
-
};
|
| 33 |
-
|
| 34 |
-
THREE.EffectComposer.prototype = {
|
| 35 |
-
|
| 36 |
-
swapBuffers: function() {
|
| 37 |
-
|
| 38 |
-
var tmp = this.readBuffer;
|
| 39 |
-
this.readBuffer = this.writeBuffer;
|
| 40 |
-
this.writeBuffer = tmp;
|
| 41 |
-
|
| 42 |
-
},
|
| 43 |
-
|
| 44 |
-
addPass: function ( pass ) {
|
| 45 |
-
|
| 46 |
-
this.passes.push( pass );
|
| 47 |
-
|
| 48 |
-
},
|
| 49 |
-
|
| 50 |
-
insertPass: function ( pass, index ) {
|
| 51 |
-
|
| 52 |
-
this.passes.splice( index, 0, pass );
|
| 53 |
-
|
| 54 |
-
},
|
| 55 |
-
|
| 56 |
-
render: function ( delta ) {
|
| 57 |
-
|
| 58 |
-
this.writeBuffer = this.renderTarget1;
|
| 59 |
-
this.readBuffer = this.renderTarget2;
|
| 60 |
-
|
| 61 |
-
var maskActive = false;
|
| 62 |
-
|
| 63 |
-
var pass, i, il = this.passes.length;
|
| 64 |
-
|
| 65 |
-
for ( i = 0; i < il; i ++ ) {
|
| 66 |
-
|
| 67 |
-
pass = this.passes[ i ];
|
| 68 |
-
|
| 69 |
-
if ( !pass.enabled ) continue;
|
| 70 |
-
|
| 71 |
-
pass.render( this.renderer, this.writeBuffer, this.readBuffer, delta, maskActive );
|
| 72 |
-
|
| 73 |
-
if ( pass.needsSwap ) {
|
| 74 |
-
|
| 75 |
-
if ( maskActive ) {
|
| 76 |
-
|
| 77 |
-
var context = this.renderer.context;
|
| 78 |
-
|
| 79 |
-
context.stencilFunc( context.NOTEQUAL, 1, 0xffffffff );
|
| 80 |
-
|
| 81 |
-
this.copyPass.render( this.renderer, this.writeBuffer, this.readBuffer, delta );
|
| 82 |
-
|
| 83 |
-
context.stencilFunc( context.EQUAL, 1, 0xffffffff );
|
| 84 |
-
|
| 85 |
-
}
|
| 86 |
-
|
| 87 |
-
this.swapBuffers();
|
| 88 |
-
|
| 89 |
-
}
|
| 90 |
-
|
| 91 |
-
if ( pass instanceof THREE.MaskPass ) {
|
| 92 |
-
|
| 93 |
-
maskActive = true;
|
| 94 |
-
|
| 95 |
-
} else if ( pass instanceof THREE.ClearMaskPass ) {
|
| 96 |
-
|
| 97 |
-
maskActive = false;
|
| 98 |
-
|
| 99 |
-
}
|
| 100 |
-
|
| 101 |
-
}
|
| 102 |
-
|
| 103 |
-
},
|
| 104 |
-
|
| 105 |
-
reset: function ( renderTarget ) {
|
| 106 |
-
|
| 107 |
-
if ( renderTarget === undefined ) {
|
| 108 |
-
|
| 109 |
-
renderTarget = this.renderTarget1.clone();
|
| 110 |
-
|
| 111 |
-
renderTarget.width = window.innerWidth;
|
| 112 |
-
renderTarget.height = window.innerHeight;
|
| 113 |
-
|
| 114 |
-
}
|
| 115 |
-
|
| 116 |
-
this.renderTarget1 = renderTarget;
|
| 117 |
-
this.renderTarget2 = renderTarget.clone();
|
| 118 |
-
|
| 119 |
-
this.writeBuffer = this.renderTarget1;
|
| 120 |
-
this.readBuffer = this.renderTarget2;
|
| 121 |
-
|
| 122 |
-
},
|
| 123 |
-
|
| 124 |
-
setSize: function ( width, height ) {
|
| 125 |
-
|
| 126 |
-
var renderTarget = this.renderTarget1.clone();
|
| 127 |
-
|
| 128 |
-
renderTarget.width = width;
|
| 129 |
-
renderTarget.height = height;
|
| 130 |
-
|
| 131 |
-
this.reset( renderTarget );
|
| 132 |
-
|
| 133 |
-
}
|
| 134 |
-
|
| 135 |
-
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
js/postprocessing/MaskPass.js
DELETED
|
@@ -1,86 +0,0 @@
|
|
| 1 |
-
/**
|
| 2 |
-
* @author alteredq / http://alteredqualia.com/
|
| 3 |
-
*/
|
| 4 |
-
|
| 5 |
-
THREE.MaskPass = function ( scene, camera ) {
|
| 6 |
-
|
| 7 |
-
this.scene = scene;
|
| 8 |
-
this.camera = camera;
|
| 9 |
-
|
| 10 |
-
this.enabled = true;
|
| 11 |
-
this.clear = true;
|
| 12 |
-
this.needsSwap = false;
|
| 13 |
-
|
| 14 |
-
this.inverse = false;
|
| 15 |
-
|
| 16 |
-
};
|
| 17 |
-
|
| 18 |
-
THREE.MaskPass.prototype = {
|
| 19 |
-
|
| 20 |
-
render: function ( renderer, writeBuffer, readBuffer, delta ) {
|
| 21 |
-
|
| 22 |
-
var context = renderer.context;
|
| 23 |
-
|
| 24 |
-
// don't update color or depth
|
| 25 |
-
|
| 26 |
-
context.colorMask( false, false, false, false );
|
| 27 |
-
context.depthMask( false );
|
| 28 |
-
|
| 29 |
-
// set up stencil
|
| 30 |
-
|
| 31 |
-
var writeValue, clearValue;
|
| 32 |
-
|
| 33 |
-
if ( this.inverse ) {
|
| 34 |
-
|
| 35 |
-
writeValue = 0;
|
| 36 |
-
clearValue = 1;
|
| 37 |
-
|
| 38 |
-
} else {
|
| 39 |
-
|
| 40 |
-
writeValue = 1;
|
| 41 |
-
clearValue = 0;
|
| 42 |
-
|
| 43 |
-
}
|
| 44 |
-
|
| 45 |
-
context.enable( context.STENCIL_TEST );
|
| 46 |
-
context.stencilOp( context.REPLACE, context.REPLACE, context.REPLACE );
|
| 47 |
-
context.stencilFunc( context.ALWAYS, writeValue, 0xffffffff );
|
| 48 |
-
context.clearStencil( clearValue );
|
| 49 |
-
|
| 50 |
-
// draw into the stencil buffer
|
| 51 |
-
|
| 52 |
-
renderer.render( this.scene, this.camera, readBuffer, this.clear );
|
| 53 |
-
renderer.render( this.scene, this.camera, writeBuffer, this.clear );
|
| 54 |
-
|
| 55 |
-
// re-enable update of color and depth
|
| 56 |
-
|
| 57 |
-
context.colorMask( true, true, true, true );
|
| 58 |
-
context.depthMask( true );
|
| 59 |
-
|
| 60 |
-
// only render where stencil is set to 1
|
| 61 |
-
|
| 62 |
-
context.stencilFunc( context.EQUAL, 1, 0xffffffff ); // draw if == 1
|
| 63 |
-
context.stencilOp( context.KEEP, context.KEEP, context.KEEP );
|
| 64 |
-
|
| 65 |
-
}
|
| 66 |
-
|
| 67 |
-
};
|
| 68 |
-
|
| 69 |
-
|
| 70 |
-
THREE.ClearMaskPass = function () {
|
| 71 |
-
|
| 72 |
-
this.enabled = true;
|
| 73 |
-
|
| 74 |
-
};
|
| 75 |
-
|
| 76 |
-
THREE.ClearMaskPass.prototype = {
|
| 77 |
-
|
| 78 |
-
render: function ( renderer, writeBuffer, readBuffer, delta ) {
|
| 79 |
-
|
| 80 |
-
var context = renderer.context;
|
| 81 |
-
|
| 82 |
-
context.disable( context.STENCIL_TEST );
|
| 83 |
-
|
| 84 |
-
}
|
| 85 |
-
|
| 86 |
-
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
js/postprocessing/RenderPass.js
DELETED
|
@@ -1,51 +0,0 @@
|
|
| 1 |
-
/**
|
| 2 |
-
* @author alteredq / http://alteredqualia.com/
|
| 3 |
-
*/
|
| 4 |
-
|
| 5 |
-
THREE.RenderPass = function ( scene, camera, overrideMaterial, clearColor, clearAlpha ) {
|
| 6 |
-
|
| 7 |
-
this.scene = scene;
|
| 8 |
-
this.camera = camera;
|
| 9 |
-
|
| 10 |
-
this.overrideMaterial = overrideMaterial;
|
| 11 |
-
|
| 12 |
-
this.clearColor = clearColor;
|
| 13 |
-
this.clearAlpha = ( clearAlpha !== undefined ) ? clearAlpha : 1;
|
| 14 |
-
|
| 15 |
-
this.oldClearColor = new THREE.Color();
|
| 16 |
-
this.oldClearAlpha = 1;
|
| 17 |
-
|
| 18 |
-
this.enabled = true;
|
| 19 |
-
this.clear = true;
|
| 20 |
-
this.needsSwap = false;
|
| 21 |
-
|
| 22 |
-
};
|
| 23 |
-
|
| 24 |
-
THREE.RenderPass.prototype = {
|
| 25 |
-
|
| 26 |
-
render: function ( renderer, writeBuffer, readBuffer, delta ) {
|
| 27 |
-
|
| 28 |
-
this.scene.overrideMaterial = this.overrideMaterial;
|
| 29 |
-
|
| 30 |
-
if ( this.clearColor ) {
|
| 31 |
-
|
| 32 |
-
this.oldClearColor.copy( renderer.getClearColor() );
|
| 33 |
-
this.oldClearAlpha = renderer.getClearAlpha();
|
| 34 |
-
|
| 35 |
-
renderer.setClearColor( this.clearColor, this.clearAlpha );
|
| 36 |
-
|
| 37 |
-
}
|
| 38 |
-
|
| 39 |
-
renderer.render( this.scene, this.camera, readBuffer, this.clear );
|
| 40 |
-
|
| 41 |
-
if ( this.clearColor ) {
|
| 42 |
-
|
| 43 |
-
renderer.setClearColor( this.oldClearColor, this.oldClearAlpha );
|
| 44 |
-
|
| 45 |
-
}
|
| 46 |
-
|
| 47 |
-
this.scene.overrideMaterial = null;
|
| 48 |
-
|
| 49 |
-
}
|
| 50 |
-
|
| 51 |
-
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
js/postprocessing/ShaderPass.js
DELETED
|
@@ -1,58 +0,0 @@
|
|
| 1 |
-
/**
|
| 2 |
-
* @author alteredq / http://alteredqualia.com/
|
| 3 |
-
*/
|
| 4 |
-
|
| 5 |
-
THREE.ShaderPass = function ( shader, textureID ) {
|
| 6 |
-
|
| 7 |
-
this.textureID = ( textureID !== undefined ) ? textureID : "tDiffuse";
|
| 8 |
-
|
| 9 |
-
this.uniforms = THREE.UniformsUtils.clone( shader.uniforms );
|
| 10 |
-
|
| 11 |
-
this.material = new THREE.ShaderMaterial( {
|
| 12 |
-
|
| 13 |
-
uniforms: this.uniforms,
|
| 14 |
-
vertexShader: shader.vertexShader,
|
| 15 |
-
fragmentShader: shader.fragmentShader
|
| 16 |
-
|
| 17 |
-
} );
|
| 18 |
-
|
| 19 |
-
this.renderToScreen = false;
|
| 20 |
-
|
| 21 |
-
this.enabled = true;
|
| 22 |
-
this.needsSwap = true;
|
| 23 |
-
this.clear = false;
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
this.camera = new THREE.OrthographicCamera( -1, 1, 1, -1, 0, 1 );
|
| 27 |
-
this.scene = new THREE.Scene();
|
| 28 |
-
|
| 29 |
-
this.quad = new THREE.Mesh( new THREE.PlaneBufferGeometry( 2, 2 ), null );
|
| 30 |
-
this.scene.add( this.quad );
|
| 31 |
-
|
| 32 |
-
};
|
| 33 |
-
|
| 34 |
-
THREE.ShaderPass.prototype = {
|
| 35 |
-
|
| 36 |
-
render: function ( renderer, writeBuffer, readBuffer, delta ) {
|
| 37 |
-
|
| 38 |
-
if ( this.uniforms[ this.textureID ] ) {
|
| 39 |
-
|
| 40 |
-
this.uniforms[ this.textureID ].value = readBuffer;
|
| 41 |
-
|
| 42 |
-
}
|
| 43 |
-
|
| 44 |
-
this.quad.material = this.material;
|
| 45 |
-
|
| 46 |
-
if ( this.renderToScreen ) {
|
| 47 |
-
|
| 48 |
-
renderer.render( this.scene, this.camera );
|
| 49 |
-
|
| 50 |
-
} else {
|
| 51 |
-
|
| 52 |
-
renderer.render( this.scene, this.camera, writeBuffer, this.clear );
|
| 53 |
-
|
| 54 |
-
}
|
| 55 |
-
|
| 56 |
-
}
|
| 57 |
-
|
| 58 |
-
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
js/three.min.js
DELETED
|
The diff for this file is too large to render.
See raw diff
|
|
|
package-lock.json
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
package.json
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"name": "interstellar-demo",
|
| 3 |
+
"version": "1.0.0",
|
| 4 |
+
"description": "Interstellar wormhole & blackhole simulator",
|
| 5 |
+
"scripts": {
|
| 6 |
+
"dev": "cross-env NODE_ENV=development webpack-dev-server --open --hot",
|
| 7 |
+
"build": "cross-env NODE_ENV=production webpack --progress --hide-modules"
|
| 8 |
+
},
|
| 9 |
+
"repository": {
|
| 10 |
+
"type": "git",
|
| 11 |
+
"url": "git+https://github.com/sirxemic/Interstellar.git"
|
| 12 |
+
},
|
| 13 |
+
"keywords": [
|
| 14 |
+
"interstellar",
|
| 15 |
+
"demo",
|
| 16 |
+
"black hole",
|
| 17 |
+
"wormhole"
|
| 18 |
+
],
|
| 19 |
+
"author": "Pim Schreurs",
|
| 20 |
+
"license": "MIT",
|
| 21 |
+
"bugs": {
|
| 22 |
+
"url": "https://github.com/sirxemic/Interstellar/issues"
|
| 23 |
+
},
|
| 24 |
+
"homepage": "https://github.com/sirxemic/Interstellar#readme",
|
| 25 |
+
"devDependencies": {
|
| 26 |
+
"babel-core": "^6.26.3",
|
| 27 |
+
"babel-eslint": "^8.2.3",
|
| 28 |
+
"babel-loader": "^7.1.4",
|
| 29 |
+
"babel-preset-env": "^1.7.0",
|
| 30 |
+
"babel-preset-stage-3": "^6.24.1",
|
| 31 |
+
"cross-env": "^5.1.5",
|
| 32 |
+
"eslint": "^4.19.1",
|
| 33 |
+
"eslint-config-standard": "^11.0.0",
|
| 34 |
+
"eslint-loader": "^2.0.0",
|
| 35 |
+
"eslint-plugin-import": "^2.12.0",
|
| 36 |
+
"eslint-plugin-node": "^6.0.1",
|
| 37 |
+
"eslint-plugin-promise": "^3.7.0",
|
| 38 |
+
"eslint-plugin-standard": "^3.1.0",
|
| 39 |
+
"raw-loader": "^0.5.1",
|
| 40 |
+
"three": "^0.92.0",
|
| 41 |
+
"webpack": "^4.8.3",
|
| 42 |
+
"webpack-cli": "^2.1.3",
|
| 43 |
+
"webpack-dev-server": "^3.1.4"
|
| 44 |
+
},
|
| 45 |
+
"browserslist": [
|
| 46 |
+
"> 1%",
|
| 47 |
+
"last 2 versions",
|
| 48 |
+
"not ie <= 8"
|
| 49 |
+
]
|
| 50 |
+
}
|
src/Player.js
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import {
|
| 2 |
+
Object3D,
|
| 3 |
+
PerspectiveCamera,
|
| 4 |
+
Vector3,
|
| 5 |
+
Quaternion,
|
| 6 |
+
Matrix4,
|
| 7 |
+
Ray,
|
| 8 |
+
Sphere
|
| 9 |
+
} from 'three'
|
| 10 |
+
|
| 11 |
+
import Simulation from './Simulation'
|
| 12 |
+
|
| 13 |
+
// For improved performance, we initialize certain variables only once instead of in the step function
|
| 14 |
+
const __prevPosition = new Vector3()
|
| 15 |
+
const __newVelocity = new Vector3()
|
| 16 |
+
const __acceleration = new Vector3()
|
| 17 |
+
const __gravityVector = new Vector3()
|
| 18 |
+
const __direction = new Vector3()
|
| 19 |
+
const __intersection = new Vector3()
|
| 20 |
+
const __axis = new Vector3()
|
| 21 |
+
const __rotation = new Quaternion()
|
| 22 |
+
const __temp = new Vector3()
|
| 23 |
+
const __ray = new Ray()
|
| 24 |
+
|
| 25 |
+
export default class Player {
|
| 26 |
+
|
| 27 |
+
constructor () {
|
| 28 |
+
this.object = new Object3D()
|
| 29 |
+
this.eyes = new PerspectiveCamera()
|
| 30 |
+
this.object.add(this.eyes)
|
| 31 |
+
|
| 32 |
+
this.velocity = new Vector3()
|
| 33 |
+
this.eyeAngularVelocity = new Vector3()
|
| 34 |
+
|
| 35 |
+
this.galaxy = 0
|
| 36 |
+
|
| 37 |
+
this.controllers = []
|
| 38 |
+
}
|
| 39 |
+
|
| 40 |
+
lookAt (position) {
|
| 41 |
+
// this.object.lookAt makes it look in the exact opposite __direction, for some reason
|
| 42 |
+
const lookAtMatrix = new Matrix4()
|
| 43 |
+
lookAtMatrix.lookAt(this.object.position, position, this.object.up)
|
| 44 |
+
this.object.quaternion.setFromRotationMatrix(lookAtMatrix)
|
| 45 |
+
this.object.quaternion.multiply(this.eyes.quaternion.clone().inverse())
|
| 46 |
+
}
|
| 47 |
+
|
| 48 |
+
addController (controller) {
|
| 49 |
+
this.controllers.push(controller)
|
| 50 |
+
}
|
| 51 |
+
|
| 52 |
+
handleInput () {
|
| 53 |
+
for (let i = 0; i < this.controllers.length; i++) {
|
| 54 |
+
this.controllers[i].update()
|
| 55 |
+
}
|
| 56 |
+
}
|
| 57 |
+
|
| 58 |
+
step (delta) {
|
| 59 |
+
const wormhole = Simulation.config.wormhole
|
| 60 |
+
const wormholeSphere = new Sphere(wormhole.position, wormhole.radius)
|
| 61 |
+
|
| 62 |
+
if (this.velocity.lengthSq() > 0.00001) {
|
| 63 |
+
__prevPosition.copy(this.object.position)
|
| 64 |
+
|
| 65 |
+
// 1. Compute wormhole curvature/gravity.
|
| 66 |
+
__gravityVector.subVectors(wormhole.position, __prevPosition)
|
| 67 |
+
const rayDistance = __gravityVector.length() - wormhole.radius * (1 - wormhole.gravityRatio)
|
| 68 |
+
const amount = wormhole.gravityRatio / rayDistance
|
| 69 |
+
__acceleration.copy(__gravityVector.normalize()).multiplyScalar(wormhole.radius * this.velocity.lengthSq() * amount * amount)
|
| 70 |
+
|
| 71 |
+
// Apply curvature to velocity
|
| 72 |
+
__newVelocity.copy(this.velocity).add(__acceleration.multiplyScalar(delta))
|
| 73 |
+
|
| 74 |
+
// Adjust new velocity (keep magnitude of old velocity)
|
| 75 |
+
__newVelocity.normalize().multiplyScalar(this.velocity.length())
|
| 76 |
+
|
| 77 |
+
// Update the player's position and orientation accordingly
|
| 78 |
+
this.object.position.addVectors(__prevPosition, __newVelocity.multiplyScalar(delta))
|
| 79 |
+
this.object.quaternion.multiplyQuaternions(
|
| 80 |
+
__rotation.setFromUnitVectors(this.velocity.normalize(), __newVelocity.normalize()),
|
| 81 |
+
this.object.quaternion
|
| 82 |
+
)
|
| 83 |
+
|
| 84 |
+
this.velocity.copy(__newVelocity)
|
| 85 |
+
|
| 86 |
+
// 2. Check if we're going through the wormhole
|
| 87 |
+
__direction.copy(this.velocity).normalize()
|
| 88 |
+
|
| 89 |
+
__ray.set(__prevPosition, __direction)
|
| 90 |
+
|
| 91 |
+
const distanceTravelledSq = __direction.subVectors(this.object.position, __prevPosition).lengthSq()
|
| 92 |
+
|
| 93 |
+
const at = __ray.intersectSphere(wormholeSphere, __intersection)
|
| 94 |
+
if (at && at.distanceToSquared(__prevPosition) <= distanceTravelledSq) {
|
| 95 |
+
// Rotate 180 degrees around __axis pointing at exit point
|
| 96 |
+
__axis.subVectors(__intersection, wormhole.position).normalize()
|
| 97 |
+
__rotation.setFromAxisAngle(__axis, Math.PI)
|
| 98 |
+
this.object.quaternion.multiplyQuaternions(__rotation, this.object.quaternion)
|
| 99 |
+
this.velocity.reflect(__axis).multiplyScalar(-1)
|
| 100 |
+
|
| 101 |
+
// Set new position a tiny bit outside mirrored __intersection point
|
| 102 |
+
this.object.position
|
| 103 |
+
.copy(wormhole.position)
|
| 104 |
+
.add(
|
| 105 |
+
__temp.subVectors(wormhole.position, __intersection)
|
| 106 |
+
.multiplyScalar(1.0001)
|
| 107 |
+
)
|
| 108 |
+
|
| 109 |
+
this.galaxy = 1 - this.galaxy
|
| 110 |
+
}
|
| 111 |
+
}
|
| 112 |
+
|
| 113 |
+
__rotation.set(
|
| 114 |
+
this.eyeAngularVelocity.x * delta,
|
| 115 |
+
this.eyeAngularVelocity.y * delta,
|
| 116 |
+
this.eyeAngularVelocity.z * delta,
|
| 117 |
+
1
|
| 118 |
+
).normalize()
|
| 119 |
+
this.eyes.quaternion.multiply(__rotation)
|
| 120 |
+
}
|
| 121 |
+
|
| 122 |
+
update (delta) {
|
| 123 |
+
this.handleInput()
|
| 124 |
+
this.step(delta)
|
| 125 |
+
|
| 126 |
+
// Object isn't actually part of a rendered scene, so we need to call this manually
|
| 127 |
+
this.object.updateMatrixWorld(true)
|
| 128 |
+
}
|
| 129 |
+
|
| 130 |
+
}
|
src/Simulation.js
ADDED
|
@@ -0,0 +1,192 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import {
|
| 2 |
+
Vector3,
|
| 3 |
+
Vector4,
|
| 4 |
+
Clock
|
| 5 |
+
} from 'three'
|
| 6 |
+
|
| 7 |
+
import Ui from './Ui'
|
| 8 |
+
|
| 9 |
+
import Player from './Player'
|
| 10 |
+
import KeyboardControls from './controls/KeyboardControls'
|
| 11 |
+
import MobileDeviceControls from './controls/MobileDeviceControls'
|
| 12 |
+
import ControlsManager from './controls/ControlsManager'
|
| 13 |
+
|
| 14 |
+
import SimulationRenderer from './SimulationRenderer'
|
| 15 |
+
import Teleporter from './Teleporter'
|
| 16 |
+
|
| 17 |
+
class Simulation {
|
| 18 |
+
|
| 19 |
+
init () {
|
| 20 |
+
this.config = {
|
| 21 |
+
wormhole: {
|
| 22 |
+
position: new Vector3(10, 0.0, -32),
|
| 23 |
+
radius: 0.8,
|
| 24 |
+
gravityRatio: 0.25
|
| 25 |
+
},
|
| 26 |
+
|
| 27 |
+
blackhole: {
|
| 28 |
+
position: new Vector3(0.0, -250.0, 250.0),
|
| 29 |
+
radius: 12.5,
|
| 30 |
+
|
| 31 |
+
// Ring definition - xyz is normal going through ring. Its magnitude determines inner radius.
|
| 32 |
+
// w component determines outer radius
|
| 33 |
+
disk: new Vector4(-12, 12, 6, 150.0),
|
| 34 |
+
diskTexture: 'assets/accretion_disk.png'
|
| 35 |
+
},
|
| 36 |
+
|
| 37 |
+
saturn: {
|
| 38 |
+
position: new Vector3(-14, 5, -40),
|
| 39 |
+
radius: 8.0,
|
| 40 |
+
|
| 41 |
+
// Ring definition - xyz is normal going through ring. Its magnitude determines inner radius.
|
| 42 |
+
// w component determines outer radius
|
| 43 |
+
rings: new Vector4(0, 9.22, 0, 17.1),
|
| 44 |
+
texture: 'assets/saturn.jpg',
|
| 45 |
+
ringsTexture: 'assets/saturnrings.png',
|
| 46 |
+
lightDirection: (new Vector3(-4, 2, 3)).normalize()
|
| 47 |
+
},
|
| 48 |
+
|
| 49 |
+
planet: {
|
| 50 |
+
position: new Vector3(7.6, -188.0, 200),
|
| 51 |
+
radius: 0.08,
|
| 52 |
+
diffuse: new Vector3(0.58, 0.85, 0.96),
|
| 53 |
+
specular: new Vector3(0.1, 0.1, 0.1)
|
| 54 |
+
},
|
| 55 |
+
|
| 56 |
+
galaxy1: { texture: 'assets/galaxy1.png' },
|
| 57 |
+
galaxy2: { texture: 'assets/galaxy2.png' }
|
| 58 |
+
}
|
| 59 |
+
|
| 60 |
+
this.teleportTargets = [
|
| 61 |
+
{ position: new Vector3(10, -307, 454), lookAt: this.config.blackhole.position, galaxy: 1 },
|
| 62 |
+
{ position: new Vector3(7.2, -188, 199.6), lookAt: this.config.planet.position, galaxy: 1 },
|
| 63 |
+
{ position: new Vector3(12.4, 3.3, -35.1), lookAt: this.config.wormhole.position, galaxy: 1 },
|
| 64 |
+
{ position: new Vector3(9.8, -4.6, -3.1), lookAt: this.config.wormhole.position, galaxy: 0 }
|
| 65 |
+
]
|
| 66 |
+
|
| 67 |
+
this.initPlayer()
|
| 68 |
+
this.initTeleporter()
|
| 69 |
+
this.initRenderer()
|
| 70 |
+
this.initControls()
|
| 71 |
+
}
|
| 72 |
+
|
| 73 |
+
initRenderer () {
|
| 74 |
+
this.renderer = new SimulationRenderer(this.config, this.player)
|
| 75 |
+
this.renderer.onTexturesLoaded = () => {
|
| 76 |
+
Ui.removeLoadingScreen()
|
| 77 |
+
this.inited = true
|
| 78 |
+
}
|
| 79 |
+
|
| 80 |
+
this.container = document.getElementById('container')
|
| 81 |
+
this.container.appendChild(this.renderer.domElement)
|
| 82 |
+
|
| 83 |
+
Ui.onPixelSizeChange = pixelSize => {
|
| 84 |
+
this.renderer.setPixelSize(pixelSize)
|
| 85 |
+
}
|
| 86 |
+
|
| 87 |
+
window.addEventListener(
|
| 88 |
+
'resize', e => {
|
| 89 |
+
this.renderer.setSize(window.innerWidth, window.innerHeight)
|
| 90 |
+
},
|
| 91 |
+
false
|
| 92 |
+
)
|
| 93 |
+
|
| 94 |
+
this.renderer.setSize(window.innerWidth, window.innerHeight, Ui.getSelectedPixelSize())
|
| 95 |
+
|
| 96 |
+
window.addEventListener('wheel', e => {
|
| 97 |
+
e.preventDefault()
|
| 98 |
+
|
| 99 |
+
const delta = e.delta || (e.deltaX + e.deltaY + e.deltaZ)
|
| 100 |
+
if (delta < 0) {
|
| 101 |
+
this.renderer.setZoom(this.renderer.zoom * 1.06)
|
| 102 |
+
}
|
| 103 |
+
else {
|
| 104 |
+
this.renderer.setZoom(this.renderer.zoom / 1.06)
|
| 105 |
+
}
|
| 106 |
+
}, false)
|
| 107 |
+
}
|
| 108 |
+
|
| 109 |
+
initPlayer () {
|
| 110 |
+
this.player = new Player()
|
| 111 |
+
this.player.lookAt(this.config.wormhole.position)
|
| 112 |
+
}
|
| 113 |
+
|
| 114 |
+
initControls () {
|
| 115 |
+
// Add keyboard controls to the player
|
| 116 |
+
this.keyboardControls = new KeyboardControls(this.player, this.container)
|
| 117 |
+
this.keyboardControls.movementSpeed = 1
|
| 118 |
+
this.keyboardControls.rollSpeed = Math.PI / 3
|
| 119 |
+
this.keyboardControls.autoForward = false
|
| 120 |
+
this.keyboardControls.dragToLook = false
|
| 121 |
+
|
| 122 |
+
// Add mobile device controls (touch + accelerometer) to the player
|
| 123 |
+
this.mobileDeviceControls = new MobileDeviceControls(this.player, this.container)
|
| 124 |
+
|
| 125 |
+
this.controlsManager = new ControlsManager(this.keyboardControls, this.mobileDeviceControls)
|
| 126 |
+
this.controlsManager.onChange = device => {
|
| 127 |
+
if (device !== 'mobile') {
|
| 128 |
+
return
|
| 129 |
+
}
|
| 130 |
+
|
| 131 |
+
// The player will probably not be looking with their device in the right direction, so fix that
|
| 132 |
+
requestAnimationFrame(() => {
|
| 133 |
+
this.player.object.quaternion.multiply(this.player.eyes.quaternion.clone().inverse())
|
| 134 |
+
})
|
| 135 |
+
}
|
| 136 |
+
|
| 137 |
+
this.player.addController(this.keyboardControls)
|
| 138 |
+
this.player.addController(this.mobileDeviceControls)
|
| 139 |
+
}
|
| 140 |
+
|
| 141 |
+
initTeleporter () {
|
| 142 |
+
this.teleporter = new Teleporter(this.player)
|
| 143 |
+
this.teleportTargets.forEach(target => {
|
| 144 |
+
this.teleporter.addTarget(target)
|
| 145 |
+
})
|
| 146 |
+
|
| 147 |
+
Ui.onTeleportClick = () => {
|
| 148 |
+
this.teleporter.teleportNext()
|
| 149 |
+
}
|
| 150 |
+
|
| 151 |
+
document.addEventListener('keydown', e => {
|
| 152 |
+
if (e.keyCode === 84) {
|
| 153 |
+
e.preventDefault()
|
| 154 |
+
|
| 155 |
+
this.teleporter.teleportNext()
|
| 156 |
+
}
|
| 157 |
+
})
|
| 158 |
+
}
|
| 159 |
+
|
| 160 |
+
step () {
|
| 161 |
+
if (this.inited) {
|
| 162 |
+
this.update()
|
| 163 |
+
}
|
| 164 |
+
|
| 165 |
+
this.render()
|
| 166 |
+
}
|
| 167 |
+
|
| 168 |
+
start () {
|
| 169 |
+
this.clock = new Clock()
|
| 170 |
+
|
| 171 |
+
const animate = () => {
|
| 172 |
+
requestAnimationFrame(animate)
|
| 173 |
+
|
| 174 |
+
this.step()
|
| 175 |
+
}
|
| 176 |
+
|
| 177 |
+
animate()
|
| 178 |
+
}
|
| 179 |
+
|
| 180 |
+
update () {
|
| 181 |
+
const delta = Math.max(0.001, this.clock.getDelta())
|
| 182 |
+
|
| 183 |
+
this.player.update(delta)
|
| 184 |
+
}
|
| 185 |
+
|
| 186 |
+
render () {
|
| 187 |
+
this.renderer.render()
|
| 188 |
+
}
|
| 189 |
+
|
| 190 |
+
}
|
| 191 |
+
|
| 192 |
+
export default new Simulation()
|
src/SimulationRenderer.js
ADDED
|
@@ -0,0 +1,231 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import vertexShader from './shaders/wormholeVertex.glsl'
|
| 2 |
+
import fragmentShader from './shaders/wormholeFragment.glsl'
|
| 3 |
+
|
| 4 |
+
import {
|
| 5 |
+
Math as MathExtra,
|
| 6 |
+
Scene,
|
| 7 |
+
OrthographicCamera,
|
| 8 |
+
Mesh,
|
| 9 |
+
PlaneBufferGeometry,
|
| 10 |
+
ShaderMaterial,
|
| 11 |
+
|
| 12 |
+
Vector3,
|
| 13 |
+
Vector4,
|
| 14 |
+
Quaternion,
|
| 15 |
+
Matrix4,
|
| 16 |
+
|
| 17 |
+
RepeatWrapping,
|
| 18 |
+
LinearFilter,
|
| 19 |
+
|
| 20 |
+
WebGLRenderer,
|
| 21 |
+
TextureLoader
|
| 22 |
+
} from 'three'
|
| 23 |
+
|
| 24 |
+
import EffectComposer from './postprocessing/EffectComposer'
|
| 25 |
+
import RenderPass from './postprocessing/RenderPass'
|
| 26 |
+
import BloomPass from './postprocessing/UnrealBloomPass'
|
| 27 |
+
|
| 28 |
+
const textureLoader = new TextureLoader()
|
| 29 |
+
|
| 30 |
+
export default class SimulationRenderer {
|
| 31 |
+
|
| 32 |
+
constructor (config, player) {
|
| 33 |
+
this.loadConfig(config)
|
| 34 |
+
|
| 35 |
+
this.player = player
|
| 36 |
+
|
| 37 |
+
this.zoom = 1
|
| 38 |
+
this.pixelSize = 4
|
| 39 |
+
this.width = window.innerWidth
|
| 40 |
+
this.height = window.innerHeight
|
| 41 |
+
|
| 42 |
+
this.renderer = new WebGLRenderer()
|
| 43 |
+
this.renderer.setSize(this.width, this.height)
|
| 44 |
+
this.renderer.sortObjects = false
|
| 45 |
+
this.renderer.autoClear = false
|
| 46 |
+
this.domElement = this.renderer.domElement
|
| 47 |
+
|
| 48 |
+
const material = new ShaderMaterial(
|
| 49 |
+
{
|
| 50 |
+
uniforms: this.uniforms,
|
| 51 |
+
|
| 52 |
+
vertexShader,
|
| 53 |
+
fragmentShader,
|
| 54 |
+
|
| 55 |
+
extensions: {
|
| 56 |
+
shaderTextureLOD: true
|
| 57 |
+
}
|
| 58 |
+
}
|
| 59 |
+
)
|
| 60 |
+
|
| 61 |
+
this.scene = new Scene()
|
| 62 |
+
this.camera = new OrthographicCamera(-1, 1, 1, -1, 0, 1)
|
| 63 |
+
|
| 64 |
+
this.quad = new Mesh(new PlaneBufferGeometry(2, 2), material)
|
| 65 |
+
this.quad.frustumCulled = false
|
| 66 |
+
this.scene.add(this.quad)
|
| 67 |
+
|
| 68 |
+
this.renderPasses = [
|
| 69 |
+
new RenderPass(this.scene, this.camera),
|
| 70 |
+
new BloomPass(1024, 2.7, 0.7, 0.8)
|
| 71 |
+
]
|
| 72 |
+
|
| 73 |
+
this.renderPasses[this.renderPasses.length - 1].renderToScreen = true
|
| 74 |
+
|
| 75 |
+
this.composer = new EffectComposer(this.renderer)
|
| 76 |
+
|
| 77 |
+
this.renderPasses.forEach(pass => {
|
| 78 |
+
this.composer.addPass(pass)
|
| 79 |
+
})
|
| 80 |
+
}
|
| 81 |
+
|
| 82 |
+
loadConfig (config) {
|
| 83 |
+
this.wormholePositionSize = new Vector4(
|
| 84 |
+
config.wormhole.position.x,
|
| 85 |
+
config.wormhole.position.y,
|
| 86 |
+
config.wormhole.position.z,
|
| 87 |
+
config.wormhole.radius
|
| 88 |
+
)
|
| 89 |
+
|
| 90 |
+
this.blackholePositionSize = new Vector4(
|
| 91 |
+
config.blackhole.position.x,
|
| 92 |
+
config.blackhole.position.y,
|
| 93 |
+
config.blackhole.position.z,
|
| 94 |
+
config.blackhole.radius
|
| 95 |
+
)
|
| 96 |
+
|
| 97 |
+
this.saturnPositionSize = new Vector4(
|
| 98 |
+
config.saturn.position.x,
|
| 99 |
+
config.saturn.position.y,
|
| 100 |
+
config.saturn.position.z,
|
| 101 |
+
config.saturn.radius
|
| 102 |
+
)
|
| 103 |
+
|
| 104 |
+
this.planetPositionSize = new Vector4(
|
| 105 |
+
config.planet.position.x,
|
| 106 |
+
config.planet.position.y,
|
| 107 |
+
config.planet.position.z,
|
| 108 |
+
config.planet.radius
|
| 109 |
+
)
|
| 110 |
+
|
| 111 |
+
this.blackholeDisk = config.blackhole.disk
|
| 112 |
+
this.saturnRings = config.saturn.rings
|
| 113 |
+
|
| 114 |
+
this.wormholeGravityRatio = config.wormhole.gravityRatio
|
| 115 |
+
|
| 116 |
+
this.uniforms = {
|
| 117 |
+
wormhole: { type: 'v4', value: this.wormholePositionSize },
|
| 118 |
+
wormholeGravityRatio: { type: 'f', value: this.wormholeGravityRatio },
|
| 119 |
+
blackhole: { type: 'v4', value: this.blackholePositionSize },
|
| 120 |
+
|
| 121 |
+
saturn: { type: 'v4', value: this.saturnPositionSize },
|
| 122 |
+
planet: { type: 'v4', value: this.planetPositionSize },
|
| 123 |
+
|
| 124 |
+
blackholeDisk: { type: 'v4', value: this.blackholeDisk },
|
| 125 |
+
saturnRings: { type: 'v4', value: this.saturnRings },
|
| 126 |
+
|
| 127 |
+
planetDiffuse: { type: 'v3', value: config.planet.diffuse },
|
| 128 |
+
planetSpecular: { type: 'v3', value: config.planet.specular },
|
| 129 |
+
texSaturn: { type: 't', value: this.loadTexture(config.saturn.texture) },
|
| 130 |
+
texSaturnRings: { type: 't', value: this.loadTexture(config.saturn.ringsTexture) },
|
| 131 |
+
texGalaxy1: { type: 't', value: this.loadTexture(config.galaxy1.texture) },
|
| 132 |
+
texGalaxy2: { type: 't', value: this.loadTexture(config.galaxy2.texture) },
|
| 133 |
+
texAccretionDisk: { type: 't', value: this.loadTexture(config.blackhole.diskTexture) },
|
| 134 |
+
|
| 135 |
+
lightDirection: { type: 'v3', value: config.saturn.lightDirection },
|
| 136 |
+
|
| 137 |
+
cameraMatrix: { type: 'm4', value: new Matrix4() },
|
| 138 |
+
cameraGalaxy: { type: 'i', value: 0 }
|
| 139 |
+
}
|
| 140 |
+
|
| 141 |
+
this.uniforms.texSaturnRings.value.wrapS = RepeatWrapping
|
| 142 |
+
this.uniforms.texAccretionDisk.value.wrapS = RepeatWrapping
|
| 143 |
+
|
| 144 |
+
this.uniforms.texSaturn.value.minFilter = LinearFilter
|
| 145 |
+
this.uniforms.texGalaxy1.value.minFilter = LinearFilter
|
| 146 |
+
this.uniforms.texGalaxy2.value.minFilter = LinearFilter
|
| 147 |
+
}
|
| 148 |
+
|
| 149 |
+
loadTexture (path) {
|
| 150 |
+
this._textureCount = (this._textureCount || 0) + 1
|
| 151 |
+
|
| 152 |
+
return textureLoader.load(path, () => {
|
| 153 |
+
this._loadedTexturesCount = (this._loadedTexturesCount || 0) + 1
|
| 154 |
+
|
| 155 |
+
if (this._loadedTexturesCount === this._textureCount) {
|
| 156 |
+
this.onTexturesLoaded && this.onTexturesLoaded()
|
| 157 |
+
}
|
| 158 |
+
})
|
| 159 |
+
}
|
| 160 |
+
|
| 161 |
+
updateEffectComposer () {
|
| 162 |
+
this.composer.setSize(
|
| 163 |
+
Math.floor(this.width / this.pixelSize),
|
| 164 |
+
Math.floor(this.height / this.pixelSize)
|
| 165 |
+
)
|
| 166 |
+
}
|
| 167 |
+
|
| 168 |
+
updateCamera () {
|
| 169 |
+
let vx, vy
|
| 170 |
+
if (this.width > this.height) {
|
| 171 |
+
vx = 1
|
| 172 |
+
vy = this.height / this.width
|
| 173 |
+
}
|
| 174 |
+
else {
|
| 175 |
+
vx = this.width / this.height
|
| 176 |
+
vy = 1
|
| 177 |
+
}
|
| 178 |
+
|
| 179 |
+
this.player.eyes.aspect = vx / vy
|
| 180 |
+
this.player.eyes.fov = MathExtra.RAD2DEG * 2 * Math.atan(vy / this.zoom)
|
| 181 |
+
}
|
| 182 |
+
|
| 183 |
+
setPixelSize (pixelSize) {
|
| 184 |
+
this.pixelSize = pixelSize
|
| 185 |
+
|
| 186 |
+
this.updateEffectComposer()
|
| 187 |
+
}
|
| 188 |
+
|
| 189 |
+
setZoom (zoom) {
|
| 190 |
+
this.zoom = zoom
|
| 191 |
+
|
| 192 |
+
this.updateCamera()
|
| 193 |
+
}
|
| 194 |
+
|
| 195 |
+
setSize (width, height, pixelSize = null) {
|
| 196 |
+
this.width = width
|
| 197 |
+
this.height = height
|
| 198 |
+
|
| 199 |
+
if (pixelSize) {
|
| 200 |
+
this.pixelSize = pixelSize
|
| 201 |
+
}
|
| 202 |
+
|
| 203 |
+
this.renderer.setSize(this.width, this.height)
|
| 204 |
+
|
| 205 |
+
this.updateCamera()
|
| 206 |
+
this.updateEffectComposer()
|
| 207 |
+
}
|
| 208 |
+
|
| 209 |
+
render () {
|
| 210 |
+
const rayScale = new Vector3(
|
| 211 |
+
this.player.eyes.aspect,
|
| 212 |
+
1,
|
| 213 |
+
1 / Math.tan(MathExtra.DEG2RAD * this.player.eyes.fov / 2)
|
| 214 |
+
)
|
| 215 |
+
|
| 216 |
+
const worldPosition = new Vector3()
|
| 217 |
+
const worldQuaternion = new Quaternion()
|
| 218 |
+
|
| 219 |
+
this.uniforms.cameraMatrix.value.compose(
|
| 220 |
+
this.player.eyes.getWorldPosition(worldPosition),
|
| 221 |
+
this.player.eyes.getWorldQuaternion(worldQuaternion),
|
| 222 |
+
rayScale
|
| 223 |
+
)
|
| 224 |
+
|
| 225 |
+
this.uniforms.cameraGalaxy.value = this.player.galaxy
|
| 226 |
+
|
| 227 |
+
this.renderer.clear()
|
| 228 |
+
this.composer.render()
|
| 229 |
+
}
|
| 230 |
+
|
| 231 |
+
}
|
src/Teleporter.js
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import Simulation from './Simulation'
|
| 2 |
+
|
| 3 |
+
export default class Teleporter {
|
| 4 |
+
|
| 5 |
+
constructor (player) {
|
| 6 |
+
this.player = player
|
| 7 |
+
this.targets = []
|
| 8 |
+
}
|
| 9 |
+
|
| 10 |
+
addTarget ({ position, lookAt, galaxy }) {
|
| 11 |
+
this.targets.push({
|
| 12 |
+
position,
|
| 13 |
+
lookAt,
|
| 14 |
+
galaxy
|
| 15 |
+
})
|
| 16 |
+
}
|
| 17 |
+
|
| 18 |
+
getClosestTeleportIndex () {
|
| 19 |
+
let minDistance = Infinity
|
| 20 |
+
let result = null
|
| 21 |
+
|
| 22 |
+
for (let i = 0; i < this.targets.length; i++) {
|
| 23 |
+
let distance
|
| 24 |
+
|
| 25 |
+
if (this.targets[i].galaxy !== this.player.galaxy) {
|
| 26 |
+
distance =
|
| 27 |
+
this.player.object.position.distanceTo(Simulation.config.wormhole.position) +
|
| 28 |
+
this.targets[i].position.distanceTo(Simulation.config.wormhole.position)
|
| 29 |
+
}
|
| 30 |
+
else {
|
| 31 |
+
distance = this.player.object.position.distanceTo(this.targets[i].position)
|
| 32 |
+
}
|
| 33 |
+
|
| 34 |
+
if (distance < minDistance) {
|
| 35 |
+
minDistance = distance
|
| 36 |
+
result = i
|
| 37 |
+
}
|
| 38 |
+
}
|
| 39 |
+
return result
|
| 40 |
+
}
|
| 41 |
+
|
| 42 |
+
teleportNext () {
|
| 43 |
+
const nextIndex = (this.getClosestTeleportIndex() + 1) % this.targets.length
|
| 44 |
+
|
| 45 |
+
this.teleportTo(this.targets[nextIndex])
|
| 46 |
+
}
|
| 47 |
+
|
| 48 |
+
teleportTo (target) {
|
| 49 |
+
this.player.object.position.copy(target.position)
|
| 50 |
+
this.player.galaxy = target.galaxy
|
| 51 |
+
|
| 52 |
+
this.player.lookAt(target.lookAt)
|
| 53 |
+
}
|
| 54 |
+
|
| 55 |
+
}
|
src/Ui.js
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
class UiController {
|
| 2 |
+
|
| 3 |
+
constructor () {
|
| 4 |
+
this.initUiToggle()
|
| 5 |
+
this.initTeleportButton()
|
| 6 |
+
this.initRadioButtons()
|
| 7 |
+
}
|
| 8 |
+
|
| 9 |
+
initUiToggle () {
|
| 10 |
+
const uiToggle = document.querySelector('.ui-toggle input')
|
| 11 |
+
|
| 12 |
+
const onUIToggle = () => {
|
| 13 |
+
if (uiToggle.checked) {
|
| 14 |
+
document.body.classList.add('no-ui')
|
| 15 |
+
}
|
| 16 |
+
else {
|
| 17 |
+
document.body.classList.remove('no-ui')
|
| 18 |
+
}
|
| 19 |
+
uiToggle.blur()
|
| 20 |
+
}
|
| 21 |
+
|
| 22 |
+
uiToggle.addEventListener('change', onUIToggle)
|
| 23 |
+
|
| 24 |
+
onUIToggle()
|
| 25 |
+
}
|
| 26 |
+
|
| 27 |
+
initTeleportButton () {
|
| 28 |
+
document.querySelector('#teleport')
|
| 29 |
+
.addEventListener('touchstart', () => {
|
| 30 |
+
this.onTeleportClick && this.onTeleportClick()
|
| 31 |
+
}, false)
|
| 32 |
+
}
|
| 33 |
+
|
| 34 |
+
initRadioButtons () {
|
| 35 |
+
document.querySelector('#resolution')
|
| 36 |
+
.addEventListener('change', event => {
|
| 37 |
+
event.target.blur()
|
| 38 |
+
|
| 39 |
+
const pixelSize = this.getSelectedPixelSize()
|
| 40 |
+
this.onPixelSizeChange && this.onPixelSizeChange(pixelSize)
|
| 41 |
+
}, false)
|
| 42 |
+
}
|
| 43 |
+
|
| 44 |
+
getSelectedPixelSize () {
|
| 45 |
+
return parseInt(document.querySelector('[name=resolution]:checked').value)
|
| 46 |
+
}
|
| 47 |
+
|
| 48 |
+
showWebGLError () {
|
| 49 |
+
document.querySelector('#webgl-error').style.display = 'block'
|
| 50 |
+
this.removeLoadingScreen()
|
| 51 |
+
}
|
| 52 |
+
|
| 53 |
+
removeLoadingScreen () {
|
| 54 |
+
const el = document.getElementById('loading')
|
| 55 |
+
el.parentElement.removeChild(el)
|
| 56 |
+
}
|
| 57 |
+
|
| 58 |
+
setUiForDesktop () {
|
| 59 |
+
document.body.classList.remove('mobile-device')
|
| 60 |
+
}
|
| 61 |
+
|
| 62 |
+
setUiForMobile () {
|
| 63 |
+
document.body.classList.add('mobile-device')
|
| 64 |
+
}
|
| 65 |
+
|
| 66 |
+
}
|
| 67 |
+
|
| 68 |
+
export default new UiController()
|
src/controls/ControlsBase.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
export default class ControlsBase {
|
| 2 |
+
|
| 3 |
+
constructor (player, element) {
|
| 4 |
+
this.player = player
|
| 5 |
+
this.element = element
|
| 6 |
+
|
| 7 |
+
this.enabled = false
|
| 8 |
+
}
|
| 9 |
+
|
| 10 |
+
}
|
src/controls/ControlsManager.js
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import Ui from '../Ui'
|
| 2 |
+
|
| 3 |
+
export default class ControlsManager {
|
| 4 |
+
|
| 5 |
+
constructor (keyboardControls, mobileDeviceControls) {
|
| 6 |
+
this.keyboardControls = keyboardControls
|
| 7 |
+
this.mobileDeviceControls = mobileDeviceControls
|
| 8 |
+
|
| 9 |
+
this.onKeyPress = this.onKeyPress.bind(this)
|
| 10 |
+
this.onDeviceOrientation = this.onDeviceOrientation.bind(this)
|
| 11 |
+
|
| 12 |
+
this.setDesktop()
|
| 13 |
+
}
|
| 14 |
+
|
| 15 |
+
setDesktop () {
|
| 16 |
+
window.addEventListener('deviceorientation', this.onDeviceOrientation, false)
|
| 17 |
+
|
| 18 |
+
this.keyboardControls.enable()
|
| 19 |
+
this.mobileDeviceControls.disable()
|
| 20 |
+
|
| 21 |
+
Ui.setUiForDesktop()
|
| 22 |
+
|
| 23 |
+
this.onChange && this.onChange('desktop')
|
| 24 |
+
}
|
| 25 |
+
|
| 26 |
+
setMobile () {
|
| 27 |
+
this.controls = 'mobile'
|
| 28 |
+
|
| 29 |
+
window.addEventListener('keypress', this.onKeyPress, false)
|
| 30 |
+
window.removeEventListener('deviceorientation', this.onDeviceOrientation, false)
|
| 31 |
+
|
| 32 |
+
this.keyboardControls.disable()
|
| 33 |
+
this.mobileDeviceControls.enable()
|
| 34 |
+
|
| 35 |
+
Ui.setUiForMobile()
|
| 36 |
+
|
| 37 |
+
this.onChange && this.onChange('mobile')
|
| 38 |
+
}
|
| 39 |
+
|
| 40 |
+
onKeyPress (event) {
|
| 41 |
+
this.setDesktop()
|
| 42 |
+
}
|
| 43 |
+
|
| 44 |
+
onDeviceOrientation (event) {
|
| 45 |
+
if (event.alpha === null) {
|
| 46 |
+
return
|
| 47 |
+
}
|
| 48 |
+
|
| 49 |
+
this.setMobile()
|
| 50 |
+
}
|
| 51 |
+
|
| 52 |
+
}
|
src/controls/DeviceOrientationControls.js
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import {
|
| 2 |
+
Math as MathExtra,
|
| 3 |
+
Vector3,
|
| 4 |
+
Euler,
|
| 5 |
+
Quaternion
|
| 6 |
+
} from 'three'
|
| 7 |
+
|
| 8 |
+
import ControlsBase from './ControlsBase'
|
| 9 |
+
|
| 10 |
+
const zee = new Vector3(0, 0, 1)
|
| 11 |
+
const euler = new Euler()
|
| 12 |
+
const q0 = new Quaternion()
|
| 13 |
+
const q1 = new Quaternion(-Math.sqrt(0.5), 0, 0, Math.sqrt(0.5)) // - PI/2 around the x-axis
|
| 14 |
+
|
| 15 |
+
export default class DeviceOrientationControls extends ControlsBase {
|
| 16 |
+
|
| 17 |
+
constructor (player) {
|
| 18 |
+
super(player, null)
|
| 19 |
+
|
| 20 |
+
this.object = player.eyes
|
| 21 |
+
this.object.rotation.reorder('YXZ')
|
| 22 |
+
|
| 23 |
+
// Let's always listen to orientation changes, even before it's enabled
|
| 24 |
+
// For some reason the orientation lags a few frames behind once we start listening
|
| 25 |
+
window.addEventListener('orientationchange', this.onScreenOrientationChangeEvent.bind(this), false)
|
| 26 |
+
window.addEventListener('deviceorientation', this.onDeviceOrientationChangeEvent.bind(this), false)
|
| 27 |
+
}
|
| 28 |
+
|
| 29 |
+
enable () {
|
| 30 |
+
this.onScreenOrientationChangeEvent()
|
| 31 |
+
|
| 32 |
+
this.enabled = true
|
| 33 |
+
}
|
| 34 |
+
|
| 35 |
+
disable () {
|
| 36 |
+
this.enabled = false
|
| 37 |
+
}
|
| 38 |
+
|
| 39 |
+
update () {
|
| 40 |
+
if (this.enabled === false || !this.deviceOrientation || this.deviceOrientation.alpha === null) {
|
| 41 |
+
return
|
| 42 |
+
}
|
| 43 |
+
|
| 44 |
+
const alpha = this.deviceOrientation.alpha ? MathExtra.degToRad(this.deviceOrientation.alpha) : 0 // Z
|
| 45 |
+
const beta = this.deviceOrientation.beta ? MathExtra.degToRad(this.deviceOrientation.beta) : 0 // X'
|
| 46 |
+
const gamma = this.deviceOrientation.gamma ? MathExtra.degToRad(this.deviceOrientation.gamma) : 0 // Y''
|
| 47 |
+
const orient = this.screenOrientation ? MathExtra.degToRad(this.screenOrientation) : 0 // O
|
| 48 |
+
|
| 49 |
+
euler.set(beta, alpha, -gamma, 'YXZ') // 'ZXY' for the device, but 'YXZ' for us
|
| 50 |
+
|
| 51 |
+
this.object.quaternion.setFromEuler(euler) // orient the device
|
| 52 |
+
this.object.quaternion.multiply(q1) // camera looks out the back of the device, not the top
|
| 53 |
+
this.object.quaternion.multiply(q0.setFromAxisAngle(zee, -orient)) // adjust for screen orientation
|
| 54 |
+
}
|
| 55 |
+
|
| 56 |
+
onDeviceOrientationChangeEvent (event) {
|
| 57 |
+
this.deviceOrientation = event
|
| 58 |
+
}
|
| 59 |
+
|
| 60 |
+
onScreenOrientationChangeEvent () {
|
| 61 |
+
this.screenOrientation = window.orientation || 0
|
| 62 |
+
}
|
| 63 |
+
|
| 64 |
+
}
|
src/controls/KeyboardControls.js
ADDED
|
@@ -0,0 +1,333 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import {
|
| 2 |
+
Vector3,
|
| 3 |
+
Quaternion
|
| 4 |
+
} from 'three'
|
| 5 |
+
|
| 6 |
+
import ControlsBase from './ControlsBase'
|
| 7 |
+
|
| 8 |
+
export default class KeyboardControls extends ControlsBase {
|
| 9 |
+
|
| 10 |
+
constructor (player, element) {
|
| 11 |
+
super(player, element)
|
| 12 |
+
|
| 13 |
+
this.element.setAttribute('tabindex', -1)
|
| 14 |
+
|
| 15 |
+
this.movementSpeedMultiplier = 1
|
| 16 |
+
this.movementSpeed = 1.0
|
| 17 |
+
this.rollSpeed = 0.005
|
| 18 |
+
|
| 19 |
+
this.mouseStatus = 0
|
| 20 |
+
this.dragToLook = false
|
| 21 |
+
this.autoForward = false
|
| 22 |
+
|
| 23 |
+
this.moveState = {
|
| 24 |
+
up: 0,
|
| 25 |
+
down: 0,
|
| 26 |
+
left: 0,
|
| 27 |
+
right: 0,
|
| 28 |
+
forward: 0,
|
| 29 |
+
back: 0,
|
| 30 |
+
pitchUp: 0,
|
| 31 |
+
pitchDown: 0,
|
| 32 |
+
yawLeft: 0,
|
| 33 |
+
yawRight: 0,
|
| 34 |
+
rollLeft: 0,
|
| 35 |
+
rollRight: 0
|
| 36 |
+
}
|
| 37 |
+
|
| 38 |
+
this.moveVector = new Vector3(0, 0, 0)
|
| 39 |
+
this.rotationVector = new Vector3(0, 0, 0)
|
| 40 |
+
|
| 41 |
+
this.contextMenu = this.contextMenu.bind(this)
|
| 42 |
+
this.mouseMove = this.mouseMove.bind(this)
|
| 43 |
+
this.mouseDown = this.mouseDown.bind(this)
|
| 44 |
+
this.mouseUp = this.mouseUp.bind(this)
|
| 45 |
+
this.keyDown = this.keyDown.bind(this)
|
| 46 |
+
this.keyUp = this.keyUp.bind(this)
|
| 47 |
+
}
|
| 48 |
+
|
| 49 |
+
keyDown (event) {
|
| 50 |
+
if (event.altKey) {
|
| 51 |
+
return
|
| 52 |
+
}
|
| 53 |
+
|
| 54 |
+
switch (event.keyCode) {
|
| 55 |
+
case 16: // Shift
|
| 56 |
+
this.movementSpeedMultiplier = 10
|
| 57 |
+
break
|
| 58 |
+
|
| 59 |
+
case 87: // W
|
| 60 |
+
this.moveState.forward = 1
|
| 61 |
+
break
|
| 62 |
+
|
| 63 |
+
case 83: // S
|
| 64 |
+
this.moveState.back = 1
|
| 65 |
+
break
|
| 66 |
+
|
| 67 |
+
case 65: // A
|
| 68 |
+
this.moveState.left = 1
|
| 69 |
+
break
|
| 70 |
+
|
| 71 |
+
case 68: // D
|
| 72 |
+
this.moveState.right = 1
|
| 73 |
+
break
|
| 74 |
+
|
| 75 |
+
case 82: // R
|
| 76 |
+
this.moveState.up = 1
|
| 77 |
+
break
|
| 78 |
+
|
| 79 |
+
case 70: // F
|
| 80 |
+
this.moveState.down = 1
|
| 81 |
+
break
|
| 82 |
+
|
| 83 |
+
case 38: // Up
|
| 84 |
+
this.moveState.pitchUp = 1
|
| 85 |
+
break
|
| 86 |
+
|
| 87 |
+
case 40: // Down
|
| 88 |
+
this.moveState.pitchDown = 1
|
| 89 |
+
break
|
| 90 |
+
|
| 91 |
+
case 37: // Left
|
| 92 |
+
this.moveState.yawLeft = 1
|
| 93 |
+
break
|
| 94 |
+
|
| 95 |
+
case 39: // Right
|
| 96 |
+
this.moveState.yawRight = 1
|
| 97 |
+
break
|
| 98 |
+
|
| 99 |
+
case 81: // Q
|
| 100 |
+
this.moveState.rollLeft = 1
|
| 101 |
+
break
|
| 102 |
+
|
| 103 |
+
case 69: // E
|
| 104 |
+
this.moveState.rollRight = 1
|
| 105 |
+
break
|
| 106 |
+
|
| 107 |
+
// Control-scheme modifiers:
|
| 108 |
+
case 32:
|
| 109 |
+
this.dragToLook = !this.dragToLook
|
| 110 |
+
this.resetDragToLook()
|
| 111 |
+
break
|
| 112 |
+
case 27:
|
| 113 |
+
this.dragToLook = true
|
| 114 |
+
this.resetDragToLook()
|
| 115 |
+
break
|
| 116 |
+
}
|
| 117 |
+
|
| 118 |
+
this.updateMovementVector()
|
| 119 |
+
this.updateRotationVector()
|
| 120 |
+
}
|
| 121 |
+
|
| 122 |
+
keyUp (event) {
|
| 123 |
+
switch (event.keyCode) {
|
| 124 |
+
case 16: // Shift
|
| 125 |
+
this.movementSpeedMultiplier = 1
|
| 126 |
+
break
|
| 127 |
+
|
| 128 |
+
case 87: // W
|
| 129 |
+
this.moveState.forward = 0
|
| 130 |
+
break
|
| 131 |
+
|
| 132 |
+
case 83: // S
|
| 133 |
+
this.moveState.back = 0
|
| 134 |
+
break
|
| 135 |
+
|
| 136 |
+
case 65: // A
|
| 137 |
+
this.moveState.left = 0
|
| 138 |
+
break
|
| 139 |
+
|
| 140 |
+
case 68: // D
|
| 141 |
+
this.moveState.right = 0
|
| 142 |
+
break
|
| 143 |
+
|
| 144 |
+
case 82: // R
|
| 145 |
+
this.moveState.up = 0
|
| 146 |
+
break
|
| 147 |
+
|
| 148 |
+
case 70: // F
|
| 149 |
+
this.moveState.down = 0
|
| 150 |
+
break
|
| 151 |
+
|
| 152 |
+
case 38: // Up
|
| 153 |
+
this.moveState.pitchUp = 0
|
| 154 |
+
break
|
| 155 |
+
|
| 156 |
+
case 40: // Down
|
| 157 |
+
this.moveState.pitchDown = 0
|
| 158 |
+
break
|
| 159 |
+
|
| 160 |
+
case 37: // Left
|
| 161 |
+
this.moveState.yawLeft = 0
|
| 162 |
+
break
|
| 163 |
+
|
| 164 |
+
case 39: // Right
|
| 165 |
+
this.moveState.yawRight = 0
|
| 166 |
+
break
|
| 167 |
+
|
| 168 |
+
case 81: // Q
|
| 169 |
+
this.moveState.rollLeft = 0
|
| 170 |
+
break
|
| 171 |
+
|
| 172 |
+
case 69: // E
|
| 173 |
+
this.moveState.rollRight = 0
|
| 174 |
+
break
|
| 175 |
+
}
|
| 176 |
+
|
| 177 |
+
this.updateMovementVector()
|
| 178 |
+
this.updateRotationVector()
|
| 179 |
+
}
|
| 180 |
+
|
| 181 |
+
resetDragToLook () {
|
| 182 |
+
if (this.dragToLook) {
|
| 183 |
+
this.moveState.yawLeft = 0
|
| 184 |
+
this.moveState.pitchDown = 0
|
| 185 |
+
}
|
| 186 |
+
}
|
| 187 |
+
|
| 188 |
+
mouseDown (event) {
|
| 189 |
+
if (this.element !== document) {
|
| 190 |
+
this.element.focus()
|
| 191 |
+
}
|
| 192 |
+
|
| 193 |
+
event.preventDefault()
|
| 194 |
+
event.stopPropagation()
|
| 195 |
+
|
| 196 |
+
if (this.dragToLook) {
|
| 197 |
+
this.mouseStatus += 1
|
| 198 |
+
}
|
| 199 |
+
else {
|
| 200 |
+
switch (event.button) {
|
| 201 |
+
case 0:
|
| 202 |
+
this.moveState.forward = 1
|
| 203 |
+
break
|
| 204 |
+
|
| 205 |
+
case 2:
|
| 206 |
+
this.moveState.back = 1
|
| 207 |
+
break
|
| 208 |
+
}
|
| 209 |
+
|
| 210 |
+
this.updateMovementVector()
|
| 211 |
+
}
|
| 212 |
+
}
|
| 213 |
+
|
| 214 |
+
mouseMove (event) {
|
| 215 |
+
if (this.dragToLook && this.mouseStatus === 0) {
|
| 216 |
+
return
|
| 217 |
+
}
|
| 218 |
+
|
| 219 |
+
const container = this.getContainerDimensions()
|
| 220 |
+
const halfWidth = container.size[0] / 2
|
| 221 |
+
const halfHeight = container.size[1] / 2
|
| 222 |
+
|
| 223 |
+
this.moveState.yawLeft = -((event.pageX - container.offset[0]) - halfWidth) / halfWidth
|
| 224 |
+
this.moveState.pitchDown = ((event.pageY - container.offset[1]) - halfHeight) / halfHeight
|
| 225 |
+
|
| 226 |
+
this.updateRotationVector()
|
| 227 |
+
}
|
| 228 |
+
|
| 229 |
+
mouseUp (event) {
|
| 230 |
+
event.preventDefault()
|
| 231 |
+
event.stopPropagation()
|
| 232 |
+
|
| 233 |
+
if (this.dragToLook) {
|
| 234 |
+
this.mouseStatus -= 1
|
| 235 |
+
this.moveState.yawLeft = this.moveState.pitchDown = 0
|
| 236 |
+
}
|
| 237 |
+
else {
|
| 238 |
+
switch (event.button) {
|
| 239 |
+
case 0:
|
| 240 |
+
this.moveState.forward = 0
|
| 241 |
+
break
|
| 242 |
+
|
| 243 |
+
case 2:
|
| 244 |
+
this.moveState.back = 0
|
| 245 |
+
break
|
| 246 |
+
}
|
| 247 |
+
|
| 248 |
+
this.updateMovementVector()
|
| 249 |
+
}
|
| 250 |
+
|
| 251 |
+
this.updateRotationVector()
|
| 252 |
+
}
|
| 253 |
+
|
| 254 |
+
updateMovementVector () {
|
| 255 |
+
const forward = (this.moveState.forward || (this.autoForward && !this.moveState.back)) ? 1 : 0
|
| 256 |
+
|
| 257 |
+
this.moveVector.x = (-this.moveState.left + this.moveState.right)
|
| 258 |
+
this.moveVector.y = (-this.moveState.down + this.moveState.up)
|
| 259 |
+
this.moveVector.z = (-forward + this.moveState.back)
|
| 260 |
+
}
|
| 261 |
+
|
| 262 |
+
updateRotationVector () {
|
| 263 |
+
this.rotationVector.x = (-this.moveState.pitchDown + this.moveState.pitchUp)
|
| 264 |
+
this.rotationVector.y = (-this.moveState.yawRight + this.moveState.yawLeft)
|
| 265 |
+
this.rotationVector.z = (-this.moveState.rollRight + this.moveState.rollLeft)
|
| 266 |
+
}
|
| 267 |
+
|
| 268 |
+
getContainerDimensions () {
|
| 269 |
+
if (this.element !== document) {
|
| 270 |
+
return {
|
| 271 |
+
size: [this.element.offsetWidth, this.element.offsetHeight],
|
| 272 |
+
offset: [this.element.offsetLeft, this.element.offsetTop]
|
| 273 |
+
}
|
| 274 |
+
}
|
| 275 |
+
else {
|
| 276 |
+
return {
|
| 277 |
+
size: [window.innerWidth, window.innerHeight],
|
| 278 |
+
offset: [0, 0]
|
| 279 |
+
}
|
| 280 |
+
}
|
| 281 |
+
}
|
| 282 |
+
|
| 283 |
+
contextMenu (event) {
|
| 284 |
+
event.preventDefault()
|
| 285 |
+
}
|
| 286 |
+
|
| 287 |
+
enable () {
|
| 288 |
+
this.updateMovementVector()
|
| 289 |
+
this.updateRotationVector()
|
| 290 |
+
|
| 291 |
+
this.element.addEventListener('contextmenu', this.contextMenu, false)
|
| 292 |
+
|
| 293 |
+
this.element.addEventListener('mousemove', this.mouseMove, false)
|
| 294 |
+
this.element.addEventListener('mousedown', this.mouseDown, false)
|
| 295 |
+
this.element.addEventListener('mouseup', this.mouseUp, false)
|
| 296 |
+
|
| 297 |
+
window.addEventListener('keydown', this.keyDown, false)
|
| 298 |
+
window.addEventListener('keyup', this.keyUp, false)
|
| 299 |
+
|
| 300 |
+
this.enabled = true
|
| 301 |
+
}
|
| 302 |
+
|
| 303 |
+
disable () {
|
| 304 |
+
this.element.removeEventListener('contextmenu', this.contextMenu, false)
|
| 305 |
+
|
| 306 |
+
this.element.removeEventListener('mousemove', this.mouseMove, false)
|
| 307 |
+
this.element.removeEventListener('mousedown', this.mouseDown, false)
|
| 308 |
+
this.element.removeEventListener('mouseup', this.mouseUp, false)
|
| 309 |
+
|
| 310 |
+
window.removeEventListener('keydown', this.keyDown, false)
|
| 311 |
+
window.removeEventListener('keyup', this.keyUp, false)
|
| 312 |
+
|
| 313 |
+
this.enabled = false
|
| 314 |
+
}
|
| 315 |
+
|
| 316 |
+
update () {
|
| 317 |
+
const moveMult = this.movementSpeed * this.movementSpeedMultiplier
|
| 318 |
+
const rotMult = this.rollSpeed
|
| 319 |
+
const worldQuaternion = new Quaternion()
|
| 320 |
+
|
| 321 |
+
this.player.eyes.getWorldQuaternion(worldQuaternion)
|
| 322 |
+
|
| 323 |
+
this.player.velocity
|
| 324 |
+
.copy(this.moveVector)
|
| 325 |
+
.multiplyScalar(moveMult)
|
| 326 |
+
.applyQuaternion(worldQuaternion)
|
| 327 |
+
|
| 328 |
+
this.player.eyeAngularVelocity
|
| 329 |
+
.copy(this.rotationVector)
|
| 330 |
+
.multiplyScalar(rotMult)
|
| 331 |
+
}
|
| 332 |
+
|
| 333 |
+
}
|
src/controls/MobileDeviceControls.js
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import DeviceOrientationControls from './DeviceOrientationControls'
|
| 2 |
+
import TouchControls from './TouchControls'
|
| 3 |
+
|
| 4 |
+
import ControlsBase from './ControlsBase'
|
| 5 |
+
|
| 6 |
+
export default class MobileDeviceControls extends ControlsBase {
|
| 7 |
+
|
| 8 |
+
constructor (player, element) {
|
| 9 |
+
super(player, element)
|
| 10 |
+
|
| 11 |
+
this.deviceOrientationControls = new DeviceOrientationControls(player)
|
| 12 |
+
this.touchControls = new TouchControls(player, element)
|
| 13 |
+
}
|
| 14 |
+
|
| 15 |
+
enable () {
|
| 16 |
+
this.deviceOrientationControls.enable()
|
| 17 |
+
this.touchControls.enable()
|
| 18 |
+
this.enabled = true
|
| 19 |
+
}
|
| 20 |
+
|
| 21 |
+
disable () {
|
| 22 |
+
this.deviceOrientationControls.disable()
|
| 23 |
+
this.touchControls.disable()
|
| 24 |
+
|
| 25 |
+
this.enabled = false
|
| 26 |
+
}
|
| 27 |
+
|
| 28 |
+
update () {
|
| 29 |
+
if (this.enabled === false) {
|
| 30 |
+
return
|
| 31 |
+
}
|
| 32 |
+
|
| 33 |
+
this.deviceOrientationControls.update()
|
| 34 |
+
this.touchControls.update()
|
| 35 |
+
}
|
| 36 |
+
|
| 37 |
+
}
|
src/controls/TouchControls.js
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import {
|
| 2 |
+
Math as MathExtra,
|
| 3 |
+
Vector3,
|
| 4 |
+
Quaternion
|
| 5 |
+
} from 'three'
|
| 6 |
+
|
| 7 |
+
import ControlsBase from './ControlsBase'
|
| 8 |
+
|
| 9 |
+
export default class TouchControls extends ControlsBase {
|
| 10 |
+
|
| 11 |
+
constructor (player, element) {
|
| 12 |
+
super(player, element)
|
| 13 |
+
|
| 14 |
+
this.velocity = new Vector3(0, 0, 0)
|
| 15 |
+
|
| 16 |
+
this.onTouchEvent = this.onTouchEvent.bind(this)
|
| 17 |
+
}
|
| 18 |
+
|
| 19 |
+
onTouchEvent (event) {
|
| 20 |
+
event.preventDefault()
|
| 21 |
+
|
| 22 |
+
const touches = event.touches
|
| 23 |
+
|
| 24 |
+
if (touches.length === 0) {
|
| 25 |
+
this.velocity.set(0, 0, 0)
|
| 26 |
+
return
|
| 27 |
+
}
|
| 28 |
+
|
| 29 |
+
let avgX = 0
|
| 30 |
+
let avgY = 0
|
| 31 |
+
for (let i = 0; i < touches.length; i++) {
|
| 32 |
+
avgX += touches[i].pageX
|
| 33 |
+
avgY += touches[i].pageY
|
| 34 |
+
}
|
| 35 |
+
avgX /= touches.length
|
| 36 |
+
avgY /= touches.length
|
| 37 |
+
|
| 38 |
+
const vy = Math.tan(0.5 * MathExtra.DEG2RAD * this.player.eyes.fov)
|
| 39 |
+
const vx = this.player.eyes.aspect * vy
|
| 40 |
+
|
| 41 |
+
this.velocity.set(
|
| 42 |
+
vx * (avgX * 2.0 / window.innerWidth - 1.0),
|
| 43 |
+
-vy * (avgY * 2.0 / window.innerHeight - 1.0),
|
| 44 |
+
-1
|
| 45 |
+
).normalize()
|
| 46 |
+
|
| 47 |
+
this.velocity.multiplyScalar(touches.length > 1 ? 10 : 1)
|
| 48 |
+
}
|
| 49 |
+
|
| 50 |
+
enable () {
|
| 51 |
+
this.element.addEventListener('touchstart', this.onTouchEvent, false)
|
| 52 |
+
this.element.addEventListener('touchmove', this.onTouchEvent, false)
|
| 53 |
+
this.element.addEventListener('touchend', this.onTouchEvent, false)
|
| 54 |
+
|
| 55 |
+
this.enabled = true
|
| 56 |
+
}
|
| 57 |
+
|
| 58 |
+
disable () {
|
| 59 |
+
this.element.removeEventListener('touchstart', this.onTouchEvent, false)
|
| 60 |
+
this.element.removeEventListener('touchmove', this.onTouchEvent, false)
|
| 61 |
+
this.element.removeEventListener('touchend', this.onTouchEvent, false)
|
| 62 |
+
|
| 63 |
+
this.enabled = false
|
| 64 |
+
}
|
| 65 |
+
|
| 66 |
+
update () {
|
| 67 |
+
if (this.enabled === false) {
|
| 68 |
+
return
|
| 69 |
+
}
|
| 70 |
+
|
| 71 |
+
const worldQuaternion = new Quaternion()
|
| 72 |
+
|
| 73 |
+
this.player.eyes.getWorldQuaternion(worldQuaternion)
|
| 74 |
+
|
| 75 |
+
this.player.velocity
|
| 76 |
+
.copy(this.velocity)
|
| 77 |
+
.applyQuaternion(worldQuaternion)
|
| 78 |
+
}
|
| 79 |
+
|
| 80 |
+
}
|
src/main.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import Simulation from './Simulation'
|
| 2 |
+
import Ui from './Ui'
|
| 3 |
+
import { isWebGlSupported } from './utils'
|
| 4 |
+
|
| 5 |
+
if (!isWebGlSupported()) {
|
| 6 |
+
Ui.showWebGLError()
|
| 7 |
+
}
|
| 8 |
+
else {
|
| 9 |
+
Simulation.init()
|
| 10 |
+
Simulation.start()
|
| 11 |
+
}
|
src/postprocessing/CopyShader.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import simpleVertexCode from '../shaders/simpleVertex.glsl'
|
| 2 |
+
import copyCode from '../shaders/copy.glsl'
|
| 3 |
+
|
| 4 |
+
export default {
|
| 5 |
+
|
| 6 |
+
uniforms: {
|
| 7 |
+
tDiffuse: { value: null },
|
| 8 |
+
opacity: { value: 1.0 }
|
| 9 |
+
},
|
| 10 |
+
|
| 11 |
+
vertexShader: simpleVertexCode,
|
| 12 |
+
fragmentShader: copyCode
|
| 13 |
+
|
| 14 |
+
}
|
src/postprocessing/EffectComposer.js
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/**
|
| 2 |
+
* The source code of this file is heavily based on
|
| 3 |
+
* https://github.com/mrdoob/three.js/blob/dev/examples/js/postprocessing/EffectComposer.js
|
| 4 |
+
*/
|
| 5 |
+
|
| 6 |
+
import { WebGLRenderTarget, LinearFilter, RGBAFormat } from 'three'
|
| 7 |
+
|
| 8 |
+
export default class EffectComposer {
|
| 9 |
+
|
| 10 |
+
constructor (renderer, renderTarget) {
|
| 11 |
+
this.renderer = renderer
|
| 12 |
+
|
| 13 |
+
if (renderTarget === undefined) {
|
| 14 |
+
const parameters = {
|
| 15 |
+
minFilter: LinearFilter,
|
| 16 |
+
magFilter: LinearFilter,
|
| 17 |
+
format: RGBAFormat,
|
| 18 |
+
stencilBuffer: false
|
| 19 |
+
}
|
| 20 |
+
|
| 21 |
+
const size = renderer.getDrawingBufferSize()
|
| 22 |
+
renderTarget = new WebGLRenderTarget(size.width, size.height, parameters)
|
| 23 |
+
renderTarget.texture.name = 'EffectComposer.rt1'
|
| 24 |
+
}
|
| 25 |
+
|
| 26 |
+
this.renderTarget1 = renderTarget
|
| 27 |
+
this.renderTarget2 = renderTarget.clone()
|
| 28 |
+
this.renderTarget2.texture.name = 'EffectComposer.rt2'
|
| 29 |
+
|
| 30 |
+
this.writeBuffer = this.renderTarget1
|
| 31 |
+
this.readBuffer = this.renderTarget2
|
| 32 |
+
|
| 33 |
+
this.passes = []
|
| 34 |
+
}
|
| 35 |
+
|
| 36 |
+
swapBuffers () {
|
| 37 |
+
const tmp = this.readBuffer
|
| 38 |
+
this.readBuffer = this.writeBuffer
|
| 39 |
+
this.writeBuffer = tmp
|
| 40 |
+
}
|
| 41 |
+
|
| 42 |
+
addPass (pass) {
|
| 43 |
+
this.passes.push(pass)
|
| 44 |
+
|
| 45 |
+
const size = this.renderer.getDrawingBufferSize()
|
| 46 |
+
pass.setSize(size.width, size.height)
|
| 47 |
+
}
|
| 48 |
+
|
| 49 |
+
render () {
|
| 50 |
+
for (let i = 0; i < this.passes.length; i++) {
|
| 51 |
+
let pass = this.passes[i]
|
| 52 |
+
|
| 53 |
+
pass.render(this.renderer, this.writeBuffer, this.readBuffer)
|
| 54 |
+
}
|
| 55 |
+
}
|
| 56 |
+
|
| 57 |
+
setSize (width, height) {
|
| 58 |
+
this.renderTarget1.setSize(width, height)
|
| 59 |
+
this.renderTarget2.setSize(width, height)
|
| 60 |
+
|
| 61 |
+
for (let i = 0; i < this.passes.length; i++) {
|
| 62 |
+
this.passes[i].setSize(width, height)
|
| 63 |
+
}
|
| 64 |
+
}
|
| 65 |
+
|
| 66 |
+
}
|
src/postprocessing/LuminosityHighPassShader.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { Color } from 'three'
|
| 2 |
+
|
| 3 |
+
import simpleVertexCode from '../shaders/simpleVertex.glsl'
|
| 4 |
+
import luminosityHighPassCode from '../shaders/luminosityHighPass.glsl'
|
| 5 |
+
|
| 6 |
+
export default {
|
| 7 |
+
|
| 8 |
+
shaderID: 'luminosityHighPass',
|
| 9 |
+
|
| 10 |
+
uniforms: {
|
| 11 |
+
tDiffuse: { type: 't', value: null },
|
| 12 |
+
luminosityThreshold: { type: 'f', value: 1.0 },
|
| 13 |
+
smoothWidth: { type: 'f', value: 1.0 },
|
| 14 |
+
defaultColor: { type: 'c', value: new Color(0x000000) },
|
| 15 |
+
defaultOpacity: { type: 'f', value: 0.0 }
|
| 16 |
+
},
|
| 17 |
+
|
| 18 |
+
vertexShader: simpleVertexCode,
|
| 19 |
+
fragmentShader: luminosityHighPassCode
|
| 20 |
+
|
| 21 |
+
}
|
src/postprocessing/Pass.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import {
|
| 2 |
+
OrthographicCamera,
|
| 3 |
+
Scene,
|
| 4 |
+
Mesh,
|
| 5 |
+
PlaneBufferGeometry
|
| 6 |
+
} from 'three'
|
| 7 |
+
|
| 8 |
+
export default class Pass {
|
| 9 |
+
|
| 10 |
+
constructor () {
|
| 11 |
+
this.clear = false
|
| 12 |
+
this.renderToScreen = false
|
| 13 |
+
|
| 14 |
+
this.camera = new OrthographicCamera(-1, 1, 1, -1, 0, 1)
|
| 15 |
+
this.scene = new Scene()
|
| 16 |
+
|
| 17 |
+
this.quad = new Mesh(new PlaneBufferGeometry(2, 2), null)
|
| 18 |
+
this.quad.frustumCulled = false // Avoid getting clipped
|
| 19 |
+
this.scene.add(this.quad)
|
| 20 |
+
}
|
| 21 |
+
|
| 22 |
+
setSize (width, height) {
|
| 23 |
+
|
| 24 |
+
}
|
| 25 |
+
|
| 26 |
+
render (renderer, writeBuffer, readBuffer) {
|
| 27 |
+
|
| 28 |
+
}
|
| 29 |
+
|
| 30 |
+
}
|
src/postprocessing/RenderPass.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import Pass from './Pass'
|
| 2 |
+
|
| 3 |
+
export default class RenderPass extends Pass {
|
| 4 |
+
|
| 5 |
+
constructor (scene, camera) {
|
| 6 |
+
super()
|
| 7 |
+
|
| 8 |
+
this.scene = scene
|
| 9 |
+
this.camera = camera
|
| 10 |
+
}
|
| 11 |
+
|
| 12 |
+
render (renderer, writeBuffer, readBuffer) {
|
| 13 |
+
const oldAutoClear = renderer.autoClear
|
| 14 |
+
renderer.autoClear = false
|
| 15 |
+
|
| 16 |
+
this.scene.overrideMaterial = this.overrideMaterial
|
| 17 |
+
renderer.render(this.scene, this.camera, this.renderToScreen ? null : readBuffer, true)
|
| 18 |
+
|
| 19 |
+
this.scene.overrideMaterial = null
|
| 20 |
+
renderer.autoClear = oldAutoClear
|
| 21 |
+
}
|
| 22 |
+
|
| 23 |
+
}
|
src/postprocessing/UnrealBloomPass.js
ADDED
|
@@ -0,0 +1,298 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/**
|
| 2 |
+
* The source code of this file is heavily based on
|
| 3 |
+
* https://github.com/mrdoob/three.js/blob/dev/examples/js/postprocessing/UnrealBloomPass.js
|
| 4 |
+
*/
|
| 5 |
+
|
| 6 |
+
import {
|
| 7 |
+
Vector2,
|
| 8 |
+
Vector3,
|
| 9 |
+
Color,
|
| 10 |
+
|
| 11 |
+
ShaderMaterial,
|
| 12 |
+
MeshBasicMaterial,
|
| 13 |
+
|
| 14 |
+
LinearFilter,
|
| 15 |
+
RGBAFormat,
|
| 16 |
+
AdditiveBlending,
|
| 17 |
+
|
| 18 |
+
UniformsUtils,
|
| 19 |
+
|
| 20 |
+
WebGLRenderTarget
|
| 21 |
+
} from 'three'
|
| 22 |
+
|
| 23 |
+
import Pass from './Pass'
|
| 24 |
+
import CopyShader from './CopyShader'
|
| 25 |
+
import LuminosityHighPassShader from './LuminosityHighPassShader'
|
| 26 |
+
import simpleVertexCode from '../shaders/simpleVertex.glsl'
|
| 27 |
+
import seperableBlurCode from '../shaders/seperableBlur.glsl'
|
| 28 |
+
import bloomCompositeCode from '../shaders/bloomComposite.glsl'
|
| 29 |
+
|
| 30 |
+
export default class UnrealBloomPass extends Pass {
|
| 31 |
+
|
| 32 |
+
constructor (resolution, strength, radius, threshold) {
|
| 33 |
+
super()
|
| 34 |
+
|
| 35 |
+
this.strength = strength
|
| 36 |
+
this.radius = radius
|
| 37 |
+
this.threshold = threshold
|
| 38 |
+
this.resolution = new Vector2(resolution.x, resolution.y)
|
| 39 |
+
|
| 40 |
+
// render targets
|
| 41 |
+
const pars = {
|
| 42 |
+
minFilter: LinearFilter,
|
| 43 |
+
magFilter: LinearFilter,
|
| 44 |
+
format: RGBAFormat
|
| 45 |
+
}
|
| 46 |
+
|
| 47 |
+
this.renderTargetsHorizontal = []
|
| 48 |
+
this.renderTargetsVertical = []
|
| 49 |
+
this.nMips = 5
|
| 50 |
+
|
| 51 |
+
let resx = Math.round(this.resolution.x / 2)
|
| 52 |
+
let resy = Math.round(this.resolution.y / 2)
|
| 53 |
+
|
| 54 |
+
this.renderTargetBright = new WebGLRenderTarget(resx, resy, pars)
|
| 55 |
+
this.renderTargetBright.texture.name = 'UnrealBloomPass.bright'
|
| 56 |
+
this.renderTargetBright.texture.generateMipmaps = false
|
| 57 |
+
|
| 58 |
+
for (let i = 0; i < this.nMips; i++) {
|
| 59 |
+
let renderTarget = new WebGLRenderTarget(resx, resy, pars)
|
| 60 |
+
|
| 61 |
+
renderTarget.texture.name = `UnrealBloomPass.h${i}`
|
| 62 |
+
renderTarget.texture.generateMipmaps = false
|
| 63 |
+
|
| 64 |
+
this.renderTargetsHorizontal.push(renderTarget)
|
| 65 |
+
|
| 66 |
+
renderTarget = new WebGLRenderTarget(resx, resy, pars)
|
| 67 |
+
|
| 68 |
+
renderTarget.texture.name = `UnrealBloomPass.v${i}`
|
| 69 |
+
renderTarget.texture.generateMipmaps = false
|
| 70 |
+
|
| 71 |
+
this.renderTargetsVertical.push(renderTarget)
|
| 72 |
+
|
| 73 |
+
resx = Math.round(resx / 2)
|
| 74 |
+
resy = Math.round(resy / 2)
|
| 75 |
+
}
|
| 76 |
+
|
| 77 |
+
this.highPassUniforms = UniformsUtils.clone(LuminosityHighPassShader.uniforms)
|
| 78 |
+
|
| 79 |
+
this.highPassUniforms.luminosityThreshold.value = threshold
|
| 80 |
+
this.highPassUniforms.smoothWidth.value = 0.01
|
| 81 |
+
|
| 82 |
+
this.materialHighPassFilter = new ShaderMaterial(
|
| 83 |
+
{
|
| 84 |
+
uniforms: this.highPassUniforms,
|
| 85 |
+
vertexShader: LuminosityHighPassShader.vertexShader,
|
| 86 |
+
fragmentShader: LuminosityHighPassShader.fragmentShader,
|
| 87 |
+
defines: {}
|
| 88 |
+
}
|
| 89 |
+
)
|
| 90 |
+
|
| 91 |
+
// Gaussian Blur Materials
|
| 92 |
+
this.separableBlurMaterials = []
|
| 93 |
+
|
| 94 |
+
const kernelSizeArray = [3, 5, 7, 9, 11]
|
| 95 |
+
|
| 96 |
+
resx = Math.round(this.resolution.x / 2)
|
| 97 |
+
resy = Math.round(this.resolution.y / 2)
|
| 98 |
+
|
| 99 |
+
for (let i = 0; i < this.nMips; i++) {
|
| 100 |
+
this.separableBlurMaterials.push(this.getSeperableBlurMaterial(kernelSizeArray[i]))
|
| 101 |
+
this.separableBlurMaterials[i].uniforms.texSize.value = new Vector2(resx, resy)
|
| 102 |
+
|
| 103 |
+
resx = Math.round(resx / 2)
|
| 104 |
+
resy = Math.round(resy / 2)
|
| 105 |
+
}
|
| 106 |
+
|
| 107 |
+
// Composite material
|
| 108 |
+
this.compositeMaterial = this.getCompositeMaterial(this.nMips)
|
| 109 |
+
this.compositeMaterial.uniforms.blurTexture1.value = this.renderTargetsVertical[0].texture
|
| 110 |
+
this.compositeMaterial.uniforms.blurTexture2.value = this.renderTargetsVertical[1].texture
|
| 111 |
+
this.compositeMaterial.uniforms.blurTexture3.value = this.renderTargetsVertical[2].texture
|
| 112 |
+
this.compositeMaterial.uniforms.blurTexture4.value = this.renderTargetsVertical[3].texture
|
| 113 |
+
this.compositeMaterial.uniforms.blurTexture5.value = this.renderTargetsVertical[4].texture
|
| 114 |
+
this.compositeMaterial.uniforms.bloomStrength.value = strength
|
| 115 |
+
this.compositeMaterial.uniforms.bloomRadius.value = 0.1
|
| 116 |
+
this.compositeMaterial.needsUpdate = true
|
| 117 |
+
|
| 118 |
+
const bloomFactors = [1.0, 0.8, 0.6, 0.4, 0.2]
|
| 119 |
+
this.compositeMaterial.uniforms.bloomFactors.value = bloomFactors
|
| 120 |
+
this.bloomTintColors = [
|
| 121 |
+
new Vector3(1, 1, 1),
|
| 122 |
+
new Vector3(1, 1, 1),
|
| 123 |
+
new Vector3(1, 1, 1),
|
| 124 |
+
new Vector3(1, 1, 1),
|
| 125 |
+
new Vector3(1, 1, 1)
|
| 126 |
+
]
|
| 127 |
+
this.compositeMaterial.uniforms.bloomTintColors.value = this.bloomTintColors
|
| 128 |
+
|
| 129 |
+
this.copyUniforms = UniformsUtils.clone(CopyShader.uniforms)
|
| 130 |
+
this.copyUniforms.opacity.value = 1.0
|
| 131 |
+
|
| 132 |
+
this.materialCopy = new ShaderMaterial(
|
| 133 |
+
{
|
| 134 |
+
uniforms: this.copyUniforms,
|
| 135 |
+
vertexShader: CopyShader.vertexShader,
|
| 136 |
+
fragmentShader: CopyShader.fragmentShader,
|
| 137 |
+
blending: AdditiveBlending,
|
| 138 |
+
depthTest: false,
|
| 139 |
+
depthWrite: false,
|
| 140 |
+
transparent: true
|
| 141 |
+
}
|
| 142 |
+
)
|
| 143 |
+
|
| 144 |
+
this.oldClearColor = new Color()
|
| 145 |
+
this.oldClearAlpha = 1
|
| 146 |
+
|
| 147 |
+
this.basic = new MeshBasicMaterial()
|
| 148 |
+
}
|
| 149 |
+
|
| 150 |
+
dispose () {
|
| 151 |
+
for (let i = 0; i < this.renderTargetsHorizontal.length; i++) {
|
| 152 |
+
this.renderTargetsHorizontal[i].dispose()
|
| 153 |
+
}
|
| 154 |
+
|
| 155 |
+
for (let i = 0; i < this.renderTargetsVertical.length; i++) {
|
| 156 |
+
this.renderTargetsVertical[i].dispose()
|
| 157 |
+
}
|
| 158 |
+
|
| 159 |
+
this.renderTargetBright.dispose()
|
| 160 |
+
}
|
| 161 |
+
|
| 162 |
+
setSize (width, height) {
|
| 163 |
+
let resx = Math.round(width / 2)
|
| 164 |
+
let resy = Math.round(height / 2)
|
| 165 |
+
|
| 166 |
+
this.renderTargetBright.setSize(resx, resy)
|
| 167 |
+
|
| 168 |
+
for (let i = 0; i < this.nMips; i++) {
|
| 169 |
+
this.renderTargetsHorizontal[i].setSize(resx, resy)
|
| 170 |
+
this.renderTargetsVertical[i].setSize(resx, resy)
|
| 171 |
+
|
| 172 |
+
this.separableBlurMaterials[i].uniforms.texSize.value = new Vector2(resx, resy)
|
| 173 |
+
|
| 174 |
+
resx = Math.round(resx / 2)
|
| 175 |
+
resy = Math.round(resy / 2)
|
| 176 |
+
}
|
| 177 |
+
}
|
| 178 |
+
|
| 179 |
+
render (renderer, writeBuffer, readBuffer) {
|
| 180 |
+
this.oldClearColor.copy(renderer.getClearColor())
|
| 181 |
+
this.oldClearAlpha = renderer.getClearAlpha()
|
| 182 |
+
const oldAutoClear = renderer.autoClear
|
| 183 |
+
renderer.autoClear = false
|
| 184 |
+
|
| 185 |
+
renderer.setClearColor(new Color(0, 0, 0), 0)
|
| 186 |
+
|
| 187 |
+
// Render input to screen
|
| 188 |
+
|
| 189 |
+
if (this.renderToScreen) {
|
| 190 |
+
this.quad.material = this.basic
|
| 191 |
+
this.basic.map = readBuffer.texture
|
| 192 |
+
|
| 193 |
+
renderer.render(this.scene, this.camera, undefined, true)
|
| 194 |
+
}
|
| 195 |
+
|
| 196 |
+
// 1. Extract Bright Areas
|
| 197 |
+
|
| 198 |
+
this.highPassUniforms.tDiffuse.value = readBuffer.texture
|
| 199 |
+
this.highPassUniforms.luminosityThreshold.value = this.threshold
|
| 200 |
+
this.quad.material = this.materialHighPassFilter
|
| 201 |
+
|
| 202 |
+
renderer.render(this.scene, this.camera, this.renderTargetBright, true)
|
| 203 |
+
|
| 204 |
+
// 2. Blur All the mips progressively
|
| 205 |
+
|
| 206 |
+
let inputRenderTarget = this.renderTargetBright
|
| 207 |
+
|
| 208 |
+
for (let i = 0; i < this.nMips; i++) {
|
| 209 |
+
this.quad.material = this.separableBlurMaterials[i]
|
| 210 |
+
|
| 211 |
+
this.separableBlurMaterials[i].uniforms.colorTexture.value = inputRenderTarget.texture
|
| 212 |
+
this.separableBlurMaterials[i].uniforms.direction.value = UnrealBloomPass.BlurDirectionX
|
| 213 |
+
renderer.render(this.scene, this.camera, this.renderTargetsHorizontal[i], true)
|
| 214 |
+
|
| 215 |
+
this.separableBlurMaterials[i].uniforms.colorTexture.value = this.renderTargetsHorizontal[i].texture
|
| 216 |
+
this.separableBlurMaterials[i].uniforms.direction.value = UnrealBloomPass.BlurDirectionY
|
| 217 |
+
renderer.render(this.scene, this.camera, this.renderTargetsVertical[i], true)
|
| 218 |
+
|
| 219 |
+
inputRenderTarget = this.renderTargetsVertical[i]
|
| 220 |
+
}
|
| 221 |
+
|
| 222 |
+
// Composite All the mips
|
| 223 |
+
|
| 224 |
+
this.quad.material = this.compositeMaterial
|
| 225 |
+
this.compositeMaterial.uniforms.bloomStrength.value = this.strength
|
| 226 |
+
this.compositeMaterial.uniforms.bloomRadius.value = this.radius
|
| 227 |
+
this.compositeMaterial.uniforms.bloomTintColors.value = this.bloomTintColors
|
| 228 |
+
|
| 229 |
+
renderer.render(this.scene, this.camera, this.renderTargetsHorizontal[0], true)
|
| 230 |
+
|
| 231 |
+
// Blend it additively over the input texture
|
| 232 |
+
|
| 233 |
+
this.quad.material = this.materialCopy
|
| 234 |
+
this.copyUniforms.tDiffuse.value = this.renderTargetsHorizontal[0].texture
|
| 235 |
+
|
| 236 |
+
if (this.renderToScreen) {
|
| 237 |
+
renderer.render(this.scene, this.camera, undefined, false)
|
| 238 |
+
}
|
| 239 |
+
else {
|
| 240 |
+
renderer.render(this.scene, this.camera, readBuffer, false)
|
| 241 |
+
}
|
| 242 |
+
|
| 243 |
+
// Restore renderer settings
|
| 244 |
+
|
| 245 |
+
renderer.setClearColor(this.oldClearColor, this.oldClearAlpha)
|
| 246 |
+
renderer.autoClear = oldAutoClear
|
| 247 |
+
}
|
| 248 |
+
|
| 249 |
+
getSeperableBlurMaterial (kernelRadius) {
|
| 250 |
+
return new ShaderMaterial(
|
| 251 |
+
{
|
| 252 |
+
defines: {
|
| 253 |
+
KERNEL_RADIUS: kernelRadius,
|
| 254 |
+
SIGMA: kernelRadius
|
| 255 |
+
},
|
| 256 |
+
|
| 257 |
+
uniforms: {
|
| 258 |
+
colorTexture: { value: null },
|
| 259 |
+
texSize: { value: new Vector2(0.5, 0.5) },
|
| 260 |
+
direction: { value: new Vector2(0.5, 0.5) }
|
| 261 |
+
},
|
| 262 |
+
|
| 263 |
+
vertexShader: simpleVertexCode,
|
| 264 |
+
fragmentShader: seperableBlurCode
|
| 265 |
+
}
|
| 266 |
+
)
|
| 267 |
+
}
|
| 268 |
+
|
| 269 |
+
getCompositeMaterial (nMips) {
|
| 270 |
+
return new ShaderMaterial(
|
| 271 |
+
{
|
| 272 |
+
defines: {
|
| 273 |
+
NUM_MIPS: nMips
|
| 274 |
+
},
|
| 275 |
+
|
| 276 |
+
uniforms: {
|
| 277 |
+
blurTexture1: { value: null },
|
| 278 |
+
blurTexture2: { value: null },
|
| 279 |
+
blurTexture3: { value: null },
|
| 280 |
+
blurTexture4: { value: null },
|
| 281 |
+
blurTexture5: { value: null },
|
| 282 |
+
dirtTexture: { value: null },
|
| 283 |
+
bloomStrength: { value: 1.0 },
|
| 284 |
+
bloomFactors: { value: null },
|
| 285 |
+
bloomTintColors: { value: null },
|
| 286 |
+
bloomRadius: { value: 0.0 }
|
| 287 |
+
},
|
| 288 |
+
|
| 289 |
+
vertexShader: simpleVertexCode,
|
| 290 |
+
fragmentShader: bloomCompositeCode
|
| 291 |
+
}
|
| 292 |
+
)
|
| 293 |
+
}
|
| 294 |
+
|
| 295 |
+
}
|
| 296 |
+
|
| 297 |
+
UnrealBloomPass.BlurDirectionX = new Vector2(1, 0)
|
| 298 |
+
UnrealBloomPass.BlurDirectionY = new Vector2(0, 1)
|
src/shaders/bloomComposite.glsl
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
varying vec2 vUv;
|
| 2 |
+
uniform sampler2D blurTexture1;
|
| 3 |
+
uniform sampler2D blurTexture2;
|
| 4 |
+
uniform sampler2D blurTexture3;
|
| 5 |
+
uniform sampler2D blurTexture4;
|
| 6 |
+
uniform sampler2D blurTexture5;
|
| 7 |
+
uniform sampler2D dirtTexture;
|
| 8 |
+
uniform float bloomStrength;
|
| 9 |
+
uniform float bloomRadius;
|
| 10 |
+
uniform float bloomFactors[NUM_MIPS];
|
| 11 |
+
uniform vec3 bloomTintColors[NUM_MIPS];
|
| 12 |
+
|
| 13 |
+
float lerpBloomFactor(const in float factor) {
|
| 14 |
+
float mirrorFactor = 1.2 - factor;
|
| 15 |
+
return mix(factor, mirrorFactor, bloomRadius);
|
| 16 |
+
}
|
| 17 |
+
|
| 18 |
+
void main() {
|
| 19 |
+
gl_FragColor = bloomStrength * ( lerpBloomFactor(bloomFactors[0]) * vec4(bloomTintColors[0], 1.0) * texture2D(blurTexture1, vUv) +
|
| 20 |
+
lerpBloomFactor(bloomFactors[1]) * vec4(bloomTintColors[1], 1.0) * texture2D(blurTexture2, vUv) +
|
| 21 |
+
lerpBloomFactor(bloomFactors[2]) * vec4(bloomTintColors[2], 1.0) * texture2D(blurTexture3, vUv) +
|
| 22 |
+
lerpBloomFactor(bloomFactors[3]) * vec4(bloomTintColors[3], 1.0) * texture2D(blurTexture4, vUv) +
|
| 23 |
+
lerpBloomFactor(bloomFactors[4]) * vec4(bloomTintColors[4], 1.0) * texture2D(blurTexture5, vUv) );
|
| 24 |
+
}
|
src/shaders/copy.glsl
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
uniform float opacity;
|
| 2 |
+
uniform sampler2D tDiffuse;
|
| 3 |
+
|
| 4 |
+
varying vec2 vUv;
|
| 5 |
+
|
| 6 |
+
void main() {
|
| 7 |
+
vec4 texel = texture2D( tDiffuse, vUv );
|
| 8 |
+
gl_FragColor = opacity * texel;
|
| 9 |
+
}
|
src/shaders/luminosityHighPass.glsl
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
uniform sampler2D tDiffuse;
|
| 2 |
+
uniform vec3 defaultColor;
|
| 3 |
+
uniform float defaultOpacity;
|
| 4 |
+
uniform float luminosityThreshold;
|
| 5 |
+
uniform float smoothWidth;
|
| 6 |
+
|
| 7 |
+
varying vec2 vUv;
|
| 8 |
+
|
| 9 |
+
void main() {
|
| 10 |
+
vec4 texel = texture2D( tDiffuse, vUv );
|
| 11 |
+
vec3 luma = vec3( 0.299, 0.587, 0.114 );
|
| 12 |
+
|
| 13 |
+
float v = dot( texel.xyz, luma );
|
| 14 |
+
|
| 15 |
+
vec4 outputColor = vec4( defaultColor.rgb, defaultOpacity );
|
| 16 |
+
|
| 17 |
+
float alpha = smoothstep( luminosityThreshold, luminosityThreshold + smoothWidth, v );
|
| 18 |
+
|
| 19 |
+
gl_FragColor = mix( outputColor, texel, alpha );
|
| 20 |
+
}
|
src/shaders/seperableBlur.glsl
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#include <common>
|
| 2 |
+
|
| 3 |
+
varying vec2 vUv;
|
| 4 |
+
uniform sampler2D colorTexture;
|
| 5 |
+
uniform vec2 texSize;
|
| 6 |
+
uniform vec2 direction;
|
| 7 |
+
|
| 8 |
+
float gaussianPdf(in float x, in float sigma) {
|
| 9 |
+
return 0.39894 * exp( -0.5 * x * x/( sigma * sigma))/sigma;
|
| 10 |
+
}
|
| 11 |
+
void main() {
|
| 12 |
+
vec2 invSize = 1.0 / texSize;
|
| 13 |
+
float fSigma = float(SIGMA);
|
| 14 |
+
float weightSum = gaussianPdf(0.0, fSigma);
|
| 15 |
+
vec3 diffuseSum = texture2D( colorTexture, vUv).rgb * weightSum;
|
| 16 |
+
for( int i = 1; i < KERNEL_RADIUS; i ++ ) {
|
| 17 |
+
float x = float(i);
|
| 18 |
+
float w = gaussianPdf(x, fSigma);
|
| 19 |
+
vec2 uvOffset = direction * invSize * x;
|
| 20 |
+
vec3 sample1 = texture2D( colorTexture, vUv + uvOffset).rgb;
|
| 21 |
+
vec3 sample2 = texture2D( colorTexture, vUv - uvOffset).rgb;
|
| 22 |
+
diffuseSum += (sample1 + sample2) * w;
|
| 23 |
+
weightSum += 2.0 * w;
|
| 24 |
+
}
|
| 25 |
+
gl_FragColor = vec4(diffuseSum/weightSum, 1.0);
|
| 26 |
+
}
|