From e0b0db8cf7b49c4ff2b9862d05156f2d56674def Mon Sep 17 00:00:00 2001 From: Felix Ableitner Date: Thu, 12 Sep 2013 18:13:03 +0200 Subject: [PATCH] Added lighting using ltbl library. --- res/shaders/light_attenuation_shader.frag | 26 +++++++++ res/textures/light_fin.png | Bin 0 -> 12979 bytes res/yaml/light.yaml | 7 +++ src/Game.cpp | 67 ++++++++++++++++++---- src/Game.h | 8 +++ src/generator/Generator.cpp | 29 ++++++++-- src/generator/Generator.h | 7 ++- src/main.cpp | 3 + src/sprites/Tile.cpp | 15 +++-- src/sprites/Tile.h | 3 +- src/sprites/abstract/Sprite.cpp | 7 ++- src/sprites/abstract/Sprite.h | 3 +- src/sprites/items/RingOfFire.cpp | 2 +- src/sprites/items/Shield.cpp | 2 +- src/sprites/items/Weapon.cpp | 4 +- src/util/Angles.cpp | 18 ++++++ src/util/Angles.h | 15 +++++ src/util/Vector.h | 12 ++++ 18 files changed, 202 insertions(+), 26 deletions(-) create mode 100644 res/shaders/light_attenuation_shader.frag create mode 100644 res/textures/light_fin.png create mode 100644 res/yaml/light.yaml create mode 100644 src/util/Angles.cpp create mode 100644 src/util/Angles.h diff --git a/res/shaders/light_attenuation_shader.frag b/res/shaders/light_attenuation_shader.frag new file mode 100644 index 0000000..d542375 --- /dev/null +++ b/res/shaders/light_attenuation_shader.frag @@ -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; +} \ No newline at end of file diff --git a/res/textures/light_fin.png b/res/textures/light_fin.png new file mode 100644 index 0000000000000000000000000000000000000000..838d442b40467db9ac0d34959930edcd6467a03f GIT binary patch literal 12979 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 literal 0 HcmV?d00001 diff --git a/res/yaml/light.yaml b/res/yaml/light.yaml new file mode 100644 index 0000000..844c7e9 --- /dev/null +++ b/res/yaml/light.yaml @@ -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 \ No newline at end of file diff --git a/src/Game.cpp b/src/Game.cpp index 52d8584..c0707b3 100644 --- a/src/Game.cpp +++ b/src/Game.cpp @@ -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("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(); diff --git a/src/Game.h b/src/Game.h index 22b310e..41e7c43 100644 --- a/src/Game.h +++ b/src/Game.h @@ -10,6 +10,9 @@ #include +#include +#include + #include "generator/Generator.h" #include "Pathfinder.h" #include "World.h" @@ -39,6 +42,7 @@ private: Vector2 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 mPlayer; bool mQuit = false; diff --git a/src/generator/Generator.cpp b/src/generator/Generator.cpp index c91873a..39b6c01 100644 --- a/src/generator/Generator.cpp +++ b/src/generator/Generator.cpp @@ -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( 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(); diff --git a/src/generator/Generator.h b/src/generator/Generator.h index 1d0b757..4dbdab4 100644 --- a/src/generator/Generator.h +++ b/src/generator/Generator.h @@ -10,6 +10,8 @@ #include +#include + #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 mHulls; /// Used only for debug drawing. std::vector > mPaths; }; diff --git a/src/main.cpp b/src/main.cpp index 12e207a..f56e74c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -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'"); diff --git a/src/sprites/Tile.cpp b/src/sprites/Tile.cpp index 0c11578..e2edda0 100644 --- a/src/sprites/Tile.cpp +++ b/src/sprites/Tile.cpp @@ -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 converted = std::dynamic_pointer_cast(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. */ diff --git a/src/sprites/Tile.h b/src/sprites/Tile.h index fda23c8..f1a628c 100644 --- a/src/sprites/Tile.h +++ b/src/sprites/Tile.h @@ -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; diff --git a/src/sprites/abstract/Sprite.cpp b/src/sprites/abstract/Sprite.cpp index 95b7ca1..5c6e2a9 100755 --- a/src/sprites/abstract/Sprite.cpp +++ b/src/sprites/abstract/Sprite.cpp @@ -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. */ diff --git a/src/sprites/abstract/Sprite.h b/src/sprites/abstract/Sprite.h index ba85a8b..1553d4d 100755 --- a/src/sprites/abstract/Sprite.h +++ b/src/sprites/abstract/Sprite.h @@ -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; diff --git a/src/sprites/items/RingOfFire.cpp b/src/sprites/items/RingOfFire.cpp index 7c13115..403b497 100644 --- a/src/sprites/items/RingOfFire.cpp +++ b/src/sprites/items/RingOfFire.cpp @@ -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 projectile(new Bullet(mCharacter->getPosition() + direction, diff --git a/src/sprites/items/Shield.cpp b/src/sprites/items/Shield.cpp index 555febf..5eab3bc 100644 --- a/src/sprites/items/Shield.cpp +++ b/src/sprites/items/Shield.cpp @@ -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( new RotatingShield(mCharacter->getPosition() + offset)); mCharacter->mWorld.insert(mRotatingShield); diff --git a/src/sprites/items/Weapon.cpp b/src/sprites/items/Weapon.cpp index dd31baa..a4f1a59 100755 --- a/src/sprites/items/Weapon.cpp +++ b/src/sprites/items/Weapon.cpp @@ -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 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 projectile(new Bullet(mHolder->getPosition() + offset, *mHolder, direction, mProjectile, mProjectileSpeed, diff --git a/src/util/Angles.cpp b/src/util/Angles.cpp new file mode 100644 index 0000000..e85e601 --- /dev/null +++ b/src/util/Angles.cpp @@ -0,0 +1,18 @@ +/* + * Angles.cpp + * + * Created on: 12.09.2013 + * Author: felix + */ + +#include "Angles.h" + +#include + +float radianToDegree(float radian) { + return radian * 180 / M_PI; +} + +float degreeToRadian(float degree) { + return degree * M_PI / 180; +} diff --git a/src/util/Angles.h b/src/util/Angles.h new file mode 100644 index 0000000..5f3092c --- /dev/null +++ b/src/util/Angles.h @@ -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_ */ diff --git a/src/util/Vector.h b/src/util/Vector.h index 9691ec2..dee4f21 100644 --- a/src/util/Vector.h +++ b/src/util/Vector.h @@ -10,6 +10,8 @@ #include +#include + /** * Vector class with comparison operator. All other operators are inherited * from sf::Vector2. @@ -17,6 +19,9 @@ template class Vector2 : public sf::Vector2 { public: + /// Needed for conversion to Vec2f. + static int SCREEN_HEIGHT; + Vector2() : sf::Vector2() {}; Vector2(T x, T y) : sf::Vector2(x, y) {}; /** @@ -26,8 +31,15 @@ public: template Vector2(const sf::Vector2& vector) : sf::Vector2(vector) {}; + Vec2f + toVec2f() { + return Vec2f(sf::Vector2f::x, SCREEN_HEIGHT - sf::Vector2f::y); + } }; +template +int Vector2::SCREEN_HEIGHT = 0; + /** * Comparison operator meant for containers like std::set. *