#include "lightmap.h" #include "sharedstate.h" #include "bitmap.h" #include "etc.h" #include "etc-internal.h" #include "util.h" #include "gl-util.h" #include "quad.h" #include "transform.h" #include "shader.h" #include "glstate.h" #include "quadarray.h" #include "config.h" #include "debugwriter.h" #include "sunshine.h" #include #include #include #include struct LightMapPrivate{ Bitmap *bitmap; Bitmap *wallMap; int cameraX, cameraY, tilemapOffsetX, tilemapOffsetY; float ambient; std::vector staticLightSources; std::vector dynamicLightSources; std::vector gpuBuffer; Quad quad; Transform trans; Rect *srcRect; sigc::connection srcRectCon; IntRect sceneRect; Vec2i sceneOrig; /* Would this sprite be visible on * the screen if drawn? */ bool isVisible; EtcTemps tmp; sigc::connection prepareCon; LightMapPrivate() : bitmap(new Bitmap(shState->graphics().width(), shState->graphics().height())), wallMap(0), cameraX(0), cameraY(0), tilemapOffsetX(0), tilemapOffsetY(0), ambient(255.0), srcRect(&tmp.rect), isVisible(false) { sceneRect.x = sceneRect.y = 0; updateSrcRectCon(); prepareCon = shState->prepareDraw.connect(sigc::mem_fun(this, &LightMapPrivate::prepare)); bitmap->ensureNonMega(); //bitmap->fillRect(0, 0, shState->graphics().width(), shState->graphics().height(), Vec4(0, 0, 0, 1)); staticLightSources.reserve(48); dynamicLightSources.reserve(16); gpuBuffer.reserve(64); } ~LightMapPrivate(){ srcRectCon.disconnect(); prepareCon.disconnect(); } void onSrcRectChange(){ FloatRect rect = srcRect->toFloatRect(); Vec2i bmSize; if (!nullOrDisposed(bitmap)) bmSize = Vec2i(bitmap->width(), bitmap->height()); /* Clamp the rectangle so it doesn't reach outside * the bitmap bounds */ rect.w = clamp(rect.w, 0, bmSize.x-rect.x); rect.h = clamp(rect.h, 0, bmSize.y-rect.y); quad.setTexRect(rect); quad.setPosRect(FloatRect(0, 0, rect.w, rect.h)); } void updateSrcRectCon(){ /* Cut old connection */ srcRectCon.disconnect(); /* Create new one */ srcRectCon = srcRect->valueChanged.connect(sigc::mem_fun(this, &LightMapPrivate::onSrcRectChange)); } void updateVisibility(){ isVisible = false; if (nullOrDisposed(bitmap)) return; /* Compare sprite bounding box against the scene */ /* If sprite is zoomed/rotated, just opt out for now * for simplicity's sake */ const Vec2 &scale = trans.getScale(); if (scale.x != 1 || scale.y != 1 || trans.getRotation() != 0){ isVisible = true; return; } IntRect self; self.setPos(trans.getPositionI() - (trans.getOriginI() + sceneOrig)); self.w = bitmap->width(); self.h = bitmap->height(); isVisible = SDL_HasRectIntersection(&self, &sceneRect); } void prepare(){ updateVisibility(); } }; LightMap::LightMap(Viewport *viewport) : ViewportElement(viewport){ p = new LightMapPrivate; onGeometryChange(scene->getGeometry()); if (nullOrDisposed(p->bitmap)) return; p->bitmap->ensureNonMega(); *p->srcRect = p->bitmap->rect(); p->onSrcRectChange(); p->quad.setPosRect(p->srcRect->toFloatRect()); } LightMap::~LightMap(){ dispose(); } DEF_ATTR_RD_SIMPLE(LightMap, WallMap, Bitmap*, p->wallMap) DEF_ATTR_SIMPLE(LightMap, CameraX, int, p->cameraX) DEF_ATTR_SIMPLE(LightMap, CameraY, int, p->cameraY) DEF_ATTR_SIMPLE(LightMap, TilemapOffsetX, int, p->tilemapOffsetX) DEF_ATTR_SIMPLE(LightMap, TilemapOffsetY, int, p->tilemapOffsetY) DEF_ATTR_SIMPLE(LightMap, Ambient, float, p->ambient); void LightMap::setWallMap(Bitmap *bitmap){ guardDisposed(); if (p->wallMap == bitmap) return; p->wallMap = bitmap; if (nullOrDisposed(bitmap)) return; bitmap->ensureNonMega(); } void LightMap::initDynAttribs(){ p->srcRect = new Rect; p->updateSrcRectCon(); } /* Flashable */ void LightMap::update(){ guardDisposed(); Flashable::update(); } void LightMap::clearStaticLightSources(){ p->staticLightSources.clear(); } void LightMap::clearDynamicLightSources(){ p->dynamicLightSources.clear(); } void LightMap::addStaticLightSource(LightSource source){ p->staticLightSources.push_back(source); } void LightMap::addDynamicLightSource(LightSource source){ p->dynamicLightSources.push_back(source); } void LightMap::removeStaticLightSource(float x, float y){ for (int i = 0; i < p->staticLightSources.size(); i++) if (p->staticLightSources[i].x == x && p->staticLightSources[i].y == y){ p->staticLightSources.erase(p->staticLightSources.begin() + i); return; } } /* SceneElement */ void LightMap::draw(){ if (!p->isVisible) return; if (emptyFlashFlag) return; DynamicLightShader &shader = shState->shaders().dynamicLight; shader.bind(); shader.applyViewportProj(); if (p->wallMap) shader.setWallMapTexture(p->wallMap->getGLTypes().tex); shader.setWallMapResolution(p->wallMap->width(), p->wallMap->height()); shader.setCameraPosition(p->cameraX, p->cameraY); shader.setTileMapOffset(p->tilemapOffsetX, p->tilemapOffsetY); p->gpuBuffer.clear(); p->gpuBuffer.insert(p->gpuBuffer.end(), p->staticLightSources.begin(), p->staticLightSources.end()); p->gpuBuffer.insert(p->gpuBuffer.end(), p->dynamicLightSources.begin(), p->dynamicLightSources.end()); shader.setLightSources(p->gpuBuffer); shader.setAmbient(p->ambient); boost::chrono::high_resolution_clock::time_point currentTime = boost::chrono::high_resolution_clock::now(); boost::chrono::duration elapsed = currentTime - startTime; shader.setTime(elapsed.count()); glState.blendMode.pushSet(BlendMultiply); p->bitmap->bindTex(shader); p->quad.draw(); glState.blendMode.pop(); } void LightMap::onGeometryChange(const Scene::Geometry &geo){ /* Offset at which the sprite will be drawn * relative to screen origin */ p->trans.setGlobalOffset(geo.offset()); p->sceneRect.setSize(geo.rect.size()); p->sceneOrig = geo.orig; } void LightMap::releaseResources(){ unlink(); delete p; }