initial commit. work on ui
48
.gitignore
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
|
||||
# nyc test coverage
|
||||
.nyc_output
|
||||
|
||||
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
|
||||
.grunt
|
||||
|
||||
# node-waf configuration
|
||||
.lock-wscript
|
||||
|
||||
# Compiled binary addons (http://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
|
||||
# Dependency directories
|
||||
node_modules
|
||||
jspm_packages
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
# Optional REPL history
|
||||
.node_repl_history
|
||||
|
||||
.DS_Store
|
||||
yarn.lock
|
||||
|
||||
exports/
|
||||
*.sw*
|
||||
/index.html
|
||||
/build
|
||||
|
||||
dump.rdb
|
||||
.ghpages
|
||||
8
README.md
Normal file
@@ -0,0 +1,8 @@
|
||||
# supersaber
|
||||
|
||||
From the [Supermedium team](https://supermedium.com).
|
||||
|
||||
```
|
||||
npm install
|
||||
npm run start
|
||||
```
|
||||
BIN
assets/images/BackspaceIcon.png
Executable file
|
After Width: | Height: | Size: 4.0 KiB |
BIN
assets/images/ButtonShadow.png
Executable file
|
After Width: | Height: | Size: 6.7 KiB |
BIN
assets/images/CheckmarkIcon.png
Executable file
|
After Width: | Height: | Size: 2.3 KiB |
BIN
assets/images/DismissIcon.png
Executable file
|
After Width: | Height: | Size: 3.3 KiB |
BIN
assets/images/EnterIcon.png
Executable file
|
After Width: | Height: | Size: 2.6 KiB |
BIN
assets/images/GlobalIcon.png
Executable file
|
After Width: | Height: | Size: 8.3 KiB |
BIN
assets/images/KeyShadow.png
Executable file
|
After Width: | Height: | Size: 6.7 KiB |
BIN
assets/images/ShiftActiveIcon.png
Executable file
|
After Width: | Height: | Size: 2.3 KiB |
BIN
assets/images/ShiftIcon.png
Executable file
|
After Width: | Height: | Size: 2.3 KiB |
BIN
assets/images/SwitchShadow.png
Executable file
|
After Width: | Height: | Size: 18 KiB |
BIN
assets/img/env/space/negx.jpg
vendored
Normal file
|
After Width: | Height: | Size: 604 KiB |
BIN
assets/img/env/space/negy.jpg
vendored
Normal file
|
After Width: | Height: | Size: 596 KiB |
BIN
assets/img/env/space/negz.jpg
vendored
Normal file
|
After Width: | Height: | Size: 611 KiB |
BIN
assets/img/env/space/posx.jpg
vendored
Normal file
|
After Width: | Height: | Size: 603 KiB |
BIN
assets/img/env/space/posy.jpg
vendored
Normal file
|
After Width: | Height: | Size: 619 KiB |
BIN
assets/img/env/space/posz.jpg
vendored
Normal file
|
After Width: | Height: | Size: 605 KiB |
BIN
assets/img/fx/fx1.png
Normal file
|
After Width: | Height: | Size: 2.3 KiB |
BIN
assets/img/fx/fx2.png
Normal file
|
After Width: | Height: | Size: 8.2 KiB |
BIN
assets/img/fx/fx3.png
Normal file
|
After Width: | Height: | Size: 6.8 KiB |
BIN
assets/img/fx/fx4.png
Normal file
|
After Width: | Height: | Size: 3.4 KiB |
BIN
assets/img/fx/fx5.png
Normal file
|
After Width: | Height: | Size: 3.6 KiB |
BIN
assets/img/fx/fx6.png
Normal file
|
After Width: | Height: | Size: 7.2 KiB |
BIN
assets/img/fx/fx7.png
Normal file
|
After Width: | Height: | Size: 7.7 KiB |
BIN
assets/img/fx/fx8.png
Normal file
|
After Width: | Height: | Size: 2.1 KiB |
BIN
assets/img/grid.png
Normal file
|
After Width: | Height: | Size: 163 B |
BIN
assets/img/play.png
Normal file
|
After Width: | Height: | Size: 26 KiB |
BIN
assets/img/play.psd
Normal file
BIN
assets/img/result.png
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
BIN
assets/img/result.psd
Normal file
BIN
assets/img/resulticons.png
Normal file
|
After Width: | Height: | Size: 3.2 KiB |
BIN
assets/img/slice.png
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
assets/img/smokeparticle.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
assets/img/tube.png
Normal file
|
After Width: | Height: | Size: 1.3 MiB |
BIN
assets/models/color.png
Normal file
|
After Width: | Height: | Size: 117 B |
BIN
assets/models/mr.png
Normal file
|
After Width: | Height: | Size: 100 B |
BIN
assets/sounds/ButtonClick.mp3
Executable file
BIN
assets/sounds/ButtonClickDisabled.mp3
Executable file
BIN
assets/sounds/InputClick.mp3
Executable file
BIN
assets/sounds/KeyDown.mp3
Executable file
BIN
assets/sounds/KeyIn.mp3
Executable file
BIN
assets/sounds/ToastShow.mp3
Executable file
BIN
assets/sounds/hover.ogg
Normal file
BIN
assets/sounds/sirkoto51-atmosphericambianceloop.wav
Normal file
BIN
assets/sounds/ui_scifi_hightech_error.wav
Normal file
11319
package-lock.json
generated
Normal file
67
package.json
Normal file
@@ -0,0 +1,67 @@
|
||||
{
|
||||
"private": true,
|
||||
"version": "1.0.0",
|
||||
"scripts": {
|
||||
"build": "cross-env NODE_ENV=production webpack",
|
||||
"start": "webpack-dev-server --host 0.0.0.0 --progress --colors --hot --inline --port 3000"
|
||||
},
|
||||
"dependencies": {
|
||||
"aframe-animation-component": "^4.1.1",
|
||||
"aframe-animation-timeline-component": "^1.3.1",
|
||||
"aframe-cubemap-component": "^0.1.2",
|
||||
"aframe-event-set-component": "^4.0.1",
|
||||
"aframe-gltf-part-component": "1.1.0",
|
||||
"aframe-haptics-component": "^1.4.1",
|
||||
"aframe-layout-component": "^4.3.1",
|
||||
"aframe-orbit-controls": "^1.2.0",
|
||||
"aframe-particle-system-component": "^1.0.11",
|
||||
"aframe-proxy-event-component": "^1.1.1",
|
||||
"aframe-slice9-component": "^1.0.0",
|
||||
"aframe-state-component": "^4.1.0",
|
||||
"algoliasearch": "^3.29.0",
|
||||
"ansi-html": "0.0.7",
|
||||
"autoprefixer": "^7.2.3",
|
||||
"babel": "6.23.0",
|
||||
"babel-core": "6.24.1",
|
||||
"babel-loader": "7.0.0",
|
||||
"babel-minify-webpack-plugin": "0.2.0",
|
||||
"babel-polyfill": "^6.26.0",
|
||||
"babel-preset-es2015": "6.24.1",
|
||||
"babel-preset-stage-0": "6.24.1",
|
||||
"body-parser": "1.18.2",
|
||||
"classnames": "^2.2.5",
|
||||
"css-loader": "^0.28.7",
|
||||
"html-entities": "^1.2.1",
|
||||
"html-minifier": "^3.5.11",
|
||||
"ip": "1.1.5",
|
||||
"json-loader": "^0.5.7",
|
||||
"nunjucks": "3.0.1",
|
||||
"postcss-loader": "^2.0.9",
|
||||
"uglify-js": "git://github.com/mishoo/UglifyJS2#harmony",
|
||||
"uglifyjs-webpack-plugin": "0.4.6",
|
||||
"webpack": "2.3.3",
|
||||
"webpack-glsl-loader": "^1.0.1",
|
||||
"webpack-sources": "1.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"cross-env": "4.0.0",
|
||||
"ghpages": "0.0.10",
|
||||
"semistandard": "10.0.0",
|
||||
"shx": "^0.2.2",
|
||||
"snazzy": "7.0.0",
|
||||
"superagent": "^3.8.2",
|
||||
"webpack-dev-server": "2.4.4"
|
||||
},
|
||||
"semistandard": {
|
||||
"globals": [
|
||||
"AFRAME",
|
||||
"THREE"
|
||||
],
|
||||
"ignore": [
|
||||
"build/**"
|
||||
]
|
||||
},
|
||||
"engines": {
|
||||
"node": "8.9.1"
|
||||
}
|
||||
}
|
||||
57
src/components/challenge-loader.js
Normal file
@@ -0,0 +1,57 @@
|
||||
AFRAME.registerComponent('song-loader', {
|
||||
schema: {
|
||||
challengeId: {type: 'string'},
|
||||
},
|
||||
|
||||
update: async function () {
|
||||
var challengeId = this.data.challengeId;
|
||||
var frameSystem = this.el.systems.frames;
|
||||
var punchSystem = this.el.systems.punches;
|
||||
|
||||
if (!challengeId) {
|
||||
return;
|
||||
}
|
||||
|
||||
const firstClient = new Client({ query: { id: challengeId } });
|
||||
const resp = await firstClient.challengeById(challengeId);
|
||||
const challenge = resp.performance;
|
||||
const auth = firstClient.serialize();
|
||||
|
||||
const client = new Client({ auth });
|
||||
this.el.emit('challengeloadstart');
|
||||
|
||||
console.log('Loading punches...');
|
||||
this.el.emit('challengeloadingpunches');
|
||||
const punches = await client.challengePunches(challenge);
|
||||
|
||||
console.log('Loading frames...');
|
||||
this.el.emit('challengeloadingframes');
|
||||
const frames = await client.challengeFrames(challenge);
|
||||
|
||||
history.pushState(
|
||||
'',
|
||||
challenge.song_name,
|
||||
updateQueryParam(window.location.href, 'challenge', this.data.challengeId)
|
||||
);
|
||||
|
||||
document.title = `Soundboxing - ${challenge.song_name}`;
|
||||
this.el.emit('challengeloaded', {
|
||||
frames: frames,
|
||||
punches: punches,
|
||||
title: challenge.song_name,
|
||||
videoId: challenge.youtube_id,
|
||||
});
|
||||
|
||||
console.log('Finished loading challenge data.');
|
||||
},
|
||||
});
|
||||
|
||||
function updateQueryParam(uri, key, value) {
|
||||
var re = new RegExp('([?&])' + key + '=.*?(&|$)', 'i');
|
||||
var separator = uri.indexOf('?') !== -1 ? '&' : '?';
|
||||
if (uri.match(re)) {
|
||||
return uri.replace(re, '$1' + key + '=' + value + '$2');
|
||||
} else {
|
||||
return uri + separator + key + '=' + value;
|
||||
}
|
||||
}
|
||||
6
src/components/console-shortcuts.js
Normal file
@@ -0,0 +1,6 @@
|
||||
AFRAME.registerComponent('console-shortcuts', {
|
||||
play: function() {
|
||||
window.scene = this.el;
|
||||
window.state = this.el.systems.state.state;
|
||||
},
|
||||
});
|
||||
117
src/components/debug-controller.js
Normal file
@@ -0,0 +1,117 @@
|
||||
/**
|
||||
* Keyboard bindings to control controller.
|
||||
* Position controller in front of camera.
|
||||
*/
|
||||
AFRAME.registerComponent('debug-controller', {
|
||||
schema: {
|
||||
enabled: { default: false },
|
||||
},
|
||||
|
||||
init: function() {
|
||||
var primaryHand;
|
||||
var secondaryHand;
|
||||
|
||||
if (!this.data.enabled && !AFRAME.utils.getUrlParameter('debug')) {
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('%c debug-controller enabled ', 'background: #111; color: red');
|
||||
|
||||
this.isTriggerDown = false;
|
||||
|
||||
primaryHand = document.getElementById('leftGloveContainer');
|
||||
secondaryHand = document.getElementById('rightGloveContainer');
|
||||
|
||||
primaryHand.setAttribute('position', { x: 0.2, y: 1.5, z: -0.5 });
|
||||
secondaryHand.setAttribute('position', { x: -0.2, y: 1.5, z: -0.5 });
|
||||
primaryHand.setAttribute('rotation', { x: 0, y: 0, z: 0 });
|
||||
secondaryHand.setAttribute('rotation', { x: 0, y: 0, z: 0 });
|
||||
|
||||
document.addEventListener('keydown', evt => {
|
||||
var primaryPosition;
|
||||
var primaryRotation;
|
||||
var secondaryPosition;
|
||||
|
||||
if (!evt.shiftKey) {
|
||||
return;
|
||||
}
|
||||
|
||||
// <space> for trigger.
|
||||
if (evt.keyCode === 32) {
|
||||
if (this.isTriggerDown) {
|
||||
primaryHand.emit('triggerup');
|
||||
this.isTriggerDown = false;
|
||||
} else {
|
||||
primaryHand.emit('triggerdown');
|
||||
this.isTriggerDown = true;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// <n> secondary grip.
|
||||
if (evt.keyCode === 78) {
|
||||
if (this.secondaryGripDown) {
|
||||
secondaryHand.emit('gripup');
|
||||
this.secondaryGripDown = false;
|
||||
} else {
|
||||
secondaryHand.emit('gripdown');
|
||||
this.secondaryGripDown = true;
|
||||
}
|
||||
}
|
||||
|
||||
// <m> primary grip.
|
||||
if (evt.keyCode === 77) {
|
||||
if (this.primaryGripDown) {
|
||||
primaryHand.emit('gripup');
|
||||
this.primaryGripDown = false;
|
||||
} else {
|
||||
primaryHand.emit('gripdown');
|
||||
this.primaryGripDown = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Position bindings.
|
||||
if (!evt.ctrlKey) {
|
||||
primaryPosition = primaryHand.object3D.position;
|
||||
if (evt.keyCode === 72) {
|
||||
primaryPosition.x -= 0.01;
|
||||
} // h.
|
||||
if (evt.keyCode === 74) {
|
||||
primaryPosition.y -= 0.01;
|
||||
} // j.
|
||||
if (evt.keyCode === 75) {
|
||||
primaryPosition.y += 0.01;
|
||||
} // k.
|
||||
if (evt.keyCode === 76) {
|
||||
primaryPosition.x += 0.01;
|
||||
} // l.
|
||||
if (evt.keyCode === 59 || evt.keyCode === 186) {
|
||||
primaryPosition.z -= 0.01;
|
||||
} // ;.
|
||||
if (evt.keyCode === 222) {
|
||||
primaryPosition.z += 0.01;
|
||||
} // ;.
|
||||
} else {
|
||||
secondaryPosition = secondaryHand.object3D.position;
|
||||
if (evt.keyCode === 72) {
|
||||
secondaryPosition.x -= 0.01;
|
||||
} // h.
|
||||
if (evt.keyCode === 74) {
|
||||
secondaryPosition.y -= 0.01;
|
||||
} // j.
|
||||
if (evt.keyCode === 75) {
|
||||
secondaryPosition.y += 0.01;
|
||||
} // k.
|
||||
if (evt.keyCode === 76) {
|
||||
secondaryPosition.x += 0.01;
|
||||
} // l.
|
||||
if (evt.keyCode === 59 || evt.keyCode === 186) {
|
||||
secondaryPosition.z -= 0.01;
|
||||
} // ;.
|
||||
if (evt.keyCode === 222) {
|
||||
secondaryPosition.z += 0.01;
|
||||
} // ;.
|
||||
}
|
||||
});
|
||||
},
|
||||
});
|
||||
31
src/components/debug-cursor.js
Normal file
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
* Log cursor events.
|
||||
*/
|
||||
AFRAME.registerComponent('debug-cursor', {
|
||||
init: function() {
|
||||
if (process.env.NODE_ENV === 'production') {
|
||||
return;
|
||||
}
|
||||
|
||||
this.el.addEventListener('mouseenter', evt => {
|
||||
this.log('mouseenter', evt.detail.intersectedEl, 'green');
|
||||
});
|
||||
|
||||
this.el.addEventListener('mouseleave', evt => {
|
||||
this.log('mouseleave', evt.detail.intersectedEl, 'red');
|
||||
});
|
||||
|
||||
this.el.addEventListener('click', evt => {
|
||||
this.log('click', evt.detail.intersectedEl, 'blue');
|
||||
});
|
||||
},
|
||||
|
||||
log: function(event, intersectedEl, color) {
|
||||
if (intersectedEl.id) {
|
||||
console.log(`%c[${event}] ${intersectedEl.id}`, `color: ${color}`);
|
||||
} else {
|
||||
console.log(`%c[${event}]`, `color: ${color}`);
|
||||
console.log(intersectedEl);
|
||||
}
|
||||
},
|
||||
});
|
||||
16
src/components/discolight.js
Normal file
@@ -0,0 +1,16 @@
|
||||
AFRAME.registerComponent('discolight', {
|
||||
schema: {
|
||||
color: { type: 'color' },
|
||||
speed: { default: 1.0 },
|
||||
},
|
||||
init: function() {
|
||||
this.color = new THREE.Color(this.data.color);
|
||||
this.hsl = this.color.getHSL();
|
||||
},
|
||||
tick: function(time, delta) {
|
||||
this.hsl.h += delta * 0.0001 * this.data.speed;
|
||||
if (this.hsl.l > 1.0) this.hsl.l = 0.0;
|
||||
this.color.setHSL(this.hsl.h, this.hsl.s, this.hsl.l);
|
||||
this.el.setAttribute('light', { color: this.color.getHex() });
|
||||
},
|
||||
});
|
||||
14
src/components/discotube.js
Normal file
@@ -0,0 +1,14 @@
|
||||
AFRAME.registerComponent('discotube', {
|
||||
schema: {
|
||||
speedX: { default: 1.0 },
|
||||
speedY: { default: 0.1 },
|
||||
},
|
||||
init: function() {
|
||||
this.material = this.el.object3D.children[0].material;
|
||||
},
|
||||
tick: function(time, delta) {
|
||||
if (this.material == null) return;
|
||||
this.material.map.offset.x -= delta * 0.0001 * this.data.speedX;
|
||||
this.material.map.offset.y -= delta * 0.0001 * this.data.speedY;
|
||||
},
|
||||
});
|
||||
10
src/components/keyboard-raycastable.js
Normal file
@@ -0,0 +1,10 @@
|
||||
AFRAME.registerComponent('keyboard-raycastable', {
|
||||
play: function() {
|
||||
var els;
|
||||
var i;
|
||||
els = this.el.querySelectorAll('*');
|
||||
for (i = 0; i < els.length; i++) {
|
||||
els[i].setAttribute('bind-toggle__raycastable', 'isSearchScreen');
|
||||
}
|
||||
},
|
||||
});
|
||||
26
src/components/play-audio.js
Normal file
@@ -0,0 +1,26 @@
|
||||
/**
|
||||
* Play audio element on event.
|
||||
*/
|
||||
AFRAME.registerComponent('play-audio', {
|
||||
schema: {
|
||||
audio: { type: 'string' },
|
||||
event: { type: 'string' },
|
||||
volume: { type: 'number', default: 1 },
|
||||
},
|
||||
|
||||
multiple: true,
|
||||
|
||||
init: function() {
|
||||
var audio;
|
||||
audio = document.querySelector(this.data.audio);
|
||||
audio.volume = this.data.volume;
|
||||
|
||||
this.el.addEventListener(this.data.event, evt => {
|
||||
if (!audio.paused) {
|
||||
audio.pause();
|
||||
audio.currentTime = 0;
|
||||
}
|
||||
audio.play();
|
||||
});
|
||||
},
|
||||
});
|
||||
13
src/components/play-button.js
Normal file
@@ -0,0 +1,13 @@
|
||||
AFRAME.registerComponent('play-button', {
|
||||
init: function() {
|
||||
var el = this.el;
|
||||
|
||||
el.addEventListener('click', () => {
|
||||
el.sceneEl.emit('playbuttonclick');
|
||||
});
|
||||
|
||||
el.sceneEl.addEventListener('youtubefinished', evt => {
|
||||
el.object3D.visible = false;
|
||||
});
|
||||
},
|
||||
});
|
||||
38
src/components/preloader.js
Normal file
@@ -0,0 +1,38 @@
|
||||
AFRAME.registerComponent('gpu-preloader', {
|
||||
init: function() {
|
||||
setTimeout(() => {
|
||||
this.preloadFont();
|
||||
this.preloadKeyboard();
|
||||
}, 1000);
|
||||
|
||||
setTimeout(() => {
|
||||
this.el.object3D.visible = false;
|
||||
}, 2000);
|
||||
},
|
||||
|
||||
preloadFont: function() {
|
||||
var text;
|
||||
text = document.querySelector('[text]');
|
||||
if (text.components.text.texture) {
|
||||
this.preloadTexture(text.components.text.texture);
|
||||
} else {
|
||||
text.addEventListener('textfontset', () => {
|
||||
this.preloadTexture(text.components.text.texture);
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
preloadKeyboard: function() {
|
||||
var kbd;
|
||||
kbd = document.getElementById('kb');
|
||||
kbd.object3D.traverse(node => {
|
||||
if (node.material && node.material.map) {
|
||||
this.preloadTexture(node.material.map);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
preloadTexture: function(texture) {
|
||||
this.el.sceneEl.renderer.setTexture2D(texture, 0);
|
||||
},
|
||||
});
|
||||
1
src/components/raycastable.js
Normal file
@@ -0,0 +1 @@
|
||||
AFRAME.registerComponent('raycastable', {});
|
||||
75
src/components/recenter.js
Normal file
@@ -0,0 +1,75 @@
|
||||
/**
|
||||
* Pivot the scene when user enters VR to face the links.
|
||||
*/
|
||||
AFRAME.registerComponent('recenter', {
|
||||
init: function() {
|
||||
var sceneEl = this.el.sceneEl;
|
||||
this.matrix = new THREE.Matrix4();
|
||||
this.frustum = new THREE.Frustum();
|
||||
this.rotationOffset = 0;
|
||||
this.euler = new THREE.Euler();
|
||||
this.euler.order = 'YXZ';
|
||||
this.menuPosition = new THREE.Vector3();
|
||||
this.recenter = this.recenter.bind(this);
|
||||
this.checkInViewAfterRecenter = this.checkInViewAfterRecenter.bind(this);
|
||||
// Delay to make sure we have a valid pose.
|
||||
sceneEl.addEventListener('enter-vr', () => {
|
||||
setTimeout(() => {
|
||||
this.recenter();
|
||||
}, 100);
|
||||
});
|
||||
// User can also recenter the menu manually.
|
||||
sceneEl.addEventListener('menudown', () => {
|
||||
this.recenter();
|
||||
});
|
||||
sceneEl.addEventListener('thumbstickdown', () => {
|
||||
this.recenter();
|
||||
});
|
||||
},
|
||||
|
||||
recenter: function(skipCheck) {
|
||||
var euler = this.euler;
|
||||
euler.setFromRotationMatrix(
|
||||
this.el.sceneEl.camera.el.object3D.matrixWorld,
|
||||
'YXZ'
|
||||
);
|
||||
this.el.object3D.rotation.y = euler.y + this.rotationOffset;
|
||||
// Check if the menu is in camera frustum in next tick after a frame has rendered.
|
||||
if (skipCheck) {
|
||||
return;
|
||||
}
|
||||
setTimeout(this.checkInViewAfterRecenter, 0);
|
||||
},
|
||||
|
||||
/*
|
||||
* Sometimes the quaternion returns the yaw in the [-180, 180] range.
|
||||
* Check if the menu is in the camera frustum after recenter it to
|
||||
* decide if we apply an offset or not.
|
||||
*/
|
||||
checkInViewAfterRecenter: function() {
|
||||
var camera = this.el.sceneEl.camera;
|
||||
var frustum = this.frustum;
|
||||
var menu = document.querySelector('#menu');
|
||||
var menuPosition = this.menuPosition;
|
||||
camera.updateMatrix();
|
||||
camera.updateMatrixWorld();
|
||||
frustum.setFromMatrix(
|
||||
this.matrix.multiplyMatrices(
|
||||
camera.projectionMatrix,
|
||||
camera.matrixWorldInverse
|
||||
)
|
||||
);
|
||||
menu.object3D.updateMatrixWorld();
|
||||
menuPosition.setFromMatrixPosition(menu.object3D.matrixWorld);
|
||||
if (frustum.containsPoint(menuPosition)) {
|
||||
return;
|
||||
}
|
||||
this.rotationOffset = this.rotationOffset === 0 ? Math.PI : 0;
|
||||
// Recenter again with the new offset.
|
||||
this.recenter(true);
|
||||
},
|
||||
|
||||
remove: function() {
|
||||
this.el.sceneEl.removeEventListener('enter-vr', this.recenter);
|
||||
},
|
||||
});
|
||||
42
src/components/search.js
Normal file
@@ -0,0 +1,42 @@
|
||||
var algoliasearch = require('algoliasearch/lite');
|
||||
|
||||
var client = algoliasearch('QULTOY3ZWU', 'be07164192471df7e97e6fa70c1d041d');
|
||||
var index = client.initIndex('supersaber');
|
||||
|
||||
/**
|
||||
* Search (including the initial list of popular searches).
|
||||
*/
|
||||
AFRAME.registerComponent('search', {
|
||||
init: function() {
|
||||
this.eventDetail = {results: []};
|
||||
this.queryObject = {query: ''};
|
||||
|
||||
// Populate popular.
|
||||
this.search('');
|
||||
},
|
||||
|
||||
search: function (query) {
|
||||
this.queryObject.query = query;
|
||||
index.search(this.queryObject, (err, content) => {
|
||||
this.eventDetail.results = content.hits;
|
||||
console.log(content.hits);
|
||||
this.el.sceneEl.emit('searchresults', this.eventDetail);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Click listener for search result.
|
||||
*/
|
||||
AFRAME.registerComponent('search-result', {
|
||||
init: function() {
|
||||
var el = this.el;
|
||||
|
||||
this.eventDetail = {};
|
||||
el.addEventListener('click', () => {
|
||||
this.eventDetail.challengeId = el.getAttribute('data-song-id');
|
||||
this.eventDetail.title = el.getAttribute('data-title');
|
||||
el.sceneEl.emit('challengeset', this.eventDetail);
|
||||
});
|
||||
},
|
||||
});
|
||||
10
src/components/toggle-pause-play.js
Normal file
@@ -0,0 +1,10 @@
|
||||
AFRAME.registerComponent('toggle-pause-play', {
|
||||
schema: {
|
||||
isPlaying: { default: false },
|
||||
},
|
||||
|
||||
update: function() {
|
||||
const action = this.data.isPlaying ? 'pause' : 'play';
|
||||
parent.postMessage(JSON.stringify({ verify: 'game-action', action }), '*');
|
||||
},
|
||||
});
|
||||
48
src/index.html
Normal file
@@ -0,0 +1,48 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Supersaber</title>
|
||||
<script src="vendor/aframe.dev.js"></script>
|
||||
<script src="build/build.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<audio id="hoverSound" src="/assets/sounds/hover.ogg"></audio>
|
||||
|
||||
<a-scene bind__challenge-loader="challengeId: challenge.id"
|
||||
bind__embedded="embedded"
|
||||
challenge-loader
|
||||
console-shortcuts
|
||||
search>
|
||||
<a-assets timeout="10000">
|
||||
<a-mixin id="raycaster"
|
||||
raycaster="objects: [raycastable], [data-ui]; far: 2"></a-mixin>
|
||||
<img id="playImg" src="assets/img/play.png">
|
||||
<img id="resultImg" src="assets/img/result.png">
|
||||
<img id="resultIconsImg" src="assets/img/resulticons.png">
|
||||
<img id="tubeImg" src="assets/img/tube.png">
|
||||
<img id="gridImg" src="assets/img/grid.png">
|
||||
<img id="sliceImg" src="assets/img/slice.png">
|
||||
<a-mixin id="slice" slice9="color: #050505; src: #sliceImg; left: 50; right: 52; top: 50; bottom: 52; padding: 0.04"></a-mixin>
|
||||
<a-mixin id="font" text="font: exo2semibold; letterSpacing: -1"></a-mixin>
|
||||
<a-mixin id="textFont" text="font: exo2semibold; letterSpacing: -1"></a-mixin>
|
||||
</a-assets>
|
||||
|
||||
<a-entity id="dustParticles" particle-system="maxAge: 100; positionSpread: 5 5 5; type: 2; rotationAxis: x; rotationAngle: 0.001; accelerationValue: 0.001 0.001 0.001; accelerationSpread: 0.001 0.001 0.001; velocityValue: 0.001 0.001 0.001; velocitySpread: 0.0125 0.0125 0.0125; color: #ffffff; size: 0.1; opacity: 0.85; direction: 0; particleCount: 150; texture: assets/img/smokeparticle.png"></a-entity>
|
||||
|
||||
<a-entity id="container">
|
||||
{% include './templates/environment.html' %}
|
||||
{% include './templates/gameUi.html' %}
|
||||
{% include './templates/menu.html' %}
|
||||
</a-entity>
|
||||
|
||||
<a-entity id="cameraRig">
|
||||
<a-entity id="camera" camera look-controls="enabled: false" orbit-controls="target: 0 1.6 -0.5; maxDistance: 20; initialPosition: 0 1.6 0"></a-entity>
|
||||
<a-entity id="leftHand"></a-entity>
|
||||
<a-entity id="rightHand"></a-entity>
|
||||
</a-entity>
|
||||
|
||||
<a-entity id="mouseCursor" mixin="raycaster" cursor="rayOrigin: mouse" bind__isPlaying="!inVR"></a-entity>
|
||||
</a-scene>
|
||||
</body>
|
||||
</html>
|
||||
17
src/index.js
Normal file
@@ -0,0 +1,17 @@
|
||||
require('babel-polyfill');
|
||||
|
||||
function requireAll (req) { req.keys().forEach(req); }
|
||||
|
||||
require('aframe-animation-component');
|
||||
require('aframe-cubemap-component');
|
||||
require('aframe-event-set-component');
|
||||
require('aframe-haptics-component');
|
||||
require('aframe-layout-component');
|
||||
require('aframe-orbit-controls');
|
||||
require('aframe-particle-system-component');
|
||||
require('aframe-proxy-event-component');
|
||||
require('aframe-state-component');
|
||||
require('aframe-slice9-component');
|
||||
|
||||
requireAll(require.context('./components/', true, /\.js$/));
|
||||
requireAll(require.context('./state/', true, /\.js$/));
|
||||
3115
src/lib/aframe-material.js
Executable file
22
src/lib/soundpool.js
Normal file
@@ -0,0 +1,22 @@
|
||||
module.exports = function SoundPool (src, volume, size) {
|
||||
var currSound = 0;
|
||||
var i;
|
||||
var pool = [];
|
||||
var sound;
|
||||
|
||||
for (i = 0; i < size; i++) {
|
||||
sound = new Audio(src);
|
||||
sound.volume = volume;
|
||||
sound.load();
|
||||
pool.push(sound);
|
||||
}
|
||||
|
||||
return {
|
||||
play: function () {
|
||||
if (pool[currSound].currentTime === 0 || pool[currSound].ended) {
|
||||
pool[currSound].play();
|
||||
}
|
||||
currSound = (currSound + 1) % size;
|
||||
}
|
||||
};
|
||||
};
|
||||
141
src/state/index.js
Normal file
@@ -0,0 +1,141 @@
|
||||
var hasInitialChallenge = !!AFRAME.utils.getUrlParameter('challenge');
|
||||
|
||||
AFRAME.registerState({
|
||||
initialState: {
|
||||
challenge: {
|
||||
id: AFRAME.utils.getUrlParameter('challenge'),
|
||||
isLoading: false,
|
||||
loadingText: ''
|
||||
},
|
||||
discoLightsOn: true,
|
||||
discotube: {speedX: -0.05, speedY: -0.1},
|
||||
featuredPanelShowing: !hasInitialChallenge,
|
||||
inVR: false,
|
||||
isChallengeScreen: hasInitialChallenge,
|
||||
isFeaturedScreen: !hasInitialChallenge,
|
||||
isSearchScreen: false,
|
||||
maxStreak: 0,
|
||||
menuActive: true,
|
||||
playButtonShowing: hasInitialChallenge,
|
||||
playButtonText: 'Play',
|
||||
score: 0,
|
||||
scoreText: '',
|
||||
screen: hasInitialChallenge ? 'challenge' : 'featured',
|
||||
screenHistory: [],
|
||||
searchResults: [],
|
||||
streak: 0
|
||||
},
|
||||
|
||||
handlers: {
|
||||
/**
|
||||
* Update search results. Will automatically render using `bind-for` (menu.html).
|
||||
*/
|
||||
searchresults: function (state, payload) {
|
||||
var i;
|
||||
state.searchResults.length = 0;
|
||||
for (i = 0; i < 6; i++) {
|
||||
state.searchResults.push(payload.results[i]);
|
||||
}
|
||||
},
|
||||
|
||||
setscreen: function (state, payload) {
|
||||
console.log('setscreen: ' + payload.screen);
|
||||
if (state.screen === payload.screen) {
|
||||
return;
|
||||
}
|
||||
switch (payload.screen) {
|
||||
case 'challenge':
|
||||
case 'featured':
|
||||
case 'search':
|
||||
state.screenHistory.push(state.screen);
|
||||
state.screen = payload.screen;
|
||||
break;
|
||||
default:
|
||||
console.log('Unknown screen set: ' + payload.screen);
|
||||
}
|
||||
},
|
||||
|
||||
popscreen: function (state, payload) {
|
||||
var prevScreen = state.screenHistory.pop();
|
||||
if (!prevScreen) {
|
||||
return;
|
||||
}
|
||||
state.screen = prevScreen;
|
||||
console.log('popscreen: ' + state.screen);
|
||||
},
|
||||
|
||||
challengeloaded: function (state, payload) {
|
||||
state.challenge.isLoading = false;
|
||||
state.isLoadingFrames = false;
|
||||
state.isLoadingPunches = false;
|
||||
state.score = 0;
|
||||
},
|
||||
|
||||
challengeloadstart: function (state, payload) {
|
||||
state.challenge.isLoading = true;
|
||||
},
|
||||
|
||||
challengeloadingframes: function (state, payload) {
|
||||
state.challenge.isLoadingPunches = false;
|
||||
state.challenge.isLoadingFrames = true;
|
||||
},
|
||||
|
||||
challengeloadingpunches: function (state, payload) {
|
||||
state.challenge.isLoadingPunches = true;
|
||||
state.challenge.isLoadingFrames = false;
|
||||
},
|
||||
|
||||
challengeset: function (state, payload) {
|
||||
state.challenge.id = payload.challengeId;
|
||||
state.score = 0;
|
||||
state.streak = 0;
|
||||
state.maxStreak = 0;
|
||||
state.menuActive = false;
|
||||
|
||||
this.setscreen(state, {screen: 'challenge'});
|
||||
},
|
||||
|
||||
playbuttonclick: function (state) {
|
||||
state.menuActive = false;
|
||||
},
|
||||
|
||||
searchblur: function (state) {
|
||||
this.popscreen(state);
|
||||
},
|
||||
|
||||
searchfocus: function (state) {
|
||||
this.setscreen(state, {screen: 'search'});
|
||||
},
|
||||
|
||||
togglemenu: function (state) {
|
||||
state.menuActive = !state.menuActive;
|
||||
},
|
||||
|
||||
togglediscolights: function (state, payload) {
|
||||
state.discoLightsOn = !state.discoLightsOn;
|
||||
},
|
||||
|
||||
'enter-vr': function (state, payload) {
|
||||
state.inVR = true;
|
||||
},
|
||||
|
||||
'exit-vr': function (state, payload) {
|
||||
state.inVR = false;
|
||||
}
|
||||
},
|
||||
|
||||
computeState: function (state) {
|
||||
state.scoreText = `Streak: ${state.streak} / Max Streak: ${state.maxStreak} / Score: ${state.score}`;
|
||||
state.isFeaturedScreen = state.screen === 'featured';
|
||||
state.isSearchScreen = state.screen === 'search';
|
||||
state.isChallengeScreen = state.screen === 'challenge';
|
||||
|
||||
state.featuredPanelShowing = state.isFeaturedScreen || (state.isChallengeScreen && state.menuActive);
|
||||
|
||||
if (state.challenge.isLoading) {
|
||||
state.loadingText = 'Loading challenge...';
|
||||
} else {
|
||||
state.loadingText = '';
|
||||
}
|
||||
}
|
||||
});
|
||||
42
src/templates/environment.html
Normal file
@@ -0,0 +1,42 @@
|
||||
<a-entity id="sky"
|
||||
cubemap="folder: /assets/img/env/space/"></a-entity>
|
||||
|
||||
<a-entity id="skyFade"
|
||||
bind__visible="menuActive"
|
||||
bind__isPlaying="menuActive"
|
||||
geometry="primitive: box; width: 50; height: 50; depth: 50"
|
||||
material="shader: flat; color: #111; opacity: 0.6; side: back"></a-entity>
|
||||
|
||||
<a-entity id="tube"
|
||||
geometry="primitive:cylinder;radius:3;height:100;openEnded:true"
|
||||
material="fog: false; depthWrite: false; src:#tubeImg;transparent:true;side:back;repeat:1 5"
|
||||
position="0 1.5 -40"
|
||||
rotation="90 0 0"
|
||||
bind__discotube="speedX: discotube.speedX; speedY: discotube.speedY"></a-entity>
|
||||
|
||||
<a-plane id="floor"
|
||||
width="40"
|
||||
height="40"
|
||||
rotation="-90 0 0"
|
||||
material="src:#gridImg; transparent: true; color: #f9e3fb; repeat: 80 80; depthWrite:false">
|
||||
<a-plane position="0 0 -0.01"
|
||||
width="2"
|
||||
height="2"
|
||||
material="color: #000; transparent: true; opacity: 0.7"></a-plane>
|
||||
</a-plane>
|
||||
|
||||
<!-- Disco lights -->
|
||||
<a-entity light="type: directional; intensity: 1"
|
||||
position="0 -2 1"
|
||||
discolight="color: #fda6f7; speed: 2.0"
|
||||
bind__visible="discoLightsOn"></a-entity>
|
||||
<a-entity light="type: directional; intensity: 1.6"
|
||||
position="-0 2 .6"
|
||||
discolight="color: #7a9efc; speed: 0.01"
|
||||
bind__visible="discoLightsOn"></a-entity>
|
||||
<!-- Default lights -->
|
||||
<a-entity light="type: ambient; color: #BBB"
|
||||
bind__visible="!discoLightsOn"></a-entity>
|
||||
<a-entity light="type: directional; color: #FFF; intensity: 0.6"
|
||||
position="-0.5 1 1"
|
||||
bind__visible="!discoLightsOn"></a-entity>
|
||||
36
src/templates/gameUi.html
Normal file
@@ -0,0 +1,36 @@
|
||||
<a-entity id="gameInfo"
|
||||
bind__visible="isChallengeScreen"
|
||||
position="0 0 -80"
|
||||
scale="100 100 100">
|
||||
<a-entity mixin="textFont"
|
||||
text="align:right; width:1.25; value:SCORE; color:#d6d955; anchor:right; letterSpacing:-2"
|
||||
position="-0.089 0.183 -1.01"
|
||||
bind__visible="!challenge.isLoading"></a-entity>
|
||||
<a-entity mixin="textFont"
|
||||
text="width:0.9; value:STREAK; color:#f95895; letterSpacing:-2; anchor:left"
|
||||
position="0.097 0.183 -1.01"
|
||||
bind__visible="!challenge.isLoading"></a-entity>
|
||||
<a-entity mixin="textFont"
|
||||
text="width:0.7; value:MAX; color:#f95895; anchor:left; letterSpacing:-2"
|
||||
position="0.101 0.1 -1.01"
|
||||
bind__visible="!challenge.isLoading"></a-entity>
|
||||
|
||||
<a-entity id="score"
|
||||
bind__text="value: score"
|
||||
mixin="textFont"
|
||||
text="align:right; width:2; color:#feffc1; anchor:right; letterSpacing:-2"
|
||||
position="-0.08 0.132 -1"
|
||||
bind__visible="!challenge.isLoading"></a-entity>
|
||||
<a-entity id="streak"
|
||||
bind__text="value: streak"
|
||||
mixin="textFont"
|
||||
text="width:1.25; color:#ffbdd6; anchor:left; letterSpacing:-2"
|
||||
position="0.098 0.144 -1"
|
||||
bind__visible="!challenge.isLoading"></a-entity>
|
||||
<a-entity id="maxStreak"
|
||||
bind__text="value: maxStreak"
|
||||
mixin="textFont"
|
||||
text="width:0.8; color:#ffbdd6; anchor:left; letterSpacing:-2"
|
||||
position="0.1 0.074 -1"
|
||||
bind__visible="!challenge.isLoading"></a-entity>
|
||||
</a-entity>
|
||||
101
src/templates/menu.html
Normal file
@@ -0,0 +1,101 @@
|
||||
{% macro searchResults () %}
|
||||
<a-entity id="searchResultList"
|
||||
layout="type: box; columns: 3; marginRow: 0.25; marginColumn: 0.85"
|
||||
bind-for="for: item; in: searchResults; key: id; template: #searchResultTemplate">
|
||||
</a-entity>
|
||||
|
||||
{% raw %}
|
||||
<template id="searchResultTemplate">
|
||||
<a-entity class="searchResult" search-result data-song-id="{{ item.id }}" data-title="{{ item.songName }}">
|
||||
<a-entity>
|
||||
<a-entity class="searchResultSlice"
|
||||
geometry="primitive: plane; width: 0.8; height: 0.2"
|
||||
material="shader:flat; transparent: true; src:#resultImg; color: #BBB"
|
||||
position="0 -0.13 -0.01"
|
||||
play-audio="event: mouseenter; audio: #hoverSound; volume: 0.03"
|
||||
animation__mouseenter="property: material.color; from: #BBB; to: #FFF; startEvents: mouseenter; pauseEvents: mouseleave; dur: 150"
|
||||
animation__mouseleave="property: material.color; from: #FFF; to: #BBB; startEvents: mouseleave; pauseEvents: mouseenter; dur: 150"
|
||||
raycastable></a-entity>
|
||||
<a-entity class="searchResultIcons"
|
||||
geometry="primitive: plane; width: 0.8; height: 0.2"
|
||||
material="shader: flat; transparent: true; src: #resultIconsImg; color: #FFF"
|
||||
position="0 -0.13 -0.005"
|
||||
raycastable></a-entity>
|
||||
</a-entity>
|
||||
<a-entity class="searchResultAuthor" mixin="textFont" text="wrapCount: 48; align: center; color: #54c2fd; value: {{ item.subSongName }}" position="0 -0.060 0"></a-entity>
|
||||
<a-entity class="searchResultTitle" mixin="textFont" text="align: center; color: #FFF; wrapCount: 35; value: {{ item.songName }}" position="0 -0.111 0"></a-entity>
|
||||
<a-entity class="searchResultDownloads" mixin="textFont" text="color: #cc0856; anchor: left; value: {{ item.downloads }}" position="0.192 -0.184 0"></a-entity>
|
||||
</a-entity>
|
||||
</template>
|
||||
{% endraw %}
|
||||
{% endmacro %}
|
||||
|
||||
<a-entity id="menu"
|
||||
bind__visible="menuActive"
|
||||
position="0 1 -1">
|
||||
<a-input id="search"
|
||||
event-set__1="_event: mouseenter; background-color: #333; placeholder-color: #FFF"
|
||||
event-set__2="_event: mouseleave; background-color: #182029; placeholder-color: #F3D765"
|
||||
event-set__3="_event: blur; value:"
|
||||
event-set__4="_event: challengeset; value:"
|
||||
position="-0.42 0.32 -0.1"
|
||||
scale="0.65 0.65 0.65"
|
||||
placeholder=" Search challenges..."
|
||||
play-audio="event: mouseenter; audio: #hoverSound; volume: 0.03"
|
||||
color="#FFF"
|
||||
cursorColor="#f3d765"
|
||||
font="exo2semibold"
|
||||
letter-spacing="-1"
|
||||
placeholder-color="#f3d765"
|
||||
background-color="#182029"
|
||||
bind-toggle__raycastable="menuActive && !isSearchScreen"
|
||||
proxy-event="event: click; to: a-scene; as: searchfocus; captureBubbles: true"
|
||||
search
|
||||
width="1.3"></a-input>
|
||||
<a-plane id="play"
|
||||
play-button
|
||||
play-audio="event: mouseenter; audio: #hoverSound; volume: 0.03"
|
||||
position="0 0.15 0"
|
||||
material="shader: flat; src: #playImg; transparent: true; color: #BBB"
|
||||
width="0.512"
|
||||
height="0.256"
|
||||
raycaster="far:2"
|
||||
animation__mouseenter1="property: material.color; from: #BBB; to: #FFF; startEvents: mouseenter; pauseEvents: mouseleave; dur: 150"
|
||||
animation__mouseleave1="property: material.color; from: #FFF; to: #BBB; startEvents: mouseleave; pauseEvents: mouseenter; dur: 150"
|
||||
animation__mouseenter2="property: scale; from: 1 1 1; to: 1.1 1.1 1.1; startEvents: mouseenter; pauseEvents: mouseleave; dur: 150"
|
||||
animation__mouseleave2="property: scale; to: 1 1 1; from: 1.1 1.1 1.1; startEvents: mouseleave; pauseEvents: mouseenter; dur: 150"
|
||||
bind-toggle__raycastable="playButtonShowing"
|
||||
bind__visible="playButtonShowing"></a-plane>
|
||||
<a-entity id="keyboardContaner"
|
||||
position="0 0 1"
|
||||
scale="0.4 0.4 0.4"
|
||||
bind__visible="isSearchScreen">
|
||||
<a-keyboard id="kb"
|
||||
bind__keyboard="isOpen: isSearchScreen"
|
||||
bind__isPlaying="isSearchScreen"
|
||||
event__set="_event: diddismiss; visible: false"
|
||||
proxy-event="event: diddismiss; to: a-scene; as: searchblur"
|
||||
keyboard-raycastable></a-keyboard>
|
||||
</a-entity>
|
||||
|
||||
<a-entity id="searchResultsContainer" position="-0.85 0.7 0">
|
||||
{{ searchResults() }}
|
||||
</a-entity>
|
||||
</a-entity>
|
||||
|
||||
<a-entity id="songInfo"
|
||||
bind__visible="isChallengeScreen"
|
||||
position="0 -0.25 0">
|
||||
<a-entity id="loadingText"
|
||||
bind__text="value: loadingText"
|
||||
mixin="textFont"
|
||||
text="align: center; width: 1.25"
|
||||
position="0 1 -1"
|
||||
rotation="-25 0 0"
|
||||
bind__visible="challenge.isLoading"></a-entity>
|
||||
<a-entity id="title"
|
||||
mixin="textFont"
|
||||
text="align: center; width: 2.50; wrapCount: 80"
|
||||
position="0 0.9 -1"
|
||||
rotation="-25 0 0"></a-entity>
|
||||
</a-entity>
|
||||
25
src/templates/ui.html
Normal file
@@ -0,0 +1,25 @@
|
||||
{% macro searchresult (featured) %}
|
||||
<a-entity class="searchResult" search-result visible="{{ featured and 'true' or 'false' }}">
|
||||
<a-entity>
|
||||
<a-entity class="searchResultSlice"
|
||||
geometry="primitive: plane; width: 0.8; height: 0.2"
|
||||
material="shader:flat; transparent: true; src:#resultImg; color: #BBB"
|
||||
position="0 -0.13 -0.01"
|
||||
play-audio="event: mouseenter; audio: #hoverSound; volume: 0.03"
|
||||
animation__mouseenter="property: material.color; from: #BBB; to: #FFF; startEvents: mouseenter; pauseEvents: mouseleave; dur: 150"
|
||||
animation__mouseleave="property: material.color; from: #FFF; to: #BBB; startEvents: mouseleave; pauseEvents: mouseenter; dur: 150"
|
||||
bind-toggle__raycastable="{{ featured and 'featuredPanelShowing' or 'isSearchScreen' }}"></a-entity>
|
||||
<a-entity class="searchResultIcons"
|
||||
geometry="primitive: plane; width: 0.8; height: 0.2"
|
||||
material="shader:flat; transparent: true; src:#resultIconsImg; color: #FFF"
|
||||
position="0 -0.13 -0.005"
|
||||
bind-toggle__visible="{{ featured and 'featuredPanelShowing' or 'isSearchScreen' }}"
|
||||
></a-entity>
|
||||
</a-entity>
|
||||
<a-entity class="searchResultAuthor" mixin="textFont" text="wrapCount: 48; align: center; color: #54c2fd" position="0 -0.060 0"></a-entity>
|
||||
<a-entity class="searchResultTitle" mixin="textFont" text="align: center; color: #FFF; wrapCount: 35;" position="0 -0.111 0"></a-entity>
|
||||
<a-entity class="searchResultTime" mixin="textFont" text="color: #54c2fd; anchor: left;" position="-0.192 -0.184 0"></a-entity>
|
||||
<a-entity class="searchResultPunches" mixin="textFont" text="color: #fff155; anchor: left;" position="0.01 -0.184 0"></a-entity>
|
||||
<a-entity class="searchResultFavorites" mixin="textFont" text="color: #cc0856; anchor: left;" position="0.192 -0.184 0"></a-entity>
|
||||
</a-entity>
|
||||
{% endmacro %}
|
||||
83261
vendor/aframe.dev.js
vendored
Normal file
99
webpack.config.js
Normal file
@@ -0,0 +1,99 @@
|
||||
var MinifyPlugin = require('babel-minify-webpack-plugin');
|
||||
var Nunjucks = require('nunjucks');
|
||||
var fs = require('fs');
|
||||
var htmlMinify = require('html-minifier').minify;
|
||||
var ip = require('ip');
|
||||
var path = require('path');
|
||||
var webpack = require('webpack');
|
||||
|
||||
// Set up templating.
|
||||
var nunjucks = Nunjucks.configure(path.resolve(__dirname, 'src'), {
|
||||
noCache: true,
|
||||
});
|
||||
nunjucks.addGlobal('HOST', ip.address());
|
||||
|
||||
// Initial Nunjucks render.
|
||||
var context = {IS_PRODUCTION: process.env.NODE_ENV === 'production'};
|
||||
fs.writeFileSync('index.html', nunjucks.render('index.html', context));
|
||||
|
||||
// For development, watch HTML for changes to compile Nunjucks.
|
||||
// The production Express server will handle Nunjucks by itself.
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
fs.watch('src', { recursive: true }, (eventType, filename) => {
|
||||
if (filename.indexOf('.html') === -1) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
fs.writeFileSync('index.html', nunjucks.render('index.html', context));
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
PLUGINS = [new webpack.EnvironmentPlugin(['NODE_ENV'])];
|
||||
if (process.env.NODE_ENV === 'production') {
|
||||
PLUGINS.push(
|
||||
new MinifyPlugin(
|
||||
{
|
||||
booleans: true,
|
||||
builtIns: true,
|
||||
consecutiveAdds: true,
|
||||
deadcode: true,
|
||||
evaluate: false,
|
||||
flipComparisons: true,
|
||||
guards: true,
|
||||
infinity: true,
|
||||
mangle: false,
|
||||
memberExpressions: true,
|
||||
mergeVars: true,
|
||||
numericLiterals: true,
|
||||
propertyLiterals: true,
|
||||
regexpConstructors: true,
|
||||
removeUndefined: true,
|
||||
replace: true,
|
||||
simplify: true,
|
||||
simplifyComparisons: true,
|
||||
typeConstructors: true,
|
||||
undefinedToVoid: true,
|
||||
keepFnName: true,
|
||||
keepClassName: true,
|
||||
tdz: true,
|
||||
},
|
||||
{
|
||||
sourceMap: 'source-map',
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
devtool: '#inline-source-map',
|
||||
devServer: {
|
||||
disableHostCheck: true,
|
||||
},
|
||||
entry: './src/index.js',
|
||||
output: {
|
||||
path: __dirname,
|
||||
filename: 'build/build.js',
|
||||
},
|
||||
plugins: PLUGINS,
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.js/,
|
||||
exclude: path =>
|
||||
path.indexOf('node_modules') !== -1 || path.indexOf('panel') !== -1,
|
||||
loader: 'babel-loader',
|
||||
},
|
||||
{
|
||||
test: /\.glsl/,
|
||||
exclude: /(node_modules)/,
|
||||
loader: 'webpack-glsl-loader',
|
||||
},
|
||||
],
|
||||
},
|
||||
resolve: {
|
||||
modules: [path.join(__dirname, 'node_modules')],
|
||||
},
|
||||
};
|
||||