Added lighting using ltbl library.

This commit is contained in:
Felix Ableitner 2013-09-12 18:13:03 +02:00
parent 7df94496e0
commit e0b0db8cf7
18 changed files with 202 additions and 26 deletions

View File

@ -0,0 +1,26 @@
uniform vec2 lightPos;
uniform vec3 lightColor;
uniform float radius;
uniform float bleed;
uniform float linearizeFactor;
void main()
{
float dist = length(lightPos - gl_FragCoord.xy);
float distFromFalloff = radius - dist;
// Still has absolute falloff point
float attenuation = distFromFalloff * (bleed / pow(dist, 2.0) + linearizeFactor / radius);
// Optional, clamp it to prevent overcoloring
attenuation = clamp(attenuation, 0.0, 1.0);
vec4 color = vec4(attenuation, attenuation, attenuation, 1.0) * vec4(lightColor.r, lightColor.g, lightColor.b, 1.0);
gl_FragColor = color;
}

BIN
res/textures/light_fin.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

7
res/yaml/light.yaml Normal file
View File

@ -0,0 +1,7 @@
# Color values for player lights, each between 0 and 255
color_red: 255
color_green: 255
color_blue: 127
# Total angle of the player light cone
light_cone_angle: 90.0

View File

@ -13,6 +13,7 @@
#include "sprites/Enemy.h"
#include "sprites/Player.h"
#include "sprites/items/HealthOrb.h"
#include "util/Angles.h"
#include "util/Loader.h"
#include "util/Yaml.h"
@ -22,12 +23,15 @@
Game::Game(tgui::Window& window) :
mWindow(window),
mWorldView(Vector2f(0, 0), mWindow.getView().getSize()),
mGenerator(mWorld, mPathfinder, Yaml("generation.yaml")) {
mLightSystem(AABB(Vec2f(-100000, -100000), Vec2f(100000, 10000)), &window,
"res/textures/light_fin.png", "res/shaders/light_attenuation_shader.frag"),
mGenerator(mWorld, mPathfinder, mLightSystem, Yaml("generation.yaml")) {
mWindow.setFramerateLimit(FPS_GOAL);
mWindow.setKeyRepeatEnabled(false);
srand(time(nullptr));
initPlayer();
initLight();
mCrosshairTexture = Loader::i().fromFile<sf::Texture>("crosshair.png");
mCrosshair.setTexture(*mCrosshairTexture, true);
@ -47,7 +51,15 @@ Game::Game(tgui::Window& window) :
mPickupInstruction->setTextSize(14);
}
void Game::initPlayer() {
/**
* Closes window.
*/
Game::~Game() {
mWindow.close();
}
void
Game::initPlayer() {
Character::EquippedItems playerItems = {
Weapon::WeaponType::PISTOL, Weapon::WeaponType::KNIFE,
Gadget::GadgetType::NONE, Gadget::GadgetType::NONE
@ -58,11 +70,37 @@ void Game::initPlayer() {
mWorld.insertCharacter(mPlayer);
}
/**
* Closes window.
*/
Game::~Game() {
mWindow.close();
void
Game::initLight() {
Yaml config("light.yaml");
Color3f lightColor(config.get("color_red", 0) / 255.0f,
config.get("color_green", 0) / 255.0f,
config.get("color_blue", 0) / 255.0f);
mLightSystem.m_checkForHullIntersect = false;
mLightSystem.m_useBloom = false;
mPlayerAreaLight->m_radius = 250.0f;
mPlayerAreaLight->m_size = 1.0f;
mPlayerAreaLight->m_softSpreadAngle = 0;
mPlayerAreaLight->m_spreadAngle = 2.0f * M_PI;
mPlayerAreaLight->m_intensity = 1.1f;
mPlayerAreaLight->m_bleed = 0;
mPlayerAreaLight->m_color = lightColor;
mPlayerDirectionLight->m_linearizeFactor = 0.5;
mPlayerAreaLight->CalculateAABB();
mLightSystem.AddLight(mPlayerAreaLight);
mPlayerDirectionLight->m_radius = 500.0f;
mPlayerDirectionLight->m_size = 25.0f;
mPlayerDirectionLight->m_softSpreadAngle = 0.1f * M_PI;
mPlayerDirectionLight->m_spreadAngle =
degreeToRadian(config.get("light_cone_angle", 0.0f));
mPlayerDirectionLight->m_intensity = 5;
mPlayerDirectionLight->m_bleed = 0;
mPlayerDirectionLight->m_color = lightColor;
mPlayerDirectionLight->m_linearizeFactor = 1;
mPlayerDirectionLight->CalculateAABB();
mLightSystem.AddLight(mPlayerDirectionLight);
}
/**
@ -73,9 +111,9 @@ Game::loop() {
while (!mQuit) {
input();
int elapsed = mClock.restart().asMilliseconds();
if (mPaused)
elapsed = 0;
int elapsed = (mPaused)
? 0
: mClock.restart().asMilliseconds();
mWorld.think(elapsed);
if (mPlayer->getHealth() == 0) {
@ -301,6 +339,15 @@ Game::render() {
mWindow.setView(mWorldView);
mWindow.draw(mWorld);
// Update light
mPlayerAreaLight->SetCenter(mPlayer->getPosition().toVec2f());
mPlayerDirectionLight->SetCenter(mPlayer->getPosition().toVec2f());
mPlayerDirectionLight->SetDirectionAngle(degreeToRadian(90 - mPlayer->getDirection()));
mLightSystem.SetView(mWorldView);
mLightSystem.RenderLights();
mLightSystem.RenderLightTexture();
// Render GUI and static stuff.
mWindow.setView(mWindow.getDefaultView());
mWindow.drawGUI();

View File

@ -10,6 +10,9 @@
#include <TGUI/TGUI.hpp>
#include <LTBL/Light/LightSystem.h>
#include <LTBL/Light/Light_Point.h>
#include "generator/Generator.h"
#include "Pathfinder.h"
#include "World.h"
@ -39,6 +42,7 @@ private:
Vector2<float> convertCoordinates(int x, int y);
void updateGui();
void initPlayer();
void initLight();
private:
static const int FPS_GOAL = 60;
@ -57,7 +61,11 @@ private:
World mWorld;
Pathfinder mPathfinder;
ltbl::LightSystem mLightSystem;
Generator mGenerator;
ltbl::Light_Point* mPlayerAreaLight = new ltbl::Light_Point();
ltbl::Light_Point* mPlayerDirectionLight = new ltbl::Light_Point();
std::shared_ptr<Player> mPlayer;
bool mQuit = false;

View File

@ -24,14 +24,16 @@
/**
* Generates new random seed.
*/
Generator::Generator(World& world, Pathfinder& pathfinder, const Yaml& config) :
Generator::Generator(World& world, Pathfinder& pathfinder,
ltbl::LightSystem& lightSystem, const Yaml& config) :
mAreaSize(config.get("generate_area_size", 1)),
mMaxRange((config.get("generate_area_range", 1.0f) / mAreaSize) / Tile::TILE_SIZE.x),
mRoomSizeValue(config.get("room_size_value", 1.0f)),
mRoomConnectionValue(config.get("room_connection_value", 1.0f)),
mEnemyGenerationChance(config.get("enemy_generation_chance", 0.0f) * 2 - 1),
mWorld(world),
mPathfinder(pathfinder) {
mPathfinder(pathfinder),
mLightSystem(lightSystem) {
}
/**
@ -188,6 +190,12 @@ Generator::connectRooms(const Vector2i& start) {
for (const auto& p : path) {
mTiles[p.x][p.y] = Tile::Type::FLOOR;
Tile::setTile(p, Tile::Type::FLOOR, mWorld);
for (auto it = mHulls.begin(); it != mHulls.end(); it++)
if ((*it)->GetWorldCenter() == Tile::toPosition(Vector2i(p.x, p.y)).toVec2f()) {
mLightSystem.RemoveConvexHull(*it);
mHulls.erase(it);
break;
}
}
}
}
@ -245,11 +253,24 @@ Generator::generateTiles(const sf::IntRect& area) {
mTiles[x][y] = Tile::Type::FLOOR;
connectRooms(start);
for (int x = area.left; x < area.left + area.width; x++)
for (int y = area.top; y < area.top + area.height; y++)
for (int y = area.top; y < area.top + area.height; y++) {
mWorld.insert(std::shared_ptr<Sprite>(
new Tile(Vector2i(x, y), mTiles[x][y])));
if (mTiles[x][y] == Tile::Type::WALL) {
ltbl::ConvexHull* tileHull = new ltbl::ConvexHull();
tileHull->m_vertices.push_back(Vec2f(-37.5f, 37.5f));
tileHull->m_vertices.push_back(Vec2f(-37.5f, -37.5f));
tileHull->m_vertices.push_back(Vec2f( 37.5f, -37.5f));
tileHull->m_vertices.push_back(Vec2f( 37.5f, 37.5f));
tileHull->m_renderLightOverHull = false;
tileHull->CalculateNormals();
tileHull->CalculateAABB();
tileHull->SetWorldCenter(Tile::toPosition(Vector2i(x, y)).toVec2f());
mLightSystem.AddConvexHull(tileHull);
mHulls.push_back(tileHull);
}
}
generateAreas(area);
mPathfinder.generatePortals();

View File

@ -10,6 +10,8 @@
#include <SFML/Graphics.hpp>
#include <LTBL/Light/LightSystem.h>
#include "../sprites/abstract/Character.h"
#include "../sprites/Tile.h"
#include "SimplexNoise.h"
@ -24,7 +26,8 @@ class Yaml;
*/
class Generator : public sf::Drawable {
public:
explicit Generator(World& world, Pathfinder& pathfinder, const Yaml& config);
explicit Generator(World& world, Pathfinder& pathfinder,
ltbl::LightSystem& lightSystem, const Yaml& config);
void generateCurrentAreaIfNeeded(const Vector2f& position,
const Character::EquippedItems& playerItems);
Vector2f getPlayerSpawn() const;
@ -51,6 +54,7 @@ private:
World& mWorld;
Pathfinder& mPathfinder;
ltbl::LightSystem& mLightSystem;
/// Contains values of all tiles that have yet been generated.
array mTiles;
/// Stores where tiles have already been generated.
@ -59,6 +63,7 @@ private:
SimplexNoise mTileNoise;
/// Perlin noise used for character placement.
SimplexNoise mCharacterNoise;
std::vector<ltbl::ConvexHull*> mHulls;
/// Used only for debug drawing.
std::vector<std::vector<Vector2i> > mPaths;
};

View File

@ -10,6 +10,8 @@
#include "util/Yaml.h"
#include "util/Log.h"
#include "util/Vector.h"
/**
* Creates Game object.
*/
@ -21,6 +23,7 @@ int main(int argc, char* argv[]) {
tgui::Window window(sf::VideoMode(1024, 768, 32), "Dungeon Gunner",
sf::Style::Close | sf::Style::Titlebar);
Vector2f::SCREEN_HEIGHT = window.getSize().y;
if (!window.globalFont.loadFromFile("res/DejaVuSans.ttf"))
LOG_W("Failed to load font at 'res/DejaVuSans.ttf'");

View File

@ -22,8 +22,8 @@ const Vector2i Tile::TILE_SIZE = Vector2i(75, 75);
*
* @param pType Type of the tile to create.
*/
Tile::Tile(const Vector2i& position, Type type) :
Rectangle(Vector2f(thor::cwiseProduct(position, TILE_SIZE)),
Tile::Tile(const Vector2i& tilePosition, Type type) :
Rectangle(toPosition(tilePosition),
CATEGORY_WORLD, (isSolid(type)) ? 0xffff : 0,
Yaml(getConfig(type))), mType(type) {
}
@ -42,8 +42,7 @@ Tile::setTile(const Vector2i& position, Type type, World& world) {
std::shared_ptr<Tile> converted = std::dynamic_pointer_cast<Tile>(c);
// Direct comparison of floats as both are from the same generation
// on the same CPU.
if (converted.get() != nullptr &&
converted->getPosition() == worldPosition &&
if (converted && converted->getPosition() == worldPosition &&
converted->getType() != type) {
world.remove(converted);
break;
@ -78,6 +77,14 @@ Tile::isSolid(Type type) {
}
}
/**
* Converts a tile position to world/pixel position.
*/
Vector2f
Tile::toPosition(const Vector2i& tilePosition) {
return Vector2f(thor::cwiseProduct(tilePosition, TILE_SIZE));
}
/**
* Returns the Type of this tile.
*/

View File

@ -24,12 +24,13 @@ public:
static const Vector2i TILE_SIZE; //< Tile size in pixels.
public:
explicit Tile(const Vector2i& position, Type type);
explicit Tile(const Vector2i& tilePosition, Type type);
Type getType() const;
static void setTile(const Vector2i& position, Type type, World& world);
static std::string getConfig(Type type);
static bool isSolid(Type type);
static Vector2f toPosition(const Vector2i& tilePosition);
private:
Type mType;

View File

@ -45,10 +45,15 @@ Sprite::getSpeed() const {
* Returns the angle of the sprite.
*/
Vector2f
Sprite::getDirection() const {
Sprite::getDirectionVector() const {
return thor::rotatedVector(Vector2f(0, - 1), mShape.getRotation());
}
float
Sprite::getDirection() const {
return mShape.getRotation();
}
/**
* Returns true if this object should be deleted.
*/

View File

@ -48,7 +48,8 @@ public:
Vector2f getPosition() const;
Vector2f getSpeed() const;
Vector2f getDirection() const;
Vector2f getDirectionVector() const;
float getDirection() const;
bool getDelete() const;
Category getCategory() const;
Vector2f getSize() const;

View File

@ -35,7 +35,7 @@ void
RingOfFire::onThink(int elapsed) {
if (mCurrentWave < mWavesPerUse && mTimer.isExpired()) {
for (int angle = mCurrentWave * 10; angle <= 360; angle += 360 / mBulletsPerWave) {
Vector2f direction(thor::rotatedVector(mCharacter->getDirection(), (float) angle) *
Vector2f direction(thor::rotatedVector(mCharacter->getDirectionVector(), (float) angle) *
mCharacter->getRadius());
std::shared_ptr<Sprite> projectile(new Bullet(mCharacter->getPosition() + direction,

View File

@ -22,7 +22,7 @@ Shield::onUse(Character& character) {
mCharacter = &character;
if (mRotatingShield)
mRotatingShield->setDelete(true);
Vector2f offset = mCharacter->getDirection() * mCharacter->getRadius();
Vector2f offset = mCharacter->getDirectionVector() * mCharacter->getRadius();
mRotatingShield = std::shared_ptr<RotatingShield>(
new RotatingShield(mCharacter->getPosition() + offset));
mCharacter->mWorld.insert(mRotatingShield);

View File

@ -181,7 +181,7 @@ Weapon::setHolder(Character& holder) {
*/
void
Weapon::insertProjectile(float angle) {
Vector2f offset(mHolder->getDirection() * mHolder->getRadius());
Vector2f offset(mHolder->getDirectionVector() * mHolder->getRadius());
float spread = (mHolder->getSpeed() == Vector2f())
? mSpread
@ -189,7 +189,7 @@ Weapon::insertProjectile(float angle) {
std::uniform_real_distribution<float> distribution(- spread, spread);
angle += distribution(mGenerator) + 90.0f;
Vector2f direction(thor::rotatedVector(mHolder->getDirection(), angle));
Vector2f direction(thor::rotatedVector(mHolder->getDirectionVector(), angle));
std::shared_ptr<Sprite> projectile(new Bullet(mHolder->getPosition() + offset,
*mHolder, direction, mProjectile, mProjectileSpeed,

18
src/util/Angles.cpp Normal file
View File

@ -0,0 +1,18 @@
/*
* Angles.cpp
*
* Created on: 12.09.2013
* Author: felix
*/
#include "Angles.h"
#include <math.h>
float radianToDegree(float radian) {
return radian * 180 / M_PI;
}
float degreeToRadian(float degree) {
return degree * M_PI / 180;
}

15
src/util/Angles.h Normal file
View File

@ -0,0 +1,15 @@
/*
* Angles.h
*
* Created on: 12.09.2013
* Author: felix
*/
#ifndef ANGLES_H_
#define ANGLES_H_
float radianToDegree(float radian);
float degreeToRadian(float degree);
#endif /* ANGLES_H_ */

View File

@ -10,6 +10,8 @@
#include <SFML/System.hpp>
#include <LTBL/Constructs/Vec2f.h>
/**
* Vector class with comparison operator. All other operators are inherited
* from sf::Vector2.
@ -17,6 +19,9 @@
template <typename T>
class Vector2 : public sf::Vector2<T> {
public:
/// Needed for conversion to Vec2f.
static int SCREEN_HEIGHT;
Vector2() : sf::Vector2<T>() {};
Vector2(T x, T y) : sf::Vector2<T>(x, y) {};
/**
@ -26,8 +31,15 @@ public:
template <typename U>
Vector2(const sf::Vector2<U>& vector) : sf::Vector2<T>(vector) {};
Vec2f
toVec2f() {
return Vec2f(sf::Vector2f::x, SCREEN_HEIGHT - sf::Vector2f::y);
}
};
template <typename T>
int Vector2<T>::SCREEN_HEIGHT = 0;
/**
* Comparison operator meant for containers like std::set.
*