Skip to content

Instantly share code, notes, and snippets.

@AndrewRayCode
Created August 13, 2017 22:41
Show Gist options
  • Save AndrewRayCode/770718f1d42941c449ceaa208f3d40cd to your computer and use it in GitHub Desktop.
Save AndrewRayCode/770718f1d42941c449ceaa208f3d40cd to your computer and use it in GitHub Desktop.
import React, { Component } from 'react';
import { Vector3, Euler, Quaternion, } from 'three';
import SimplexNoise from 'simplex-noise';
import SPE from 'shader-particle-engine';
import shaderFrog from 'helpers/shaderFrog';
import { easeInQuint, easeOutCirc, } from 'easing-utils';
import { upsidownHalfPipe, camelHump, startEaseAt, } from 'helpers/EasingHelpers';
import { Mesh, ParticleEmitter, } from '../';
import { twinkleMaterial } from 'ThreeMaterials';
const simplex = new SimplexNoise();
// Teleporter sphere
const maxSphereOpacity = 0.8;
const sphereRotation = new Euler( ( Math.PI / 2 ) + 0.2, 0, 0 );
const sphereScale = new Vector3( 1.3, 1.3, 1.3 );
// Smaller shockwave ("splatter")
const maxShockwaveOpacity = 0.5;
const shockwave1Rotation = new Euler( Math.PI / 2, 0, 0 );
const shockwave1Position = new Vector3( 0, 0, 0 );
const shockwave1Scale = new Vector3( 2, 2, 2 );
// Larger shockwave ("ring")
const shockwave2Rotation = new Euler( Math.PI / 2, 0, 0 );
const shockwave2Position = new Vector3( 0, -0.25, 0 );
const shockwave2Scale = new Vector3( 3, 3, 3 );
// Generic white glow
const maxGlowOpacity = 0.75;
const glowBillboardRotation = new Euler( Math.PI / 2, 0, 0 );
const glowBillboardPosition = new Vector3( 0, 0.75, 0 );
const glowBillboardScale = new Vector3( 2.5, 2.5, 2.5 );
// Circular rainbow after flare
const rainbowFlareScale = new Vector3( 3, 3, 3 );
const rainbowFlarePosition = new Vector3( 0, 1, 0 );
const rainbowFlareGrowSpeed = 0.2;
const rainbowFlickerMultiplier = 0.002;
const rainbowFlareRotationSpeed = -1;
const rainbowFlareRotationOffset = Math.PI / 4;
const maxRainbowFlareOpacity = 0.7;
// JJ Abrahams flare
const wideFlareRotation = new Euler( Math.PI / 2, 0, 0 );
const wideFlarePosition = new Vector3( 0, 1.25, 0 );
const wideFlareScale = new Vector3( 4, 2, 2 );
// Flicker for jj abrahams flare and rainbow flare
const flickerSpeed = 0.002;
const flickerMultiplier = 0.2;
const flickerVariationSpeed = 0.0005;
// TODO: This probably isn't right
const defaultScale = new Vector3( 1, 1, 1 ).multiplyScalar( 1.7 );
// Particle effect group
const emitterPosition = new Vector3( 0, 0, 0 );
const particleVelocityDistribution = SPE.distributions.BOX;
const particleRotationAxis = new Vector3( 0, 0, 1 );
const particleRotationAngle = 0;
const particleOpacity = [ 0.2, 1, 0 ];
const particleVelocity = new Vector3( 0, 0, -1 );
const particleVelocitySpread = new Vector3( 0, 0, 0 );
const particleParticleRotation = [ -1, 1 ];
const particleParticleRotationSpread = [ -1, 1 ];
const particlePositionSpread = new Vector3( 0.8, 0, 0.8 );
const particleRotation = new Quaternion( 0, 0, 0, 1 )
.setFromEuler( new Euler( 0, Math.PI / 2, 0 ) );
const particleColors = [ 0xffffff, 0xff0000, 0x0000ff ];
const colorSpread = new Vector3( 0.6, 0.4, 0.2 );
const particleSize = 0.4;
const particleSizeSpread = [ 0.4, 2 ];
const particleCount = 40;
const emitterRadius = 2;
const emitterMinimumRadius = 0.07;
export default class TeleporterAnimation extends Component {
constructor( props, context ) {
super( props, context );
this.state = this._getStateFromProps( props, true );
}
shouldComponentUpdate( nextProps ) {
return ( nextProps.position !== this.props.position ) ||
( nextProps.rotation !== this.props.rotation ) ||
( nextProps.quaternion !== this.props.quaternion ) ||
( nextProps.scale !== this.props.scale ) ||
( nextProps.time !== this.props.time ) ||
( nextProps.animationPercent !== this.props.animationPercent ) ||
( nextProps.materialId !== this.props.materialId );
}
componentWillReceiveProps( nextProps ) {
if( ( nextProps.time !== this.props.time ) ) {
this.setState( this._getStateFromProps( nextProps ) );
}
}
_getStateFromProps( props:Object, forceUpdate:?boolean ):Object {
const {
scale, playerRadius, playerTexture, position, opacity, time,
percent,
} = props;
const newState = {
computedScale: scale ?
defaultScale.clone().multiply( scale ) :
defaultScale.clone().multiplyScalar( playerRadius ),
};
if( forceUpdate || ( playerRadius !== this.props.playerRadius ) ) {
newState.positionSpread = particlePositionSpread
.clone()
.multiplyScalar( playerRadius );
newState.sizeSpread = particleSizeSpread.map( val => val * playerRadius );
}
if( forceUpdate || ( time !== this.props.time ) ) {
const opacityValue = upsidownHalfPipe( percent );
shaderFrog.get( 'teleporter' ).uniforms.opacity.value = opacityValue * maxSphereOpacity;
// Needs one render first I guess to set up refs
if( this.refs.wideFlare ) {
this.refs.wideFlare.refs.mesh.material.opacity = upsidownHalfPipe( startEaseAt( percent, 0.4 ) );
this.refs.whiteGlow.refs.mesh.material.opacity = opacityValue * maxGlowOpacity;
this.refs.shockwave1.refs.mesh.material.opacity = camelHump( startEaseAt( percent, 0.3, 0.5 ) ) * maxShockwaveOpacity;
this.refs.shockwave2.refs.mesh.material.opacity = camelHump( startEaseAt( percent, 0.4, 0.5 ) ) * maxShockwaveOpacity;
this.refs.rainbowFlare.refs.mesh.material.opacity = camelHump(
easeInQuint( startEaseAt( percent, 0.1 ) )
) * maxRainbowFlareOpacity;
const playerOpacity = 1 - startEaseAt( percent, 0.3, 0.1 );
shaderFrog.get( 'glowTextureFace' ).uniforms.opacity.value = playerOpacity;
shaderFrog.get( 'glowTextureLegs' ).uniforms.opacity.value = playerOpacity;
shaderFrog.get( 'glowTextureLid' ).uniforms.opacity.value = playerOpacity;
shaderFrog.get( 'glowTextureTail' ).uniforms.opacity.value = playerOpacity;
shaderFrog.get( 'glowTextureSkin' ).uniforms.opacity.value = playerOpacity;
}
}
return newState;
}
render() {
const {
position, rotation, quaternion, scale, materialId, time, delta,
playerRadius, percent,
} = this.props;
const { positionSpread, sizeSpread, } = this.state;
const flicker = time * flickerSpeed;
const noise = simplex.noise2D( flicker, flicker );
return <group
position={ position }
quaternion={ quaternion }
rotation={ rotation }
scale={ scale }
>
<Mesh
position={ rainbowFlarePosition }
rotation={ new Euler( Math.PI / 2, 0, rainbowFlareRotationOffset + percent * rainbowFlareRotationSpeed ) }
scale={ rainbowFlareScale.clone().multiplyScalar(
1.0 +
( percent * rainbowFlareGrowSpeed ) +
( noise * rainbowFlickerMultiplier )
)}
ref="rainbowFlare"
geometryId="1x1plane"
materialId="rainbowFlare"
/>
<Mesh
position={ wideFlarePosition }
rotation={ wideFlareRotation }
scale={ wideFlareScale.clone().multiplyScalar(
percent * 0.5 + 1.0 + flickerMultiplier * noise
)}
ref="wideFlare"
geometryId="1x1plane"
materialId="wideFlare"
/>
<Mesh
ref="whiteGlow"
position={ glowBillboardPosition }
rotation={ glowBillboardRotation }
scale={ glowBillboardScale }
geometryId="1x1plane"
materialId="whiteGlow"
/>
<Mesh
ref="sphere"
rotation={ sphereRotation }
scale={ sphereScale.clone().multiplyScalar( upsidownHalfPipe( percent ) ) }
geometryId="sphereHighPoly"
materialId="teleporter"
/>
<Mesh
ref="shockwave1"
position={ shockwave1Position }
rotation={ new Euler( Math.PI / 2, 0, time * 0.001 ) }
scale={ shockwave1Scale.clone().multiplyScalar( 1.0 + 1.5 * easeOutCirc( startEaseAt( percent, 0.35 ) ) ) }
geometryId="1x1plane"
materialId="shockwaveSplatter"
/>
<Mesh
ref="shockwave2"
position={ shockwave2Position }
rotation={ new Euler( Math.PI / 2, 0, -time * 0.001 ) }
scale={ shockwave2Scale.clone().multiplyScalar( 1.0 + easeOutCirc( startEaseAt( percent, 0.45 ) ) ) }
geometryId="1x1plane"
materialId="shockwaveLarge"
/>
<ParticleEmitter
scale={ 1 }
time={ time }
delta={ delta }
enabledFalseWrong={ percent <= 0.75 }
enabled
texture={ twinkleMaterial }
emitterPosition={ emitterPosition }
quaternion={ particleRotation }
positionSpread={ positionSpread }
velocityV3={ particleVelocity }
velocitySpread={ particleVelocitySpread }
colors={ particleColors }
colorSpread={ colorSpread }
size={ particleSize + ( playerRadius * 0.6 ) }
sizeSpread={ sizeSpread }
particleCount={ particleCount }
type={ SPE.distributions.DISC }
emitterRadius={ emitterMinimumRadius + ( playerRadius * emitterRadius ) }
velocityDistribution={ particleVelocityDistribution }
rotationAxis={ particleRotationAxis }
rotationAngle={ particleRotationAngle * playerRadius }
angle={ particleParticleRotation }
angleSpread={ particleParticleRotationSpread }
opacity={ particleOpacity }
maxAge={ 1 }
/>
</group>;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment