Created
August 13, 2017 22:41
-
-
Save AndrewRayCode/770718f1d42941c449ceaa208f3d40cd to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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