diff --git a/src/framework/graphics/image.h b/src/framework/graphics/image.h index 1762785d..6608fb6c 100644 --- a/src/framework/graphics/image.h +++ b/src/framework/graphics/image.h @@ -39,6 +39,8 @@ public: void paste(const ImagePtr& other); bool nextMipmap(); + void setPixel(int x, int y, uint8 *pixel) { memcpy(&m_pixels[(y * m_size.width() + x) * m_bpp], pixel, m_bpp);} + std::vector& getPixels() { return m_pixels; } uint8* getPixelData() { return &m_pixels[0]; } int getPixelCount() { return m_size.area(); } @@ -46,7 +48,7 @@ public: int getWidth() { return m_size.width(); } int getHeight() { return m_size.height(); } int getBpp() { return m_bpp; } - uint8* getPixel(int x, int y) { return &m_pixels[(y * m_size.width() + x) * 4]; } + uint8* getPixel(int x, int y) { return &m_pixels[(y * m_size.width() + x) * m_bpp]; } private: std::vector m_pixels; diff --git a/src/otclient/creature.cpp b/src/otclient/creature.cpp index a13bdf38..1b533c32 100644 --- a/src/otclient/creature.cpp +++ b/src/otclient/creature.cpp @@ -81,8 +81,19 @@ void Creature::draw(const Point& dest, float scaleFactor, bool animate, LightVie internalDrawOutfit(dest + animationOffset * scaleFactor, scaleFactor, animate, animate, m_direction); m_footStepDrawn = true; - if(lightView && m_light.intensity > 0) - lightView->addLightSource(dest + (animationOffset + Point(16,16)) * scaleFactor, scaleFactor, m_light); + if(lightView) { + Light light = m_light; + + // local player always have a minimum light in complete darkness + if(isLocalPlayer() && (g_map.getLight().intensity < 40 || m_position.z > Otc::SEA_FLOOR)) { + light.intensity = std::max(light.intensity, 2); + if(light.color == 0 || light.color > 215) + light.color = 215; + } + + if(light.intensity > 0) + lightView->addLightSource(dest + (animationOffset + Point(16,16)) * scaleFactor, scaleFactor, light); + } } void Creature::internalDrawOutfit(Point dest, float scaleFactor, bool animateWalk, bool animateIdle, Otc::Direction direction, LightView *lightView) diff --git a/src/otclient/lightview.cpp b/src/otclient/lightview.cpp index c1fcc15c..4cdaf393 100644 --- a/src/otclient/lightview.cpp +++ b/src/otclient/lightview.cpp @@ -36,29 +36,25 @@ LightView::LightView() void LightView::generateLightBuble() { - const int size = 256; - uint8_t dest[size*size*4]; + m_lightRadius = 128; + int circleDiameter = m_lightRadius * 2; + ImagePtr lightImage = ImagePtr(new Image(Size(circleDiameter, circleDiameter))); - for(int x = 0; x < size; x++){ - for(int y = 0; y < size; y++){ - int norm = std::sqrt((size/2 - x)*(size/2 - x) + (size/2 - y)*(size/2 - y)); - float p = 128*norm/(size/2); + for(int x = 0; x < circleDiameter; x++) { + for(int y = 0; y < circleDiameter; y++) { + float radius = std::sqrt((m_lightRadius - x)*(m_lightRadius - x) + (m_lightRadius - y)*(m_lightRadius - y)); + float intensity = std::max((m_lightRadius-radius)/(float)m_lightRadius, 0.0f); - float color = (p <= 128 ? 255 * (128-p)*(128-p)/(128*128) : 0); - if(color < 0) - color = 0; + // light intensity varies inversely with the square of the distance + intensity = intensity * intensity; + uint8_t colorByte = intensity * 255; - int line = y; - - dest[4*x + 4*size*line] = (uint8_t)color; - dest[4*x + 4*size*line + 1] = (uint8_t)color; - dest[4*x + 4*size*line + 2] = (uint8_t)color; - dest[4*x + 4*size*line + 3] = 255; + uint8_t pixel[4] = {colorByte,colorByte,colorByte,255}; + lightImage->setPixel(x, y, pixel); } } - ImagePtr light = ImagePtr(new Image(Size(size,size), 4, dest)); - m_lightTexture = TexturePtr(new Texture(light, false)); + m_lightTexture = TexturePtr(new Texture(lightImage, true)); m_lightTexture->setSmooth(true); } @@ -74,48 +70,66 @@ void LightView::setGlobalLight(const Light& light) void LightView::addLightSource(const Point& center, float scaleFactor, const Light& light) { - int radius = light.intensity * 64 * scaleFactor; + int radius = light.intensity * m_lightRadius * scaleFactor * 0.25f; + + Color color = Color::from8bit(light.color); + float brightness = 0.8f + (light.intensity/8.0f)*0.2f; + + color.setRed(color.rF() * brightness); + color.setGreen(color.gF() * brightness); + color.setBlue(color.bF() * brightness); LightSource source; source.center = center; - source.color = Color::from8bit(light.color); + source.color = color; source.radius = radius; m_lightMap.push_back(source); } -void LightView::drawGlobalLight(const Light& light, const Size& size) +void LightView::drawGlobalLight(const Light& light) { Color color = Color::from8bit(light.color); - float factor = light.intensity / 256.0f; - color.setRed(color.rF() * factor); - color.setGreen(color.gF() * factor); - color.setBlue(color.bF() * factor); - color.setAlpha(1.0f); + float brightness = light.intensity / 256.0f; + color.setRed(color.rF() * brightness); + color.setGreen(color.gF() * brightness); + color.setBlue(color.bF() * brightness); g_painter->setColor(color); - g_painter->drawFilledRect(Rect(0,0,size)); + g_painter->drawFilledRect(Rect(0,0,m_lightbuffer->getSize())); } void LightView::drawLightSource(const Point& center, const Color& color, int radius) { Rect dest = Rect(center - Point(radius, radius), Size(radius*2,radius*2)); g_painter->setColor(color); + g_painter->setCompositionMode(Painter::CompositionMode_Add); g_painter->drawTexturedRect(dest, m_lightTexture); // debug draw - //g_painter->drawBoundingRect(dest); + /* + radius = 8; + g_painter->setColor(Color::black); + g_painter->setCompositionMode(Painter::CompositionMode_Replace); + g_painter->drawBoundingRect(Rect(center - Point(radius, radius), Size(radius*2,radius*2)), 4); + */ } -void LightView::draw(Size size) +void LightView::resize(const Size& size) { m_lightbuffer->resize(size); +} + +void LightView::draw(const Rect& dest, const Rect& src) +{ + g_painter->saveAndResetState(); m_lightbuffer->bind(); - drawGlobalLight(m_globalLight, size); + g_painter->setCompositionMode(Painter::CompositionMode_Replace); + drawGlobalLight(m_globalLight); g_painter->setCompositionMode(Painter::CompositionMode_Add); for(const LightSource& source : m_lightMap) drawLightSource(source.center, source.color, source.radius); m_lightbuffer->release(); g_painter->setCompositionMode(Painter::CompositionMode_Light); - m_lightbuffer->draw(); - g_painter->setCompositionMode(Painter::CompositionMode_Normal); + m_lightbuffer->draw(dest, src); + g_painter->restoreSavedState(); } diff --git a/src/otclient/lightview.h b/src/otclient/lightview.h index a6ff856f..959552b5 100644 --- a/src/otclient/lightview.h +++ b/src/otclient/lightview.h @@ -41,14 +41,16 @@ public: void reset(); void setGlobalLight(const Light& light); void addLightSource(const Point& center, float scaleFactor, const Light& light); - void draw(Size size); + void resize(const Size& size); + void draw(const Rect& dest, const Rect& src); private: - void drawGlobalLight(const Light& light, const Size& size); + void drawGlobalLight(const Light& light); void drawLightSource(const Point& center, const Color& color, int radius); void generateLightBuble(); TexturePtr m_lightTexture; + int m_lightRadius; FrameBufferPtr m_lightbuffer; MapView* m_mapView; Light m_globalLight; diff --git a/src/otclient/mapview.cpp b/src/otclient/mapview.cpp index 9eebd78d..e20daffd 100644 --- a/src/otclient/mapview.cpp +++ b/src/otclient/mapview.cpp @@ -100,13 +100,14 @@ void MapView::draw(const Rect& rect) if(m_drawLights) { m_lightView->reset(); + m_lightView->resize(m_framebuffer->getSize()); if(cameraPosition.z <= 7) m_lightView->setGlobalLight(g_map.getLight()); else { Light undergroundLight; - undergroundLight.color = 0; - undergroundLight.intensity = 0; + undergroundLight.color = 215; + undergroundLight.intensity = 16; m_lightView->setGlobalLight(undergroundLight); } } @@ -144,9 +145,6 @@ void MapView::draw(const Rect& rect) } } - if(m_drawLights && m_updateTilesPos == 0) - m_lightView->draw(m_framebuffer->getSize()); - m_framebuffer->release(); // generating mipmaps each frame can be slow in older cards @@ -208,7 +206,13 @@ void MapView::draw(const Rect& rect) creature->drawInformation(p, g_map.isCovered(pos, m_cachedFirstVisibleFloor), rect); } + } + // lights are drawn after names and before texts + if(m_drawLights) + m_lightView->draw(rect, srcRect); + + if(m_viewMode == NEAR_VIEW && m_drawTexts) { for(const StaticTextPtr& staticText : g_map.getStaticTexts()) { Position pos = staticText->getPosition(); diff --git a/src/otclient/thingtype.h b/src/otclient/thingtype.h index 1c19e711..25cc18b0 100644 --- a/src/otclient/thingtype.h +++ b/src/otclient/thingtype.h @@ -96,6 +96,7 @@ struct MarketData { }; struct Light { + Light() { intensity = 0; color = 215; } uint8 intensity; uint8 color; }; diff --git a/src/otclient/tile.cpp b/src/otclient/tile.cpp index 943ab587..fe5dc8f5 100644 --- a/src/otclient/tile.cpp +++ b/src/otclient/tile.cpp @@ -28,6 +28,7 @@ #include "localplayer.h" #include "effect.h" #include "protocolgame.h" +#include "lightview.h" #include Tile::Tile(const Position& position) : @@ -92,7 +93,7 @@ void Tile::draw(const Point& dest, float scaleFactor, int drawFlags, LightView * continue; const TilePtr& tile = g_map.getTile(m_position.translated(x,y)); if(tile) - tile->draw(dest + Point(x*Otc::TILE_PIXELS, y*Otc::TILE_PIXELS)*scaleFactor, scaleFactor, topRedrawFlags, lightView); + tile->draw(dest + Point(x*Otc::TILE_PIXELS, y*Otc::TILE_PIXELS)*scaleFactor, scaleFactor, topRedrawFlags); } } } @@ -133,6 +134,13 @@ void Tile::draw(const Point& dest, float scaleFactor, int drawFlags, LightView * } } } + + // draw translucent light (for tiles beneath holes) + if(hasTranslucentLight() && lightView) { + Light light; + light.intensity = 2; + lightView->addLightSource(dest + Point(16,16) * scaleFactor, scaleFactor, light); + } } void Tile::clean() @@ -213,6 +221,9 @@ void Tile::addThing(const ThingPtr& thing, int stackPos) thing->setPosition(m_position); thing->onAppear(); + + if(thing->isTranslucent()) + checkTranslucentLight(); } bool Tile::removeThing(ThingPtr thing) @@ -239,6 +250,9 @@ bool Tile::removeThing(ThingPtr thing) thing->onDisappear(); + if(thing->isTranslucent()) + checkTranslucentLight(); + return removed; } @@ -567,7 +581,7 @@ bool Tile::limitsFloorsView() bool Tile::canErase() { - return m_walkingCreatures.empty() && m_effects.empty() && m_things.empty(); + return m_walkingCreatures.empty() && m_effects.empty() && m_things.empty() && m_flags == 0; } bool Tile::hasElevation(int elevation) @@ -578,3 +592,30 @@ bool Tile::hasElevation(int elevation) count++; return count >= elevation; } + +void Tile::checkTranslucentLight() +{ + if(m_position.z < Otc::SEA_FLOOR) + return; + + Position downPos = m_position; + if(!downPos.down()) + return; + + TilePtr tile = g_map.getOrCreateTile(downPos); + if(!tile) + return; + + bool translucent = false; + for(const ThingPtr& thing : m_things) { + if(thing->isTranslucent() || thing->hasLensHelp()) { + translucent = true; + break; + } + } + + if(translucent) + tile->m_flags = tile->m_flags | TILESTATE_TRANSLUECENT_LIGHT; + else + tile->m_flags = tile->m_flags & ~TILESTATE_TRANSLUECENT_LIGHT; +} diff --git a/src/otclient/tile.h b/src/otclient/tile.h index 4a755231..fd6b2c39 100644 --- a/src/otclient/tile.h +++ b/src/otclient/tile.h @@ -47,7 +47,8 @@ enum tileflags_t TILESTATE_MAILBOX = 1 << 19, TILESTATE_TRASHHOLDER = 1 << 20, TILESTATE_BED = 1 << 21, - TILESTATE_DEPOT = 1 << 22 + TILESTATE_DEPOT = 1 << 22, + TILESTATE_TRANSLUECENT_LIGHT = 1 << 23 }; #pragma pack(push,1) // disable memory alignment @@ -101,6 +102,7 @@ public: bool isClickable(); bool isEmpty(); bool isDrawable(); + bool hasTranslucentLight() { return m_flags & TILESTATE_TRANSLUECENT_LIGHT; } bool mustHookSouth(); bool mustHookEast(); bool hasCreature(); @@ -118,6 +120,8 @@ public: TilePtr asTile() { return static_self_cast(); } private: + void checkTranslucentLight(); + stdext::packed_vector m_walkingCreatures; stdext::packed_vector m_effects; // leave this outside m_things because it has no stackpos. stdext::packed_vector m_things;