added particles on beat slicing (fixes #54)
This commit is contained in:
BIN
assets/img/spark.png
Normal file
BIN
assets/img/spark.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.3 KiB |
1
assets/models/sabercut.json
Normal file
1
assets/models/sabercut.json
Normal file
File diff suppressed because one or more lines are too long
25
package-lock.json
generated
25
package-lock.json
generated
@@ -99,21 +99,11 @@
|
||||
"animejs": "2.2.0"
|
||||
}
|
||||
},
|
||||
"aframe-animation-timeline-component": {
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/aframe-animation-timeline-component/-/aframe-animation-timeline-component-1.5.0.tgz",
|
||||
"integrity": "sha512-mVxIH5qjik3qnbmeuXnYWFyKkcdLdZEQIFIFky1Z3QY8gZFsbCgocImwAx9UV9DTsIywa/lt3nXyNrpgOQgNCg=="
|
||||
},
|
||||
"aframe-audioanalyser-component": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/aframe-audioanalyser-component/-/aframe-audioanalyser-component-4.0.1.tgz",
|
||||
"integrity": "sha512-jTkt9umD4SoRmW6vlJfm2zG9fV8pzecNzTdJYn12w8hpZ0lp3BtFSKqTDy0RE3dqAsDJo9AU72niZAu8w3NeeA=="
|
||||
},
|
||||
"aframe-cubemap-component": {
|
||||
"version": "0.1.4",
|
||||
"resolved": "https://registry.npmjs.org/aframe-cubemap-component/-/aframe-cubemap-component-0.1.4.tgz",
|
||||
"integrity": "sha512-9k5CVFltFACmoQxmS9Jf3XpjqpES9iNSRnCxamxSDCpNyXn7myHii7bjnfIuzs6zwSeJU8Gbb72zzRyQ6Csp9g=="
|
||||
},
|
||||
"aframe-event-decorators": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/aframe-event-decorators/-/aframe-event-decorators-1.0.2.tgz",
|
||||
@@ -129,11 +119,6 @@
|
||||
"resolved": "https://registry.npmjs.org/aframe-geometry-merger-component/-/aframe-geometry-merger-component-2.0.0-beta1.tgz",
|
||||
"integrity": "sha512-0anZA8K7X5lybhbQEGdI8uH0tMZx1b90EA69MOkWfaYPKLWEK9neOWoF+qxOQql5Zz04zVl+BJ9q0fESuUnS9Q=="
|
||||
},
|
||||
"aframe-gltf-part-component": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/aframe-gltf-part-component/-/aframe-gltf-part-component-1.1.0.tgz",
|
||||
"integrity": "sha1-jX/8ViYbozraC6izQf3eif8rhlQ="
|
||||
},
|
||||
"aframe-haptics-component": {
|
||||
"version": "1.4.2",
|
||||
"resolved": "https://registry.npmjs.org/aframe-haptics-component/-/aframe-haptics-component-1.4.2.tgz",
|
||||
@@ -153,16 +138,6 @@
|
||||
"resolved": "https://registry.npmjs.org/aframe-orbit-controls/-/aframe-orbit-controls-1.2.0.tgz",
|
||||
"integrity": "sha512-g3j3Z3Lpf+LxIuNUwteVcB+eJ5jXwYEEzHbbRhdCJ754a9yqlvjt4tjvBq56F5ilpsg/bNUi8cjza8FFvJfd5g=="
|
||||
},
|
||||
"aframe-particle-system-component": {
|
||||
"version": "1.0.11",
|
||||
"resolved": "https://registry.npmjs.org/aframe-particle-system-component/-/aframe-particle-system-component-1.0.11.tgz",
|
||||
"integrity": "sha1-rB86vvIweyON9YyTWJgsmH+xABY="
|
||||
},
|
||||
"aframe-particleplayer-component": {
|
||||
"version": "2.0.0-beta2",
|
||||
"resolved": "https://registry.npmjs.org/aframe-particleplayer-component/-/aframe-particleplayer-component-2.0.0-beta2.tgz",
|
||||
"integrity": "sha512-5g2G5a9rvEma6ybgcCZJhJF53PKB5H5rIXjxozh7tE1ZbSDJv5EhCYLlwAJpmxUHbd59DJJQQGTbGdh33py2Iw=="
|
||||
},
|
||||
"aframe-proxy-event-component": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/aframe-proxy-event-component/-/aframe-proxy-event-component-2.1.0.tgz",
|
||||
|
||||
@@ -7,18 +7,13 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"aframe-animation-component": "^5.1.2",
|
||||
"aframe-animation-timeline-component": "^1.3.1",
|
||||
"aframe-audioanalyser-component": "^4.0.1",
|
||||
"aframe-cubemap-component": "^0.1.2",
|
||||
"aframe-event-decorators": "^1.0.2",
|
||||
"aframe-event-set-component": "^4.0.1",
|
||||
"aframe-geometry-merger-component": "^2.0.0-beta1",
|
||||
"aframe-gltf-part-component": "1.1.0",
|
||||
"aframe-haptics-component": "^1.4.2",
|
||||
"aframe-layout-component": "^5.2.0",
|
||||
"aframe-orbit-controls": "^1.2.0",
|
||||
"aframe-particle-system-component": "^1.0.11",
|
||||
"aframe-particleplayer-component": "^2.0.0-beta2",
|
||||
"aframe-proxy-event-component": "^2.1.0",
|
||||
"aframe-slice9-component": "^1.0.0",
|
||||
"aframe-state-component": "^5.0.0-beta6",
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
var SIGN_MATERIAL = {shader: 'flat', color: '#88f'};
|
||||
|
||||
var auxObj3D = new THREE.Object3D();
|
||||
|
||||
/**
|
||||
* Create beat from pool, collision detection, clipping planes.
|
||||
*/
|
||||
@@ -52,6 +54,7 @@ AFRAME.registerComponent('beat', {
|
||||
this.missElRight = document.getElementById('missRight');
|
||||
|
||||
this.beams = document.getElementById('beams').components['beams'];
|
||||
this.particles = document.getElementById('saberParticles');
|
||||
|
||||
this.initBlock();
|
||||
this.initColliders();
|
||||
@@ -308,7 +311,12 @@ AFRAME.registerComponent('beat', {
|
||||
|
||||
this.returnToPoolTimer = 800;
|
||||
|
||||
// this.el.sceneEl.components['json-particles__hit'].explode(this.el.object3D.position, rightCutPlane.normal, direction, this.data.color);
|
||||
auxObj3D.up.copy(rightCutPlane.normal);
|
||||
auxObj3D.lookAt(direction);
|
||||
this.particles.emit('explode', {
|
||||
position: this.el.object3D.position,
|
||||
rotation: auxObj3D.rotation
|
||||
});
|
||||
}
|
||||
})(),
|
||||
|
||||
|
||||
621
src/components/particleplayer.js
Normal file
621
src/components/particleplayer.js
Normal file
@@ -0,0 +1,621 @@
|
||||
const NUM_PLANE_POSITIONS = 12;
|
||||
|
||||
const BLENDINGS = {
|
||||
normal: THREE.NormalBlending,
|
||||
additive: THREE.AdditiveBlending,
|
||||
substractive: THREE.SubstractiveBlending,
|
||||
multiply: THREE.MultiplyBlending
|
||||
};
|
||||
|
||||
const SHADERS = {
|
||||
flat: THREE.MeshBasicMaterial,
|
||||
lambert: THREE.MeshLambertMaterial,
|
||||
phong: THREE.MeshPhongMaterial,
|
||||
standard: THREE.MeshStandardMaterial
|
||||
};
|
||||
|
||||
const OFFSCREEN_VEC3 = new THREE.Vector3(-99999, -99999, -99999);
|
||||
|
||||
/**
|
||||
* Particle Player component for A-Frame.
|
||||
*/
|
||||
AFRAME.registerComponent('particleplayer', {
|
||||
schema: {
|
||||
blending: {
|
||||
default: 'additive',
|
||||
oneOf: ['normal', 'additive', 'multiply', 'substractive']
|
||||
},
|
||||
color: {default: '#fff', type: 'color'},
|
||||
count: {default: '100%'},
|
||||
delay: {default: 0, type: 'int'},
|
||||
dur: {default: 1000, type: 'int'},
|
||||
img: {type: 'selector'},
|
||||
interpolate: {default: false},
|
||||
loop: {default: 'false'},
|
||||
on: {default: 'init'},
|
||||
poolSize: {default: 5, type: 'int'}, // number of simultaneous particle systems
|
||||
protation: {type: 'vec3'},
|
||||
pscale: {default: 1.0, type: 'float'},
|
||||
scale: {default: 1.0, type: 'float'},
|
||||
initialScale: {type: 'vec3'},
|
||||
finalScale: {type: 'vec3'},
|
||||
animateScale: {default: false},
|
||||
shader: {
|
||||
default: 'flat',
|
||||
oneOf: ['flat', 'lambert', 'phong', 'standard']
|
||||
},
|
||||
src: {type: 'selector'}
|
||||
},
|
||||
|
||||
multiple: true,
|
||||
|
||||
init: function() {
|
||||
this.frame = 0;
|
||||
this.framedata = null;
|
||||
this.indexPool = null;
|
||||
this.lastFrame = 0;
|
||||
this.material = null;
|
||||
this.msPerFrame = 0;
|
||||
this.numFrames = 0;
|
||||
this.numParticles = 0; // total number of particles per system
|
||||
this.originalVertexPositions = [];
|
||||
this.particleCount = 0; // actual number of particles to spawn per event (data.count)
|
||||
this.particleSystems = [];
|
||||
this.protation = false;
|
||||
this.restPositions = []; // position at first frame each particle is alive
|
||||
this.restRotations = [];
|
||||
this.sprite_rotation = false;
|
||||
this.systems = null;
|
||||
this.useRotation = false;
|
||||
this.scaleAnim = new THREE.Vector3();
|
||||
},
|
||||
|
||||
update: function(oldData) {
|
||||
const data = this.data;
|
||||
|
||||
if (!data.src) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (oldData.on !== data.on) {
|
||||
if (oldData.on) {
|
||||
this.el.removeEventListener(oldData.on, this.start);
|
||||
}
|
||||
if (data.on !== 'play') {
|
||||
this.el.addEventListener(data.on, this.start.bind(this));
|
||||
}
|
||||
}
|
||||
|
||||
this.loadParticlesJSON(data.src, data.scale);
|
||||
|
||||
this.numFrames = this.framedata.length;
|
||||
this.numParticles = this.numFrames > 0 ? this.framedata[0].length : 0;
|
||||
|
||||
if (data.count[data.count.length - 1] === '%') {
|
||||
this.particleCount = Math.floor(
|
||||
(parseInt(data.count) * this.numParticles) / 100.0
|
||||
);
|
||||
} else {
|
||||
this.particleCount = parseInt(data.count);
|
||||
}
|
||||
this.particleCount = Math.min(
|
||||
this.numParticles,
|
||||
Math.max(0, this.particleCount)
|
||||
);
|
||||
|
||||
this.msPerFrame = data.dur / this.numFrames;
|
||||
|
||||
this.indexPool = new Array(this.numParticles);
|
||||
|
||||
const materialParams = {
|
||||
color: new THREE.Color(data.color),
|
||||
side: THREE.DoubleSide,
|
||||
blending: BLENDINGS[data.blending],
|
||||
map: data.img ? new THREE.TextureLoader().load(data.img.src) : null,
|
||||
depthWrite: false,
|
||||
opacity: data.opacity,
|
||||
transparent: !!data.img || data.blending !== 'normal' || data.opacity < 1
|
||||
};
|
||||
if (SHADERS[data.shader] !== undefined) {
|
||||
this.material = new SHADERS[data.shader](materialParams);
|
||||
} else {
|
||||
this.material = new SHADERS['flat'](materialParams);
|
||||
}
|
||||
|
||||
this.createParticles(data.poolSize);
|
||||
|
||||
if (data.on === 'init') {
|
||||
this.start();
|
||||
}
|
||||
},
|
||||
|
||||
loadParticlesJSON: function(json, scale) {
|
||||
var alive;
|
||||
|
||||
this.restPositions.length = 0;
|
||||
this.restRotations.length = 0;
|
||||
|
||||
const jsonData = JSON.parse(json.data);
|
||||
const frames = jsonData.frames;
|
||||
const precision = jsonData.precision;
|
||||
|
||||
this.useRotation = jsonData.rotation;
|
||||
|
||||
if (jsonData.sprite_rotation !== false) {
|
||||
this.sprite_rotation = {
|
||||
x: jsonData.sprite_rotation[0] / precision,
|
||||
y: jsonData.sprite_rotation[1] / precision,
|
||||
z: jsonData.sprite_rotation[2] / precision
|
||||
};
|
||||
} else {
|
||||
this.sprite_rotation = false;
|
||||
}
|
||||
|
||||
this.framedata = new Array(frames.length);
|
||||
for (let frameIndex = 0; frameIndex < frames.length; frameIndex++) {
|
||||
this.framedata[frameIndex] = new Array(frames[frameIndex].length);
|
||||
for (
|
||||
let particleIndex = 0;
|
||||
particleIndex < frames[frameIndex].length;
|
||||
particleIndex++
|
||||
) {
|
||||
let rawP = frames[frameIndex][particleIndex]; // data of particle i in frame f
|
||||
alive = rawP !== 0; // 0 means not alive yet this frame.
|
||||
|
||||
let p = (this.framedata[frameIndex][particleIndex] = {
|
||||
position: alive
|
||||
? {
|
||||
x: (rawP[0] / precision) * scale,
|
||||
y: (rawP[1] / precision) * scale,
|
||||
z: (rawP[2] / precision) * scale
|
||||
}
|
||||
: null,
|
||||
alive: alive
|
||||
});
|
||||
|
||||
if (jsonData.rotation) {
|
||||
p.rotation = alive
|
||||
? {
|
||||
x: rawP[3] / precision,
|
||||
y: rawP[4] / precision,
|
||||
z: rawP[5] / precision
|
||||
}
|
||||
: null;
|
||||
}
|
||||
|
||||
if (alive && frameIndex === 0) {
|
||||
this.restPositions[particleIndex] = p.position
|
||||
? {x: p.position.y, y: p.position.y, z: p.position.z}
|
||||
: null;
|
||||
this.restRotations[particleIndex] = p.rotation
|
||||
? {x: p.rotation.y, y: p.rotation.y, z: p.rotation.z}
|
||||
: null;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
createParticles: (function() {
|
||||
const tempGeometries = [];
|
||||
|
||||
return function(numParticleSystems) {
|
||||
const data = this.data;
|
||||
var loop = parseInt(this.data.loop);
|
||||
|
||||
this.particleSystems.length = 0;
|
||||
|
||||
if (isNaN(loop)) {
|
||||
loop = this.data.loop === 'true' ? Number.MAX_VALUE : 0;
|
||||
}
|
||||
|
||||
for (let i = 0; i < numParticleSystems; i++) {
|
||||
let particleSystem = {
|
||||
active: false,
|
||||
activeParticleIndices: new Array(this.particleCount),
|
||||
loopCount: 0,
|
||||
loopTotal: loop,
|
||||
mesh: null,
|
||||
time: 0,
|
||||
pscale: data.animateScale ? new THREE.Vector3() : null
|
||||
};
|
||||
|
||||
// Fill array of geometries to merge.
|
||||
const ratio = data.img ? data.img.width / data.img.height : 1;
|
||||
tempGeometries.length = 0;
|
||||
for (let p = 0; p < this.numParticles; p++) {
|
||||
let geometry = new THREE.PlaneBufferGeometry(
|
||||
0.1 * ratio * data.pscale,
|
||||
0.1 * data.pscale
|
||||
);
|
||||
if (this.sprite_rotation !== false) {
|
||||
geometry.rotateX(this.sprite_rotation.x);
|
||||
geometry.rotateY(this.sprite_rotation.y);
|
||||
geometry.rotateZ(this.sprite_rotation.z);
|
||||
} else {
|
||||
geometry.rotateX((this.data.protation.x * Math.PI) / 180);
|
||||
geometry.rotateY((this.data.protation.y * Math.PI) / 180);
|
||||
geometry.rotateZ((this.data.protation.z * Math.PI) / 180);
|
||||
}
|
||||
tempGeometries.push(geometry);
|
||||
}
|
||||
|
||||
// Create merged geometry for whole particle system.
|
||||
let mergedBufferGeometry = THREE.BufferGeometryUtils.mergeBufferGeometries(
|
||||
tempGeometries
|
||||
);
|
||||
|
||||
particleSystem.mesh = new THREE.Mesh(
|
||||
mergedBufferGeometry,
|
||||
this.material
|
||||
);
|
||||
particleSystem.mesh.visible = false;
|
||||
this.el.setObject3D(`particleplayer${i}`, particleSystem.mesh);
|
||||
copyArray(
|
||||
this.originalVertexPositions,
|
||||
mergedBufferGeometry.attributes.position.array
|
||||
);
|
||||
|
||||
// Hide all particles by default.
|
||||
for (
|
||||
let i = 0;
|
||||
i < mergedBufferGeometry.attributes.position.array.length;
|
||||
i++
|
||||
) {
|
||||
mergedBufferGeometry.attributes.position.array[i] = -99999;
|
||||
}
|
||||
|
||||
for (let i = 0; i < particleSystem.activeParticleIndices.length; i++) {
|
||||
particleSystem.activeParticleIndices[i] = i;
|
||||
};
|
||||
|
||||
this.particleSystems.push(particleSystem);
|
||||
}
|
||||
};
|
||||
})(),
|
||||
|
||||
start: function(evt) {
|
||||
if (this.data.delay > 0) {
|
||||
setTimeout(() => this.startAfterDelay(evt), this.data.delay);
|
||||
} else {
|
||||
this.startAfterDelay(evt);
|
||||
}
|
||||
},
|
||||
|
||||
startAfterDelay: function(evt) {
|
||||
// position, rotation
|
||||
var found = -1;
|
||||
var particleSystem;
|
||||
var oldestTime = 0;
|
||||
var position = evt ? evt.detail.position : null;
|
||||
var rotation = evt ? evt.detail.rotation : null;
|
||||
|
||||
if (!(position instanceof THREE.Vector3)) {
|
||||
position = new THREE.Vector3();
|
||||
}
|
||||
if (!(rotation instanceof THREE.Euler)) {
|
||||
rotation = new THREE.Euler();
|
||||
}
|
||||
|
||||
// find available (or oldest) particle system
|
||||
for (var i = 0; i < this.particleSystems.length; i++) {
|
||||
if (this.particleSystems[i].active === false) {
|
||||
found = i;
|
||||
break;
|
||||
}
|
||||
if (this.particleSystems[i].time > oldestTime) {
|
||||
found = i;
|
||||
oldestTime = this.particleSystems[i].time;
|
||||
}
|
||||
}
|
||||
|
||||
particleSystem = this.particleSystems[found];
|
||||
particleSystem.active = true;
|
||||
particleSystem.loopCount = 1;
|
||||
particleSystem.mesh.visible = true;
|
||||
particleSystem.mesh.position.copy(position);
|
||||
particleSystem.mesh.rotation.copy(rotation);
|
||||
particleSystem.time = 0;
|
||||
if (this.data.animateScale) {
|
||||
particleSystem.pscale.copy(this.data.initialScale);
|
||||
}
|
||||
|
||||
this.resetParticles(particleSystem);
|
||||
},
|
||||
|
||||
doLoop: function(particleSystem) {
|
||||
particleSystem.loopCount++;
|
||||
particleSystem.frame = -1;
|
||||
particleSystem.time = 0;
|
||||
if (this.data.animateScale) {
|
||||
particleSystem.pscale.copy(data.initialScale);
|
||||
}
|
||||
this.resetParticles(particleSystem);
|
||||
},
|
||||
|
||||
resetParticle: function(particleSystem, particleIndex) {
|
||||
const geometry = particleSystem.mesh.geometry;
|
||||
|
||||
if (this.restPositions[particleIndex]) {
|
||||
transformPlane(
|
||||
particleIndex,
|
||||
geometry,
|
||||
this.originalVertexPositions,
|
||||
this.restPositions[particleIndex],
|
||||
this.useRotation && this.restRotations[particleIndex],
|
||||
particleSystem.pscale
|
||||
);
|
||||
} else {
|
||||
// Hide.
|
||||
transformPlane(
|
||||
particleIndex,
|
||||
geometry,
|
||||
this.originalVertexPositions,
|
||||
OFFSCREEN_VEC3,
|
||||
undefined,
|
||||
null
|
||||
);
|
||||
}
|
||||
|
||||
// TODO: Can update transformPlane for lookAt.
|
||||
// lookAt does not support rotated or translated parents! :_(
|
||||
// part.lookAt(this.camera.position);
|
||||
},
|
||||
|
||||
/**
|
||||
* When starting or finishing (looping) animation, this resets particles
|
||||
* to their initial position and, if user asked for replaying less than 100%
|
||||
* of particles, randomly choose them.
|
||||
*/
|
||||
resetParticles: function(particleSystem) {
|
||||
var i;
|
||||
var rand;
|
||||
|
||||
// no picking, just hide and reset
|
||||
if (this.particleCount === this.numParticles) {
|
||||
for (i = 0; i < this.numParticles; i++) {
|
||||
this.resetParticle(particleSystem, i);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// hide particles from last animation and initialize indexPool
|
||||
const geometry = particleSystem.mesh.geometry;
|
||||
for (i = 0; i < this.numParticles; i++) {
|
||||
if (i < this.particleCount) {
|
||||
transformPlane(
|
||||
particleSystem.activeParticleIndices[i],
|
||||
geometry,
|
||||
this.originalVertexPositions,
|
||||
OFFSCREEN_VEC3,
|
||||
undefined,
|
||||
null
|
||||
);
|
||||
}
|
||||
this.indexPool[i] = i;
|
||||
}
|
||||
|
||||
// scramble indexPool
|
||||
for (i = 0; i < this.particleCount; i++) {
|
||||
rand = i + Math.floor(Math.random() * (this.numParticles - i));
|
||||
particleSystem.activeParticleIndices[i] = this.indexPool[rand];
|
||||
this.indexPool[rand] = this.indexPool[i];
|
||||
this.resetParticle(
|
||||
particleSystem,
|
||||
particleSystem.activeParticleIndices[i]
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
tick: (function() {
|
||||
const helperPositionVec3 = new THREE.Vector3();
|
||||
|
||||
return function(time, delta) {
|
||||
var frame; // current particle system frame
|
||||
var fdata; // all particles data in current frame
|
||||
var fdataNext; // next frame (for interpolation)
|
||||
var useRotation = this.useRotation;
|
||||
var frameTime; // time in current frame (for interpolation)
|
||||
var relTime; // current particle system relative time (0-1)
|
||||
var interpolate; // whether interpolate between frames or not
|
||||
|
||||
for (
|
||||
let particleSystemIndex = 0;
|
||||
particleSystemIndex < this.particleSystems.length;
|
||||
particleSystemIndex++
|
||||
) {
|
||||
let particleSystem = this.particleSystems[particleSystemIndex];
|
||||
if (!particleSystem.active) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// if the duration is so short that there's no need to interpolate, don't do it
|
||||
// even if user asked for it.
|
||||
interpolate =
|
||||
this.data.interpolate && this.data.dur / this.numFrames > delta;
|
||||
relTime = particleSystem.time / this.data.dur;
|
||||
frame = relTime * this.numFrames;
|
||||
fdata = this.framedata[Math.floor(frame)];
|
||||
if (interpolate) {
|
||||
frameTime = frame - Math.floor(frame);
|
||||
fdataNext =
|
||||
frame < this.numFrames - 1
|
||||
? this.framedata[Math.floor(frame) + 1]
|
||||
: null;
|
||||
}
|
||||
|
||||
if (this.data.animateScale){
|
||||
particleSystem.pscale.lerp(this.data.finalScale, relTime);
|
||||
}
|
||||
|
||||
for (
|
||||
let activeParticleIndex = 0;
|
||||
activeParticleIndex < particleSystem.activeParticleIndices.length;
|
||||
activeParticleIndex++
|
||||
) {
|
||||
|
||||
let particleIndex =
|
||||
particleSystem.activeParticleIndices[activeParticleIndex];
|
||||
let rotation = useRotation && fdata[particleIndex].rotation;
|
||||
|
||||
// TODO: Add vertex position to original position to all vertices of plane...
|
||||
if (!fdata[particleIndex].alive) {
|
||||
// Hide plane off-screen when not alive.
|
||||
transformPlane(
|
||||
particleIndex,
|
||||
particleSystem.mesh.geometry,
|
||||
this.originalVertexPositions,
|
||||
OFFSCREEN_VEC3,
|
||||
undefined,
|
||||
null
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (interpolate && fdataNext && fdataNext[particleIndex].alive) {
|
||||
helperPositionVec3.lerpVectors(
|
||||
fdata[particleIndex].position,
|
||||
fdataNext[particleIndex].position,
|
||||
frameTime
|
||||
);
|
||||
transformPlane(
|
||||
particleIndex,
|
||||
particleSystem.mesh.geometry,
|
||||
this.originalVertexPositions,
|
||||
helperPositionVec3,
|
||||
rotation,
|
||||
particleSystem.pscale
|
||||
);
|
||||
} else {
|
||||
transformPlane(
|
||||
particleIndex,
|
||||
particleSystem.mesh.geometry,
|
||||
this.originalVertexPositions,
|
||||
fdata[particleIndex].position,
|
||||
rotation,
|
||||
particleSystem.pscale
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
particleSystem.time += delta;
|
||||
if (particleSystem.time >= this.data.dur) {
|
||||
if (particleSystem.loopCount < particleSystem.loopTotal) {
|
||||
this.el.emit('particleplayerloop', null, false);
|
||||
this.doLoop(particleSystem);
|
||||
} else {
|
||||
this.el.emit('particleplayerfinished', null, false);
|
||||
particleSystem.active = false;
|
||||
particleSystem.mesh.visible = false;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
};
|
||||
})(),
|
||||
|
||||
_transformPlane: transformPlane
|
||||
});
|
||||
|
||||
// Use triangle geometry as a helper for rotating.
|
||||
const tri = (function() {
|
||||
const tri = new THREE.Geometry();
|
||||
tri.vertices.push(new THREE.Vector3());
|
||||
tri.vertices.push(new THREE.Vector3());
|
||||
tri.vertices.push(new THREE.Vector3());
|
||||
tri.faces.push(new THREE.Face3(0, 1, 2));
|
||||
return tri;
|
||||
})();
|
||||
|
||||
/**
|
||||
* Faces of a plane are v0, v2, v1 and v2, v3, v1.
|
||||
* Positions are 12 numbers: [v0, v1, v2, v3].
|
||||
*/
|
||||
function transformPlane(
|
||||
particleIndex,
|
||||
geometry,
|
||||
originalArray,
|
||||
position,
|
||||
rotation,
|
||||
scale
|
||||
) {
|
||||
const array = geometry.attributes.position.array;
|
||||
const index = particleIndex * NUM_PLANE_POSITIONS;
|
||||
|
||||
// Calculate first face (0, 2, 1).
|
||||
tri.vertices[0].set(
|
||||
originalArray[index + 0],
|
||||
originalArray[index + 1],
|
||||
originalArray[index + 2]
|
||||
);
|
||||
tri.vertices[1].set(
|
||||
originalArray[index + 3],
|
||||
originalArray[index + 4],
|
||||
originalArray[index + 5]
|
||||
);
|
||||
tri.vertices[2].set(
|
||||
originalArray[index + 6],
|
||||
originalArray[index + 7],
|
||||
originalArray[index + 8]
|
||||
);
|
||||
if (scale !== null) {
|
||||
tri.scale(scale.x, scale.y, scale.z);
|
||||
}
|
||||
if (rotation) {
|
||||
tri.rotateX(rotation.x);
|
||||
tri.rotateY(rotation.y);
|
||||
tri.rotateZ(rotation.z);
|
||||
}
|
||||
tri.vertices[0].add(position);
|
||||
tri.vertices[1].add(position);
|
||||
tri.vertices[2].add(position);
|
||||
array[index + 0] = tri.vertices[0].x;
|
||||
array[index + 1] = tri.vertices[0].y;
|
||||
array[index + 2] = tri.vertices[0].z;
|
||||
array[index + 3] = tri.vertices[1].x;
|
||||
array[index + 4] = tri.vertices[1].y;
|
||||
array[index + 5] = tri.vertices[1].z;
|
||||
array[index + 6] = tri.vertices[2].x;
|
||||
array[index + 7] = tri.vertices[2].y;
|
||||
array[index + 8] = tri.vertices[2].z;
|
||||
|
||||
// Calculate second face (2, 3, 1) just for the last vertex.
|
||||
tri.vertices[0].set(
|
||||
originalArray[index + 3],
|
||||
originalArray[index + 4],
|
||||
originalArray[index + 5]
|
||||
);
|
||||
tri.vertices[1].set(
|
||||
originalArray[index + 6],
|
||||
originalArray[index + 7],
|
||||
originalArray[index + 8]
|
||||
);
|
||||
tri.vertices[2].set(
|
||||
originalArray[index + 9],
|
||||
originalArray[index + 10],
|
||||
originalArray[index + 11]
|
||||
);
|
||||
if (scale !== null) {
|
||||
tri.scale(scale.x, scale.y, scale.z);
|
||||
}
|
||||
if (rotation) {
|
||||
tri.rotateX(rotation.x);
|
||||
tri.rotateY(rotation.y);
|
||||
tri.rotateZ(rotation.z);
|
||||
}
|
||||
tri.vertices[0].add(position);
|
||||
tri.vertices[1].add(position);
|
||||
tri.vertices[2].add(position);
|
||||
array[index + 9] = tri.vertices[2].x;
|
||||
array[index + 10] = tri.vertices[2].y;
|
||||
array[index + 11] = tri.vertices[2].z;
|
||||
|
||||
geometry.attributes.position.needsUpdate = true;
|
||||
}
|
||||
module.exports.transformPlane = transformPlane;
|
||||
|
||||
function copyArray(dest, src) {
|
||||
dest.length = 0;
|
||||
for (let i = 0; i < src.length; i++) {
|
||||
dest[i] = src[i];
|
||||
}
|
||||
}
|
||||
@@ -35,8 +35,9 @@
|
||||
<a-asset-item id="logofrontObj" src="assets/models/logofront.obj"></a-asset-item>
|
||||
<a-asset-item id="logobackObj" src="assets/models/logoback.obj"></a-asset-item>
|
||||
<a-asset-item id="logofrontUObj" src="assets/models/logofront-u.obj"></a-asset-item>
|
||||
<a-asset-item id="logoSparks" src="assets/models/logosparks.json"></a-asset-item>
|
||||
<a-asset-item id="laserObj" src="assets/models/laser/laser.obj"></a-asset-item>
|
||||
<a-asset-item id="logoSparks" src="assets/models/logosparks.json"></a-asset-item>
|
||||
<a-asset-item id="sabercutParticles" src="assets/models/sabercut.json"></a-asset-item>
|
||||
|
||||
<audio id="hoverSound" src="assets/sounds/hover.ogg"></audio>
|
||||
<audio id="saberDraw" src="assets/sounds/saberDraw.wav"></audio>
|
||||
@@ -57,6 +58,7 @@
|
||||
<img id="wrongRedImg" src="assets/img/wrongred.png">
|
||||
<img id="missBlueImg" src="assets/img/missblue.png">
|
||||
<img id="missRedImg" src="assets/img/missred.png">
|
||||
<img id="sparkImg" src="assets/img/spark.png">
|
||||
|
||||
<a-mixin id="slice" slice9="color: #050505; transparent: true; opacity: 0.7; src: #sliceImg; left: 50; right: 52; top: 50; bottom: 52; padding: 0.18"></a-mixin>
|
||||
<a-mixin id="font" text="font: assets/fonts/Teko-Bold.json; shader: msdf; letterSpacing: 1"></a-mixin>
|
||||
@@ -96,6 +98,8 @@
|
||||
<a-entity id="missLeft" mixin='badBeat' geometry="height: 0.25; width: 0.5" material="src: #missRedImg"></a-entity>
|
||||
<a-entity id="missRight" mixin='badBeat' geometry="height: 0.25; width: 0.5" material="src: #missBlueImg"></a-entity>
|
||||
|
||||
<a-entity id="saberParticles" particleplayer="src: #sabercutParticles; pscale: 0.5; loop: false; on: explode; img: #sparkImg; count: 20%; animateScale: true; initialScale: 4 1 1; finalScale: 0.2 0.2 1"> </a-entity>
|
||||
|
||||
<!-- Player. -->
|
||||
<a-mixin
|
||||
id="raycaster"
|
||||
|
||||
@@ -4,19 +4,16 @@ require('../vendor/BufferGeometryUtils');
|
||||
|
||||
require('aframe-animation-component');
|
||||
require('aframe-audioanalyser-component');
|
||||
require('aframe-cubemap-component');
|
||||
require('aframe-event-set-component');
|
||||
require('aframe-geometry-merger-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');
|
||||
require('aframe-super-keyboard');
|
||||
require('aframe-thumb-controls-component');
|
||||
require('aframe-particleplayer-component');
|
||||
|
||||
requireAll(require.context('./components/', true, /\.js$/));
|
||||
requireAll(require.context('./state/', true, /\.js$/));
|
||||
|
||||
@@ -64,7 +64,7 @@
|
||||
light="type: spot; penumbra: 1; intensity: 5; angle: 20"
|
||||
animation="property: object3D.rotation.y; from: 0.87; to: -0.87; dur:300; easing: linear; startEvents: audioanalyser-beat"></a-entity>
|
||||
|
||||
<a-entity id="logosparks" bind__visible="menu.active" particleplayer="src: #logoSparks; scale: 1.4; pscale: 0.35; count: 10; dur: 600; on: logoflicker" position="-2.8 5.5 -7.2"></a-entity>
|
||||
<a-entity id="logosparks" bind__visible="menu.active" particleplayer="src: #logoSparks; scale: 1.4; pscale: 0.35; count: 10; dur: 1000; on: logoflicker; animateScale: true; initialScale: 1.5 1.5 1.5; finalScale: 0.3 0.3 0.3" position="-2.8 5.5 -7.2"></a-entity>
|
||||
<a-entity id="logo" bind__visible="menu.active" position="0 6 -7.5" rotation="90 0 0">
|
||||
<a-entity obj-model="obj: #logobackObj" material="color: #001b29"></a-entity>
|
||||
<a-entity obj-model="obj: #logofrontObj" material="color: #e81e23"></a-entity>
|
||||
|
||||
Reference in New Issue
Block a user