cactus source/threejs/trampoline.coffee

shuffle = (a) ->
    i = a.length
    while --i > 0
        j = ~~(Math.random() * (i + 1))
        t = a[j]
        a[j] = a[i]
        a[i] = t
    a

class Trampoline
  size: 14
  constructor: (@scene, $options) ->
    @subdivisions = @size
    @geometry = new THREE.PlaneGeometry(@size, @size, @subdivisions, @subdivisions )

    @uniforms = {
      size: {type: 'f', value: @size}
      width: {type: 'f', value: window.innerWidth}
      height: {type: 'f', value: window.innerHeight}
      time: {type: 'f', value: 0.0}
    }

    @uniforms = THREE.UniformsUtils.merge([THREE.UniformsLib['lights'], @uniforms])

    @attributes = {
      idx: {type: 'f', value: []}
      velocity: {type: 'f', value: []}
    }

    if $options.trampolineShader
      @materials = [
         new THREE.ShaderMaterial { transparent: false, lights: true, side: THREE.DoubleSide, uniforms: @uniforms,	attributes: @attributes, vertexShader: document.getElementById( 'trampolineVertShader' ).textContent,	fragmentShader: document.getElementById( 'trampolineFragShader' ).textContent }
       ]
    else
      @materials = [
        new THREE.MeshBasicMaterial {wireframe: true, transparent: true, color: 0x333333, shading: THREE.FlatShading}
        new THREE.MeshPhongMaterial {  specular: 0x111111, shininess: 20, vertexColors: THREE.VertexColors, side: THREE.DoubleSide},
      ]

    @halfsize = @size / 2
    @vwidth = @subdivisions + 1

    @acceleration = []
    @velocity = []
    for x in [0...@vwidth]
      @acceleration[x] = []
      @velocity[x] = []
      for y in [0...@vwidth]
        @acceleration[x][y] = 6.0
        @velocity[x][y] = 0.0
        @attributes.velocity.value[@at(x,y)] = 0.0

    faceIndices = [ 'a', 'b', 'c', 'd' ]
    for face in @geometry.faces
      numberOfSides = 3
      for j in [0...numberOfSides]
        vertexIndex = face[ faceIndices[ j ] ];
        @attributes.idx.value[vertexIndex] = vertexIndex
        p = @geometry.vertices[vertexIndex]
        color = new THREE.Color( 0xffffff );
        color.setRGB(p.x / @halfsize, 1.0 - p.y / @halfsize, (p.x * p.x + p.y * p.y) / (@halfsize * @halfsize))
        face.vertexColors[j] = color


    # @mesh = new THREE.Mesh @geometry, @material
    @mesh = THREE.SceneUtils.createMultiMaterialObject @geometry, @materials
    @mesh.rotation.x = - Math.PI / 2.0
    @scene.add @mesh
    @geometry.colorsNeedUpdate = true

  at: (x, z) ->
    x = (x) / @size * @vwidth
    z = (z) / @size * @vwidth
    return Math.floor(z * @subdivisions + x)

  dist: (x, z) ->
    @geometry.vertices[@at(x,z)].z

  update: (step) ->
    @uniforms.time.value += step
    for x in shuffle [0..@subdivisions - 1]
      for y in shuffle [0..@subdivisions - 1]
        idx = @at(x, y)

        k = 50.5 * step
        d = 5.0
        da = 7.0
        dk = k * 0.25

        @geometry.vertices[idx].z += @velocity[x][y] * step
        @velocity[x][y] += @acceleration[x][y] * step * d
        @acceleration[x][y] = - da * @geometry.vertices[idx].z

        # @velocity[x][y] = 0.0 if @geometry.vertices[idx].z > 0 and @velocity[x][y] > 0
        @geometry.vertices[idx].z = 0.0 if @geometry.vertices[idx].z > 0


        limit = @subdivisions
        z = @geometry.vertices[idx].z
        if x + 1 < limit
          @velocity[x][y] += (@dist(x + 1, y) - z) * k
        if y + 1 < limit
          @velocity[x][y] += (@dist(x, y + 1) - z) * k
        if x - 1 > 0
          @velocity[x][y] += (@dist(x - 1, y) - z) * k
        if y - 1 > 0
          @velocity[x][y] += (@dist(x, y - 1) - z) * k

        if x + 1 < limit and y + 1 < limit
          @velocity[x][y] += (@dist(x + 1, y + 1) - z) * dk
        if x + 1 < limit and y - 1 > 0
          @velocity[x][y] += (@dist(x + 1, y - 1) - z) * dk
        if x - 1 > 0 and y + 1 < limit
          @velocity[x][y] += (@dist(x - 1, y + 1) - z) * dk
        if x - 1 > 0 and y - 1 > 0
          @velocity[x][y] += (@dist(x - 1, y - 1) - z) * dk

        @attributes.velocity.value[idx] = @velocity[x][y]

    @geometry.verticesNeedUpdate = true

  impact: (particle) ->
    k = 0.8 * particle.mass
    vk = 0.9
    d = 0.05

    rx = particle.position.x + @halfsize
    ry = particle.position.z + @halfsize
    x = Math.floor(rx)
    y = Math.floor(ry)

    py = particle.velocity.length() + particle.angularVelocity.y

    fx = rx - x
    fy = ry - y

    newV = particle.velocity.y * d

    limit = @subdivisions

    if x < limit and x > 0 and y < limit and y > 0
      newV += @velocity[x][y] * vk * (1.0 - fx) * (1.0 - fy)
      @velocity[x][y] += - py * k * (1.0 - fx) * (1.0 - fy)
    if x + 1 < limit and x + 1 > 0 and y < limit and y > 0
      newV += @velocity[x + 1][y] * vk * fx * (1.0 - fy)
      @velocity[x + 1][y] += - py * k * fx * (1.0 - fy)
    if x + 1 < limit and x + 1 > 0 and y + 1 < limit and y + 1 > 0
      newV += @velocity[x + 1][y + 1] * vk * fx * fy
      @velocity[x + 1][y + 1] += - py * k * fx * fy
    if x < limit and x > 0 and y + 1 < limit and y + 1 > 0
      newV += @velocity[x][y + 1] * vk * (1.0 - fx) * fy
      @velocity[x][y + 1] += - py * k * (1.0 - fx) * (fy)

    if newV < 0
      newV = 0.01

    particle.velocity.y += newV


window.Trampoline = Trampoline