CocoClass = require 'core/CocoClass' utils = require 'core/utils' module.exports = ParticleMan = class ParticleMan extends CocoClass constructor: -> return @unsupported = true unless Modernizr.webgl try @renderer = new THREE.WebGLRenderer alpha: true catch err return @unsupported = true $(@renderer.domElement).addClass 'particle-man' @scene = new THREE.Scene() @clock = new THREE.Clock() @particleGroups = [] destroy: -> @detach() @disposeObject3D @scene for child in @scene?.children?.slice() or [] @scene.remove child super() disposeObject3D: (obj) -> return unless obj @disposeObject3D child for child in obj.children obj.geometry?.dispose() obj.geometry = undefined if obj.material material.dispose() for material in obj.material.materials ? [] obj.material.dispose() obj.material = undefined if obj.texture obj.texture.dispose() obj.texture = undefined attach: (@$el) -> return if @unsupported width = @$el.innerWidth() height = @$el.innerHeight() @aspectRatio = width / height @renderer.setSize(width, height) @$el.append @renderer.domElement @camera = camera = new THREE.OrthographicCamera( 100 * -0.5, # Left 100 * 0.5, # Right 100 * 0.5 * @aspectRatio, # Top 100 * -0.5 * @aspectRatio, # Bottom 0, # Near frustrum distance 1000 # Far frustrum distance ) @camera.position.set(0, 0, 100) @camera.up = new THREE.Vector3(0, 1, 0) # http://stackoverflow.com/questions/14271672/moving-the-camera-lookat-and-rotations-in-three-js @camera.lookAt new THREE.Vector3(0, 0, 0) unless @started @started = true @render() detach: -> return if @unsupported @renderer.domElement.remove() @started = false render: => return if @unsupported return if @destroyed return unless @started @renderer.render @scene, @camera dt = @clock.getDelta() for group in @particleGroups group.tick dt requestAnimationFrame @render #@countFPS() countFPS: -> @framesRendered ?= 0 ++@framesRendered @lastFPS ?= new Date() now = new Date() if now - @lastFPS > 1000 console.log @framesRendered, 'fps with', @particleGroups.length, 'particle groups.' @framesRendered = 0 @lastFPS = now addEmitter: (x, y, kind="level-dungeon-premium") -> return if @unsupported kind = kind.replace 'intro', 'dungeon' options = $.extend true, {}, particleKinds[kind] return console.error "Couldn't find particle configuration for", kind unless options.group options.group.texture = THREE.ImageUtils.loadTexture "/images/common/particles/#{options.group.texture}.png" scale = 100 aspectRatio = @$el group = new SPE.Group options.group group.mesh.position.x = scale * (-0.5 + x) group.mesh.position.y = scale * (-0.5 + y) * @aspectRatio emitter = new SPE.Emitter options.emitter group.addEmitter emitter @particleGroups.push group @scene.add group.mesh group removeEmitter: (group) -> return if @unsupported @scene.remove group.mesh @particleGroups = _.without @particleGroups, group removeEmitters: -> return if @unsupported @removeEmitter group for group in @particleGroups.slice() #addTestCube: -> #geometry = new THREE.BoxGeometry 5, 5, 5 #material = new THREE.MeshLambertMaterial color: 0xFF0000 #mesh = new THREE.Mesh geometry, material #@scene.add mesh #light = new THREE.PointLight 0xFFFF00 #light.position.set 10, 0, 20 #@scene.add light hsl = (hue, saturation, lightness) -> new THREE.Color utils.hslToHex([hue, saturation, lightness]) vec = (x, y, z) -> new THREE.Vector3 x, y, z defaults = group: texture: 'star' maxAge: 1.9 radius: 0.75 hasPerspective: 1 colorize: 1 transparent: 1 alphaTest: 0.5 depthWrite: false depthTest: true blending: THREE.NormalBlending emitter: type: "disk" particleCount: 100 radius: 1 position: vec 0, 0, 0 positionSpread: vec 1, 0, 1 acceleration: vec 0, 2, 0 accelerationSpread: vec 0, 0, 0 velocity: vec 0, 4, 0 velocitySpread: vec 2, 2, 2 sizeStart: 6 sizeStartSpread: 1 sizeMiddle: 4 sizeMiddleSpread: 1 sizeEnd: 2 sizeEndSpread: 1 angleStart: 0 angleStartSpread: 0 angleMiddle: 0 angleMiddleSpread: 0 angleEnd: 0 angleEndSpread: 0 angleAlignVelocity: false colorStart: hsl 0.55, 0.75, 0.75 colorStartSpread: vec 0.3, 0.3, 0.3 colorMiddle: hsl 0.55, 0.6, 0.5 colorMiddleSpread: vec 0.2, 0.2, 0.2 colorEnd: hsl 0.55, 0.5, 0.25 colorEndSpread: vec 0.1, 0.1, 0.1 opacityStart: 1 opacityStartSpread: 0 opacityMiddle: 0.75 opacityMiddleSpread: 0 opacityEnd: 0.25 opacityEndSpread: 0 duration: null alive: 1 isStatic: 0 ext = (d, options) -> $.extend true, {}, d, options ? {} particleKinds = 'level-dungeon-premium': ext defaults 'level-forest-premium': ext defaults, emitter: colorStart: hsl 0.56, 0.97, 0.5 colorMiddle: hsl 0.56, 0.57, 0.5 colorEnd: hsl 0.56, 0.17, 0.5 'level-desert-premium': ext defaults, emitter: colorStart: hsl 0.56, 0.97, 0.5 colorMiddle: hsl 0.56, 0.57, 0.5 colorEnd: hsl 0.56, 0.17, 0.5 'level-mountain-premium': ext defaults, emitter: colorStart: hsl 0.56, 0.97, 0.5 colorMiddle: hsl 0.56, 0.57, 0.5 colorEnd: hsl 0.56, 0.17, 0.5 'level-glacier-premium': ext defaults, emitter: colorStart: hsl 0.56, 0.97, 0.5 colorMiddle: hsl 0.56, 0.57, 0.5 colorEnd: hsl 0.56, 0.17, 0.5 'level-volcano-premium': ext defaults, emitter: colorStart: hsl 0.56, 0.97, 0.5 colorMiddle: hsl 0.56, 0.57, 0.5 colorEnd: hsl 0.56, 0.17, 0.5 particleKinds['level-dungeon-premium-hero'] = ext particleKinds['level-dungeon-premium'], emitter: particleCount: 200 radius: 1.5 acceleration: vec 0, 4, 0 opacityStart: 0.25 opacityMiddle: 0.5 opacityEnd: 0.75 particleKinds['level-dungeon-gate'] = ext particleKinds['level-dungeon-premium'], emitter: particleCount: 2000 acceleration: vec 0, 8, 0 colorStart: hsl 0.5, 0.75, 0.9 colorMiddle: hsl 0.5, 0.75, 0.7 colorEnd: hsl 0.5, 0.75, 0.3 colorStartSpread: vec 1, 1, 1 colorMiddleSpread: vec 1.5, 1.5, 1.5 colorEndSpread: vec 2.5, 2.5, 2.5 particleKinds['level-dungeon-hero-ladder'] = particleKinds['level-dungeon-course-ladder'] = ext particleKinds['level-dungeon-premium'], emitter: particleCount: 200 acceleration: vec 0, 3, 0 colorStart: hsl 0, 0.75, 0.7 colorMiddle: hsl 0, 0.75, 0.5 colorEnd: hsl 0, 0.75, 0.3 particleKinds['level-dungeon-replayable'] = particleKinds['level-dungeon-replayable-premium'] = ext particleKinds['level-dungeon-hero-ladder'], emitter: colorStart: hsl 0.17, 0.75, 0.7 colorMiddle: hsl 0.17, 0.75, 0.5 colorEnd: hsl 0.17, 0.75, 0.3 particleKinds['level-dungeon-premium-item'] = ext particleKinds['level-dungeon-gate'], emitter: particleCount: 2000 radius: 2.5 acceleration: vec 0, 8, 1 opacityStart: 0 opacityMiddle: 0.5 opacityEnd: 0.75 colorStart: hsl 0.5, 0.75, 0.9 colorMiddle: hsl 0.5, 0.75, 0.7 colorEnd: hsl 0.5, 0.75, 0.3 colorStartSpread: vec 1, 1, 1 colorMiddleSpread: vec 1.5, 1.5, 1.5 colorEndSpread: vec 2.5, 2.5, 2.5 particleKinds['level-forest-premium-hero'] = ext particleKinds['level-forest-premium'], emitter: particleCount: 200 radius: 1.5 acceleration: vec 0, 4, 0 opacityStart: 0.25 opacityMiddle: 0.5 opacityEnd: 0.75 particleKinds['level-forest-gate'] = ext particleKinds['level-forest-premium'], emitter: particleCount: 120 velocity: vec 0, 8, 0 colorStart: hsl 0.56, 0.97, 0.3 colorMiddle: hsl 0.56, 0.57, 0.3 colorEnd: hsl 0.56, 0.17, 0.3 colorStartSpread: vec 1, 1, 1 colorMiddleSpread: vec 1.5, 1.5, 1.5 colorEndSpread: vec 2.5, 2.5, 2.5 particleKinds['level-forest-hero-ladder'] = particleKinds['level-forest-course-ladder'] = ext particleKinds['level-forest-premium'], emitter: particleCount: 90 velocity: vec 0, 4, 0 colorStart: hsl 0, 0.95, 0.3 colorMiddle: hsl 0, 1, 0.5 colorEnd: hsl 0, 0.75, 0.1 particleKinds['level-forest-replayable'] = particleKinds['level-forest-replayable-premium'] = ext particleKinds['level-forest-hero-ladder'], emitter: colorStart: hsl 0.17, 0.75, 0.7 colorMiddle: hsl 0.17, 0.75, 0.5 colorEnd: hsl 0.17, 0.75, 0.3 particleKinds['level-forest-premium-item'] = ext particleKinds['level-forest-gate'], emitter: particleCount: 2000 radius: 2.5 acceleration: vec 0, 8, 1 opacityStart: 0 opacityMiddle: 0.5 opacityEnd: 0.75 colorStart: hsl 0.5, 0.75, 0.9 colorMiddle: hsl 0.5, 0.75, 0.7 colorEnd: hsl 0.5, 0.75, 0.3 colorStartSpread: vec 1, 1, 1 colorMiddleSpread: vec 1.5, 1.5, 1.5 colorEndSpread: vec 2.5, 2.5, 2.5 particleKinds['level-desert-premium-hero'] = ext particleKinds['level-desert-premium'], emitter: particleCount: 200 radius: 1.5 acceleration: vec 0, 4, 0 opacityStart: 0.25 opacityMiddle: 0.5 opacityEnd: 0.75 particleKinds['level-desert-gate'] = ext particleKinds['level-desert-premium'], emitter: particleCount: 120 velocity: vec 0, 8, 0 colorStart: hsl 0.56, 0.97, 0.3 colorMiddle: hsl 0.56, 0.57, 0.3 colorEnd: hsl 0.56, 0.17, 0.3 colorStartSpread: vec 1, 1, 1 colorMiddleSpread: vec 1.5, 1.5, 1.5 colorEndSpread: vec 2.5, 2.5, 2.5 particleKinds['level-desert-hero-ladder'] = particleKinds['level-desert-course-ladder'] = ext particleKinds['level-desert-premium'], emitter: particleCount: 90 velocity: vec 0, 4, 0 colorStart: hsl 0, 0.95, 0.3 colorMiddle: hsl 0, 1, 0.5 colorEnd: hsl 0, 0.75, 0.1 particleKinds['level-desert-replayable'] = particleKinds['level-desert-replayable-premium'] = ext particleKinds['level-desert-hero-ladder'], emitter: colorStart: hsl 0.17, 0.75, 0.7 colorMiddle: hsl 0.17, 0.75, 0.5 colorEnd: hsl 0.17, 0.75, 0.3 particleKinds['level-mountain-premium-hero'] = ext particleKinds['level-mountain-premium'], emitter: particleCount: 200 radius: 1.5 acceleration: vec 0, 4, 0 opacityStart: 0.25 opacityMiddle: 0.5 opacityEnd: 0.75 particleKinds['level-mountain-gate'] = ext particleKinds['level-mountain-premium'], emitter: particleCount: 120 velocity: vec 0, 8, 0 colorStart: hsl 0.56, 0.97, 0.3 colorMiddle: hsl 0.56, 0.57, 0.3 colorEnd: hsl 0.56, 0.17, 0.3 colorStartSpread: vec 1, 1, 1 colorMiddleSpread: vec 1.5, 1.5, 1.5 colorEndSpread: vec 2.5, 2.5, 2.5 particleKinds['level-mountain-hero-ladder'] = particleKinds['level-mountain-course-ladder'] = ext particleKinds['level-mountain-premium'], emitter: particleCount: 90 velocity: vec 0, 4, 0 colorStart: hsl 0, 0.95, 0.3 colorMiddle: hsl 0, 1, 0.5 colorEnd: hsl 0, 0.75, 0.1 particleKinds['level-mountain-replayable'] = particleKinds['level-mountain-replayable-premium'] = ext particleKinds['level-mountain-hero-ladder'], emitter: colorStart: hsl 0.17, 0.75, 0.7 colorMiddle: hsl 0.17, 0.75, 0.5 colorEnd: hsl 0.17, 0.75, 0.3 particleKinds['level-glacier-premium-hero'] = ext particleKinds['level-glacier-premium'], emitter: particleCount: 200 radius: 1.5 acceleration: vec 0, 4, 0 opacityStart: 0.25 opacityMiddle: 0.5 opacityEnd: 0.75 particleKinds['level-glacier-gate'] = ext particleKinds['level-glacier-premium'], emitter: particleCount: 120 velocity: vec 0, 8, 0 colorStart: hsl 0.56, 0.97, 0.3 colorMiddle: hsl 0.56, 0.57, 0.3 colorEnd: hsl 0.56, 0.17, 0.3 colorStartSpread: vec 1, 1, 1 colorMiddleSpread: vec 1.5, 1.5, 1.5 colorEndSpread: vec 2.5, 2.5, 2.5 particleKinds['level-glacier-hero-ladder'] = particleKinds['level-glacier-course-ladder'] = ext particleKinds['level-glacier-premium'], emitter: particleCount: 90 velocity: vec 0, 4, 0 colorStart: hsl 0, 0.95, 0.3 colorMiddle: hsl 0, 1, 0.5 colorEnd: hsl 0, 0.75, 0.1 particleKinds['level-glacier-replayable'] = particleKinds['level-glacier-replayable-premium'] = ext particleKinds['level-glacier-hero-ladder'], emitter: colorStart: hsl 0.17, 0.75, 0.7 colorMiddle: hsl 0.17, 0.75, 0.5 colorEnd: hsl 0.17, 0.75, 0.3 particleKinds['level-volcano-premium-hero'] = ext particleKinds['level-volcano-premium'], emitter: particleCount: 200 radius: 1.5 acceleration: vec 0, 4, 0 opacityStart: 0.25 opacityMiddle: 0.5 opacityEnd: 0.75 particleKinds['level-volcano-gate'] = ext particleKinds['level-volcano-premium'], emitter: particleCount: 120 velocity: vec 0, 8, 0 colorStart: hsl 0.56, 0.97, 0.3 colorMiddle: hsl 0.56, 0.57, 0.3 colorEnd: hsl 0.56, 0.17, 0.3 colorStartSpread: vec 1, 1, 1 colorMiddleSpread: vec 1.5, 1.5, 1.5 colorEndSpread: vec 2.5, 2.5, 2.5 particleKinds['level-volcano-hero-ladder'] = ext particleKinds['level-volcano-premium'], emitter: particleCount: 90 velocity: vec 0, 4, 0 colorStart: hsl 0, 0.95, 0.3 colorMiddle: hsl 0, 1, 0.5 colorEnd: hsl 0, 0.75, 0.1 particleKinds['level-volcano-replayable'] = particleKinds['level-volcano-replayable-premium'] = ext particleKinds['level-volcano-hero-ladder'], emitter: colorStart: hsl 0.17, 0.75, 0.7 colorMiddle: hsl 0.17, 0.75, 0.5 colorEnd: hsl 0.17, 0.75, 0.3