Billboards

SourceEdit
-- red puffs are billboards oriented in GLSL
-- blue puffs are billboards oriented in Lua

function lovr.load()
  -- puff texture
  puffImageSize = 128                                                         -- try 16
  puffImage = lovr.data.newImage(puffImageSize, puffImageSize)
  for x = 0, puffImageSize - 1 do
    for y = 0, puffImageSize - 1 do
      local radius = puffImageSize / 2 - 1
      local distanceFromCenter = vec2(radius, radius):distance(vec2(x, y))
      local depth = math.sqrt(radius^2 - distanceFromCenter^2)
      puffImage:setPixel(x, y, 1, 1, 1, depth / radius)
      lovr.math.drain()
    end
  end
  puffTexture = lovr.graphics.newTexture(puffImage)
  puffMaterial = lovr.graphics.newMaterial({ texture = puffTexture })

  -- puff positions
  redPuffs = {}
  for i = 1, 100 do
    table.insert(redPuffs, {
      position = lovr.math.newVec3(
        lovr.math.random() - .5,
        lovr.math.random() - .5 + 1.7,
        lovr.math.random() - .5
      )
    })
  end
  bluePuffs = {}
  for i = 1, 100 do
    table.insert(bluePuffs, {
      position = lovr.math.newVec3(
        lovr.math.random() - .5, 
        lovr.math.random() - .5 + 1.7,
        lovr.math.random() - .5
      )
    })
  end

  -- red puff billboard shader
  redPuffVertex = [[
    Constants {
      vec3 headPosition;
    };

    mat4 target(vec3 from, vec3 to, vec3 up) {
      mat4 m;
      vec3 z = normalize(from - to);
      vec3 x = normalize(cross(up, z));
      vec3 y = cross(z, x);
      m[0] = vec4(x, 0);
      m[1] = vec4(y, 0);
      m[2] = vec4(z, 0);
      m[3] = vec4(from, 1);
      return m;
    }

    vec4 lovrmain() {
      vec3 puffPosition = Transform[3].xyz;
      vec3 puffScale = vec3(length(Transform[0]), length(Transform[1]), length(Transform[2]));
      mat4 billboardModel = target(puffPosition, headPosition, vec3(0, 1, 0));
      billboardModel *= mat4(
        puffScale.x, 0,           0,           0,
        0,           puffScale.y, 0,           0,
        0,           0,           puffScale.z, 0,
        0,           0,           0,           1
      );
      return Projection * View * billboardModel * VertexPosition;
    }
  ]]
  defaultFragment = [[
    vec4 lovrmain() {
      return DefaultColor;
    }
  ]]
  redPuffShader = lovr.graphics.newShader(redPuffVertex, defaultFragment)

  lovr.graphics.setBackgroundColor(0, .5, 1)
end

function lovr.draw(pass)
  pass:setSampler('nearest')                                         -- try uncommenting

  -- red puffs
  pass:setColor(1, .9, .9)
  pass:setMaterial(puffMaterial)
  pass:setDepthWrite(false)
  pass:setShader(redPuffShader)
  pass:send('headPosition', { lovr.headset.getPosition('head') })
  for _, puff in pairs(redPuffs) do
    pass:plane(puff.position, .1)
  end
  pass:setShader()

  -- blue puffs
  pass:setColor(.9, 1, 1)
  for _, puff in pairs(bluePuffs) do
    puff.orientation = quat(mat4():target(puff.position, vec3(lovr.headset.getPosition('head'))))
    pass:plane(puff.position, .1, .1, puff.orientation)
  end
end