diff --git a/assets/opensb/rendering/effects/world.config b/assets/opensb/rendering/effects/world.config index 188b727..68b84fa 100644 --- a/assets/opensb/rendering/effects/world.config +++ b/assets/opensb/rendering/effects/world.config @@ -30,11 +30,6 @@ "textureSizeUniform" : "lightMapSize", "textureAddressing" : "clamp", "textureFiltering" : "linear" - }, - "tileLightMap" : { - "textureUniform" : "tileLightMap", - "textureAddressing" : "clamp", - "textureFiltering" : "linear" } }, diff --git a/assets/opensb/rendering/effects/world.frag b/assets/opensb/rendering/effects/world.frag index b4450ad..10bd1ad 100644 --- a/assets/opensb/rendering/effects/world.frag +++ b/assets/opensb/rendering/effects/world.frag @@ -6,9 +6,7 @@ uniform sampler2D texture2; uniform sampler2D texture3; uniform bool lightMapEnabled; uniform vec2 lightMapSize; -uniform vec2 tileLightMapSize; uniform sampler2D lightMap; -uniform sampler2D tileLightMap; uniform float lightMapMultiplier; varying vec2 fragmentTextureCoordinate; @@ -55,16 +53,6 @@ vec4 bicubicSample(sampler2D texture, vec2 texcoord, vec2 texscale) { mix(sample1, sample0, sx), sy); } -vec3 sampleLightMap(vec2 texcoord, vec2 texscale) { - vec4 b = bicubicSample(tileLightMap, texcoord, texscale); - vec4 a = bicubicSample(lightMap, texcoord, texscale); - - if (b.z <= 0.0) - return a.rgb; - - return mix(a.rgb, b.rgb / b.z, b.z); -} - void main() { vec4 texColor; if (fragmentTextureIndex > 2.9) { @@ -84,6 +72,6 @@ void main() { if (texColor.a == 0.99607843137) finalColor.a = fragmentColor.a; else if (lightMapEnabled && finalLightMapMultiplier > 0.0) - finalColor.rgb *= sampleLightMap(fragmentLightMapCoordinate, 1.0 / lightMapSize) * finalLightMapMultiplier; + finalColor.rgb *= bicubicSample(lightMap, fragmentLightMapCoordinate, 1.0 / lightMapSize).rgb * finalLightMapMultiplier; gl_FragColor = finalColor; } \ No newline at end of file diff --git a/source/base/StarCellularLighting.cpp b/source/base/StarCellularLighting.cpp index e4fe8dd..c0a92ea 100644 --- a/source/base/StarCellularLighting.cpp +++ b/source/base/StarCellularLighting.cpp @@ -89,12 +89,17 @@ void CellularLightingCalculator::calculate(Image& output) { output.reset(arrayMax[0] - arrayMin[0], arrayMax[1] - arrayMin[1], PixelFormat::RGB24); - for (size_t x = arrayMin[0]; x < arrayMax[0]; ++x) { - for (size_t y = arrayMin[1]; y < arrayMax[1]; ++y) { - if (m_monochrome) + if (m_monochrome) { + for (size_t x = arrayMin[0]; x < arrayMax[0]; ++x) { + for (size_t y = arrayMin[1]; y < arrayMax[1]; ++y) { output.set24(x - arrayMin[0], y - arrayMin[1], Color::grayf(m_lightArray.right().getLight(x, y)).toRgb()); - else + } + } + } else { + for (size_t x = arrayMin[0]; x < arrayMax[0]; ++x) { + for (size_t y = arrayMin[1]; y < arrayMax[1]; ++y) { output.set24(x - arrayMin[0], y - arrayMin[1], Color::v3fToByte(m_lightArray.left().getLight(x, y))); + } } } } diff --git a/source/client/StarClientApplication.cpp b/source/client/StarClientApplication.cpp index 6df7916..54c78b3 100644 --- a/source/client/StarClientApplication.cpp +++ b/source/client/StarClientApplication.cpp @@ -426,7 +426,14 @@ void ClientApplication::render() { LogMap::set("client_render_world_client", strf(u8"{:05d}\u00b5s", Time::monotonicMicroseconds() - clientStart)); auto paintStart = Time::monotonicMicroseconds(); - m_worldPainter->render(m_renderData, [&]() { worldClient->waitForLighting(&m_renderData.lightMap); }); + m_worldPainter->render(m_renderData, [&]() -> bool { + if (auto newMinPosition = worldClient->waitForLighting(&m_renderData.lightMap)) { + m_renderData.lightMinPosition = *newMinPosition; + return true; + } else { + return false; + } + }); LogMap::set("client_render_world_painter", strf(u8"{:05d}\u00b5s", Time::monotonicMicroseconds() - paintStart)); LogMap::set("client_render_world_total", strf(u8"{:05d}\u00b5s", Time::monotonicMicroseconds() - totalStart)); } diff --git a/source/game/StarWorldClient.cpp b/source/game/StarWorldClient.cpp index 489cc31..5e6dc0e 100644 --- a/source/game/StarWorldClient.cpp +++ b/source/game/StarWorldClient.cpp @@ -428,7 +428,7 @@ void WorldClient::render(WorldRenderData& renderData, unsigned bufferTiles) { } List renderLightSources; - List previewTiles; + m_previewTiles.clear(); renderData.geometry = m_geometry; @@ -444,43 +444,20 @@ void WorldClient::render(WorldRenderData& renderData, unsigned bufferTiles) { RectI window = m_clientState.window(); RectI tileRange = window.padded(bufferTiles); - RectI lightRange = window.padded(1); - //Kae: Padded by one to fix light spread issues at the edges of the frame. - renderData.tileMinPosition = tileRange.min(); - renderData.lightMinPosition = lightRange.min(); - Vec2U lightSize(lightRange.size()); - - renderData.tileLightMap.reset(lightSize, PixelFormat::RGBA32); - renderData.tileLightMap.fill(Vec4B::filled(0)); - - if (m_fullBright) { - renderData.lightMap.reset(lightSize, PixelFormat::RGB24); - renderData.lightMap.fill(Vec3B(255, 255, 255)); - } else { - m_lightingCalculator.begin(lightRange); - - if (!m_asyncLighting) - lightingTileGather(); - - for (auto const& light : renderLightSources) { - Vec2F position = m_geometry.nearestTo(Vec2F(m_lightingCalculator.calculationRegion().min()), light.position); - if (light.pointLight) - m_lightingCalculator.addPointLight(position, Color::v3bToFloat(light.color), light.pointBeam, light.beamAngle, light.beamAmbience); - else - m_lightingCalculator.addSpreadLight(position, Color::v3bToFloat(light.color)); - } - - for (auto const& lightPair : m_particles->lightSources()) { - Vec2F position = m_geometry.nearestTo(Vec2F(m_lightingCalculator.calculationRegion().min()), lightPair.first); - m_lightingCalculator.addSpreadLight(position, Color::v3bToFloat(lightPair.second)); - } + if (!m_fullBright) { + { + MutexLocker m_prepLocker(m_lightMapPrepMutex); + m_pendingLights = std::move(renderLightSources); + m_pendingParticleLights = std::move(m_particles->lightSources()); + m_pendingLightRange = window.padded(1); + } //Kae: Padded by one to fix light spread issues at the edges of the frame. if (m_asyncLighting) m_lightingCond.signal(); else - m_lightingCalculator.calculate(m_lightMap); + lightingCalc(); } float pulseAmount = Root::singleton().assets()->json("/highlights.config:interactivePulseAmount").toFloat(); @@ -545,7 +522,7 @@ void WorldClient::render(WorldRenderData& renderData, unsigned bufferTiles) { m_particles->addParticles(std::move(renderCallback.particles)); m_samples.appendAll(std::move(renderCallback.audios)); - previewTiles.appendAll(std::move(renderCallback.previewTiles)); + m_previewTiles.appendAll(std::move(renderCallback.previewTiles)); renderData.overheadBars.appendAll(std::move(renderCallback.overheadBars)); }, [](EntityPtr const& a, EntityPtr const& b) { @@ -596,7 +573,7 @@ void WorldClient::render(WorldRenderData& renderData, unsigned bufferTiles) { } } - for (auto const& previewTile : previewTiles) { + for (auto const& previewTile : m_previewTiles) { Vec2I tileArrayPos = m_geometry.diff(previewTile.position, renderData.tileMinPosition); if (tileArrayPos[0] >= 0 && tileArrayPos[0] < (int)renderData.tiles.size(0) && tileArrayPos[1] >= 0 && tileArrayPos[1] < (int)renderData.tiles.size(1)) { RenderTile& renderTile = renderData.tiles(tileArrayPos[0], tileArrayPos[1]); @@ -621,12 +598,6 @@ void WorldClient::render(WorldRenderData& renderData, unsigned bufferTiles) { renderTile.liquidLevel = 255; } } - - if (previewTile.updateLight) { - Vec2I lightArrayPos = m_geometry.diff(previewTile.position, renderData.lightMinPosition); - if (lightArrayPos[0] >= 0 && lightArrayPos[0] < (int)renderData.tileLightMap.width() && lightArrayPos[1] >= 0 && lightArrayPos[1] < (int)renderData.tileLightMap.height()) - renderData.tileLightMap.set(Vec2U(lightArrayPos), previewTile.light); - } } renderData.particles = &m_particles->particles(); @@ -1090,8 +1061,6 @@ void WorldClient::update(float dt) { auto assets = Root::singleton().assets(); - m_lightingCalculator.setMonochrome(Root::singleton().configuration()->get("monochromeLighting").toBool()); - float expireTime = min(float(m_latency + 800), 2000.f); auto now = Time::monotonicMilliseconds(); eraseWhere(m_predictedTiles, [&](auto& pair) { @@ -1406,10 +1375,21 @@ void WorldClient::collectLiquid(List const& tilePositions, LiquidId liqui m_outgoingPackets.append(make_shared(tilePositions, liquidId)); } -void WorldClient::waitForLighting(Image* out) { - MutexLocker lock(m_lightMapMutex); - if (out) +Maybe WorldClient::waitForLighting(Image* out) { + MutexLocker prepLocker(m_lightMapPrepMutex); + MutexLocker lightMapLocker(m_lightMapMutex); + if (out && !m_lightMap.empty()) { + for (auto& previewTile : m_previewTiles) { + if (previewTile.updateLight) { + Vec2I lightArrayPos = m_geometry.diff(previewTile.position, m_lightMinPosition); + if (lightArrayPos[0] >= 0 && lightArrayPos[0] < (int)m_lightMap.width() && lightArrayPos[1] >= 0 && lightArrayPos[1] < (int)m_lightMap.height()) + m_lightMap.set(Vec2U(lightArrayPos), previewTile.light); + } + } *out = std::move(m_lightMap); + return m_lightMinPosition; + } + return {}; } WorldClient::BroadcastCallback& WorldClient::broadcastCallback() { @@ -1629,11 +1609,11 @@ void WorldClient::lightingTileGather() { // Each column in tileEvalColumns is guaranteed to be no larger than the sector size. + size_t lights = 0; m_tileArray->tileEvalColumns(m_lightingCalculator.calculationRegion(), [&](Vec2I const& pos, ClientTile const* column, size_t ySize) { size_t baseIndex = m_lightingCalculator.baseIndexFor(pos); for (size_t y = 0; y < ySize; ++y) { auto& tile = column[y]; - Vec3F light; if (tile.foreground != EmptyMaterialId || tile.foregroundMod != NoModId) light += materialDatabase->radiantLight(tile.foreground, tile.foregroundMod); @@ -1646,9 +1626,43 @@ void WorldClient::lightingTileGather() { if (tile.backgroundLightTransparent && pos[1] + y > undergroundLevel) light += environmentLight; } - m_lightingCalculator.setCellIndex(baseIndex + y, std::move(light), !tile.foregroundLightTransparent); + if (light.max() > 0.0f) + ++lights; + m_lightingCalculator.setCellIndex(baseIndex + y, light, !tile.foregroundLightTransparent); } }); + LogMap::set("client_render_world_async_light_tiles", toString(lights)); +} + +void WorldClient::lightingCalc() { + MutexLocker prepLocker(m_lightMapPrepMutex); + RectI lightRange = m_pendingLightRange; + List lights = std::move(m_pendingLights); + List> particleLights = std::move(m_pendingParticleLights); + m_lightingCalculator.setMonochrome(Root::singleton().configuration()->get("monochromeLighting").toBool()); + m_lightingCalculator.begin(lightRange); + lightingTileGather(); + prepLocker.unlock(); + + for (auto const& light : lights) { + Vec2F position = m_geometry.nearestTo(Vec2F(m_lightingCalculator.calculationRegion().min()), light.position); + if (light.pointLight) + m_lightingCalculator.addPointLight(position, Color::v3bToFloat(light.color), light.pointBeam, light.beamAngle, light.beamAmbience); + else + m_lightingCalculator.addSpreadLight(position, Color::v3bToFloat(light.color)); + } + + for (auto const& lightPair : particleLights) { + Vec2F position = m_geometry.nearestTo(Vec2F(m_lightingCalculator.calculationRegion().min()), lightPair.first); + m_lightingCalculator.addSpreadLight(position, Color::v3bToFloat(lightPair.second)); + } + + m_lightingCalculator.calculate(m_pendingLightMap); + { + MutexLocker mapLocker(m_lightMapMutex); + m_lightMinPosition = lightRange.min(); + m_lightMap = std::move(m_pendingLightMap); + } } void WorldClient::lightingMain() { @@ -1658,11 +1672,8 @@ void WorldClient::lightingMain() { if (m_stopLightingThread) return; - MutexLocker mapLocker(m_lightMapMutex); int64_t start = Time::monotonicMicroseconds(); - lightingTileGather(); - m_lightingCalculator.calculate(m_lightMap); - mapLocker.unlock(); + lightingCalc(); LogMap::set("client_render_world_async_light_calc", strf(u8"{:05d}\u00b5s", Time::monotonicMicroseconds() - start)); } } diff --git a/source/game/StarWorldClient.hpp b/source/game/StarWorldClient.hpp index 3fe66eb..6ccd52e 100644 --- a/source/game/StarWorldClient.hpp +++ b/source/game/StarWorldClient.hpp @@ -170,7 +170,7 @@ public: void collectLiquid(List const& tilePositions, LiquidId liquidId); - void waitForLighting(Image* out = nullptr); + Maybe waitForLighting(Image* out = nullptr); typedef std::function BroadcastCallback; BroadcastCallback& broadcastCallback(); @@ -210,6 +210,7 @@ private: typedef function ClientTileGetter; void lightingTileGather(); + void lightingCalc(); void lightingMain(); void initWorld(WorldStartPacket const& packet); @@ -272,10 +273,19 @@ private: Mutex m_lightingMutex; ConditionVariable m_lightingCond; - Mutex m_lightMapMutex; - Image m_lightMap; atomic m_stopLightingThread; + Mutex m_lightMapPrepMutex; + Mutex m_lightMapMutex; + + Image m_pendingLightMap; + Image m_lightMap; + List m_pendingLights; + List> m_pendingParticleLights; + RectI m_pendingLightRange; + Vec2I m_lightMinPosition; + List m_previewTiles; + SkyPtr m_sky; CollisionGenerator m_collisionGenerator; diff --git a/source/game/StarWorldRenderData.hpp b/source/game/StarWorldRenderData.hpp index 96fec29..b392615 100644 --- a/source/game/StarWorldRenderData.hpp +++ b/source/game/StarWorldRenderData.hpp @@ -26,7 +26,6 @@ struct WorldRenderData { RenderTileArray tiles; Vec2I lightMinPosition; Image lightMap; - Image tileLightMap; List entityDrawables; List const* particles; diff --git a/source/rendering/StarWorldPainter.cpp b/source/rendering/StarWorldPainter.cpp index 15fb9b9..c55f372 100644 --- a/source/rendering/StarWorldPainter.cpp +++ b/source/rendering/StarWorldPainter.cpp @@ -49,7 +49,7 @@ void WorldPainter::update(float dt) { m_environmentPainter->update(dt); } -void WorldPainter::render(WorldRenderData& renderData, function lightWaiter) { +void WorldPainter::render(WorldRenderData& renderData, function lightWaiter) { m_camera.setScreenSize(m_renderer->screenSize()); m_camera.setTargetPixelRatio(Root::singleton().configuration()->get("zoomLevel").toFloat()); @@ -76,23 +76,24 @@ void WorldPainter::render(WorldRenderData& renderData, function lightWai m_renderer->flush(); + bool lightMapUpdated = false; if (lightWaiter) { auto start = Time::monotonicMicroseconds(); - lightWaiter(); + lightMapUpdated = lightWaiter(); LogMap::set("client_render_world_async_light_wait", strf(u8"{:05d}\u00b5s", Time::monotonicMicroseconds() - start)); } if (renderData.isFullbright) { m_renderer->setEffectTexture("lightMap", Image::filled(Vec2U(1, 1), { 255, 255, 255, 255 }, PixelFormat::RGB24)); - m_renderer->setEffectTexture("tileLightMap", Image::filled(Vec2U(1, 1), { 0, 0, 0, 0 }, PixelFormat::RGBA32)); m_renderer->setEffectParameter("lightMapMultiplier", 1.0f); } else { - adjustLighting(renderData); + if (lightMapUpdated) { + adjustLighting(renderData); + m_renderer->setEffectTexture("lightMap", renderData.lightMap); + } m_renderer->setEffectParameter("lightMapMultiplier", m_assets->json("/rendering.config:lightMapMultiplier").toFloat()); m_renderer->setEffectParameter("lightMapScale", Vec2F::filled(TilePixels * m_camera.pixelRatio())); m_renderer->setEffectParameter("lightMapOffset", m_camera.worldToScreen(Vec2F(renderData.lightMinPosition))); - m_renderer->setEffectTexture("lightMap", renderData.lightMap); - m_renderer->setEffectTexture("tileLightMap", renderData.tileLightMap); } // Parallax layers diff --git a/source/rendering/StarWorldPainter.hpp b/source/rendering/StarWorldPainter.hpp index 43ce83c..6cf53e2 100644 --- a/source/rendering/StarWorldPainter.hpp +++ b/source/rendering/StarWorldPainter.hpp @@ -23,7 +23,7 @@ public: WorldCamera& camera(); void update(float dt); - void render(WorldRenderData& renderData, function lightWaiter); + void render(WorldRenderData& renderData, function lightWaiter); void adjustLighting(WorldRenderData& renderData); private: