parent
4276bd680d
commit
9db7bd2602
@ -0,0 +1,321 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2012 OTClient <https://github.com/edubart/otclient>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "mapview.h"
|
||||
|
||||
#include <framework/graphics/graphics.h>
|
||||
#include <framework/graphics/framebuffer.h>
|
||||
#include <framework/graphics/paintershaderprogram.h>
|
||||
#include <framework/graphics/paintershadersources.h>
|
||||
#include "creature.h"
|
||||
#include "map.h"
|
||||
#include "tile.h"
|
||||
#include "statictext.h"
|
||||
#include "animatedtext.h"
|
||||
|
||||
MapView::MapView()
|
||||
{
|
||||
int frameBufferSize = std::min(g_graphics.getMaxTextureSize(), (int)DEFAULT_FRAMBUFFER_SIZE);
|
||||
|
||||
m_framebuffer = FrameBufferPtr(new FrameBuffer(Size(frameBufferSize, frameBufferSize)));
|
||||
m_framebuffer->setClearColor(Fw::black);
|
||||
m_lockedFirstVisibleFloor = -1;
|
||||
setVisibleDimension(Size(15, 11));
|
||||
|
||||
m_shaderProgram = PainterShaderProgramPtr(new PainterShaderProgram);
|
||||
m_shaderProgram->addShaderFromSourceCode(Shader::Vertex, glslMainWithTexCoordsVertexShader + glslPositionOnlyVertexShader);
|
||||
m_shaderProgram->addShaderFromSourceFile(Shader::Fragment, "/game_shaders/map.frag");
|
||||
assert(m_shaderProgram->link());
|
||||
}
|
||||
|
||||
void MapView::draw(const Rect& rect)
|
||||
{
|
||||
// update visible tiles cache when needed
|
||||
bool updated = updateVisibleTilesCache();
|
||||
|
||||
float scaleFactor = m_tileSize/(float)Otc::TILE_PIXELS;
|
||||
|
||||
if(updated || m_animated) {
|
||||
m_framebuffer->bind();
|
||||
for(const TilePtr& tile : m_cachedVisibleTiles) {
|
||||
tile->draw(transformPositionTo2D(tile->getPosition()), scaleFactor);
|
||||
//TODO: restore missiles
|
||||
}
|
||||
m_framebuffer->generateMipmaps();
|
||||
|
||||
m_framebuffer->release();
|
||||
}
|
||||
|
||||
g_painter.setCustomProgram(m_shaderProgram);
|
||||
g_painter.setColor(Fw::white);
|
||||
|
||||
Point drawOffset(m_tileSize, m_tileSize);
|
||||
if(m_followingCreature)
|
||||
drawOffset += m_followingCreature->getWalkOffset() * scaleFactor;
|
||||
Rect srcRect = Rect(drawOffset, m_visibleDimension * m_tileSize);
|
||||
m_framebuffer->draw(rect, srcRect);
|
||||
|
||||
g_painter.releaseCustomProgram();
|
||||
|
||||
float horizontalStretchFactor = rect.width() / (float)(m_visibleDimension.width() * m_tileSize);
|
||||
float verticalStretchFactor = rect.height() / (float)(m_visibleDimension.height() * m_tileSize);
|
||||
Size tileStretchedSize = Size(m_tileSize * horizontalStretchFactor, m_tileSize * verticalStretchFactor);
|
||||
|
||||
// avoid drawing texts on map in far zoom outs
|
||||
if(tileStretchedSize.width() >= 24) {
|
||||
for(const CreaturePtr& creature : m_cachedFloorVisibleCreatures) {
|
||||
const TilePtr& tile = creature->getCurrentTile();
|
||||
Position pos = tile->getPosition();
|
||||
|
||||
Point p = transformPositionTo2D(pos) - drawOffset;
|
||||
p += (creature->getWalkOffset()-tile->getDrawElevation() + Point(8, -8)) * scaleFactor;
|
||||
p.x = p.x * horizontalStretchFactor;
|
||||
p.y = p.y * verticalStretchFactor;
|
||||
p += rect.topLeft();
|
||||
|
||||
creature->drawInformation(p, g_map.isCovered(pos, m_cachedFirstVisibleFloor), rect);
|
||||
}
|
||||
|
||||
for(const StaticTextPtr& staticText : g_map.getStaticTexts()) {
|
||||
Position pos = staticText->getPosition();
|
||||
|
||||
// ony draw static texts from current camera floor, unless yells
|
||||
if(pos.z != getCameraPosition().z && !staticText->isYell())
|
||||
continue;
|
||||
|
||||
Point p = transformPositionTo2D(pos) - drawOffset;
|
||||
p.x = p.x * horizontalStretchFactor;
|
||||
p.y = p.y * verticalStretchFactor;
|
||||
p += rect.topLeft();
|
||||
staticText->draw(p, rect);
|
||||
}
|
||||
|
||||
for(const AnimatedTextPtr& animatedText : g_map.getAnimatedTexts()) {
|
||||
Position pos = animatedText->getPosition();
|
||||
|
||||
// only draw animated texts from visible floors
|
||||
if(pos.z < m_cachedFirstVisibleFloor || pos.z > m_cachedLastVisibleFloor)
|
||||
continue;
|
||||
|
||||
Point p = transformPositionTo2D(pos) - drawOffset;
|
||||
p.x = p.x * horizontalStretchFactor;
|
||||
p.y = p.y * verticalStretchFactor;
|
||||
p += rect.topLeft();
|
||||
animatedText->draw(p, rect);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool MapView::updateVisibleTilesCache()
|
||||
{
|
||||
// update only when needed
|
||||
if(!m_mustUpdateVisibleTilesCache)
|
||||
return false;
|
||||
|
||||
int firstFloor = getFirstVisibleFloor();
|
||||
int lastFloor = getLastVisibleFloor();
|
||||
m_cachedFirstVisibleFloor = firstFloor;
|
||||
m_cachedLastVisibleFloor = lastFloor;
|
||||
Position cameraPosition = getCameraPosition();
|
||||
|
||||
// clear current visible tiles cache
|
||||
m_cachedVisibleTiles.clear();
|
||||
|
||||
// cache visible tiles in draw order
|
||||
// draw from last floor (the lower) to first floor (the higher)
|
||||
for(int iz = m_cachedLastVisibleFloor; iz >= m_cachedFirstVisibleFloor; --iz) {
|
||||
// draw tiles like linus pauling's rule order
|
||||
const int numDiagonals = m_drawDimension.width() + m_drawDimension.height() - 1;
|
||||
for(int diagonal = 0; diagonal < numDiagonals; ++diagonal) {
|
||||
// loop through / diagonal tiles
|
||||
for(int ix = std::min(diagonal, m_drawDimension.width() - 1), iy = std::max(diagonal - m_drawDimension.width() + 1, 0); ix >= 0 && iy < m_drawDimension.height(); --ix, ++iy) {
|
||||
// position on current floor
|
||||
Position tilePos(cameraPosition.x + (ix - m_virtualCenterOffset.x), cameraPosition.y + (iy - m_virtualCenterOffset.y), cameraPosition.z);
|
||||
// adjust tilePos to the wanted floor
|
||||
tilePos.coveredUp(cameraPosition.z - iz);
|
||||
if(TilePtr tile = g_map.getTile(tilePos)) {
|
||||
// skip tiles that have nothing
|
||||
if(tile->getThingCount() == 0)
|
||||
continue;
|
||||
// skip tiles that are completely behind another tile
|
||||
if(g_map.isCompletelyCovered(tilePos, firstFloor))
|
||||
continue;
|
||||
m_cachedVisibleTiles.push_back(tile);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_cachedFloorVisibleCreatures = g_map.getSpectators(cameraPosition, false);
|
||||
|
||||
m_mustUpdateVisibleTilesCache = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void MapView::recalculateTileSize()
|
||||
{
|
||||
int possiblesTileSizes[] = {32,16,8,4,2,1};
|
||||
|
||||
int foundSize = 0;
|
||||
for(int candidateTileSize : possiblesTileSizes) {
|
||||
Size candidateFramebufferSize = m_drawDimension * candidateTileSize;
|
||||
|
||||
// found a valid size
|
||||
if(candidateFramebufferSize.width() <= m_framebuffer->getSize().width() && candidateFramebufferSize.height() <= m_framebuffer->getSize().height()) {
|
||||
foundSize = candidateTileSize;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
assert(foundSize > 0);
|
||||
m_tileSize = foundSize;
|
||||
}
|
||||
|
||||
void MapView::onTileUpdate(const Position& pos)
|
||||
{
|
||||
requestVisibleTilesCacheUpdate();
|
||||
}
|
||||
|
||||
void MapView::lockFirstVisibleFloor(int firstVisibleFloor)
|
||||
{
|
||||
m_lockedFirstVisibleFloor = firstVisibleFloor;
|
||||
requestVisibleTilesCacheUpdate();
|
||||
}
|
||||
|
||||
void MapView::unlockFirstVisibleFloor()
|
||||
{
|
||||
m_lockedFirstVisibleFloor = -1;
|
||||
requestVisibleTilesCacheUpdate();
|
||||
}
|
||||
|
||||
void MapView::followCreature(const CreaturePtr& creature)
|
||||
{
|
||||
m_followingCreature = creature;
|
||||
m_customCameraPosition = Position();
|
||||
requestVisibleTilesCacheUpdate();
|
||||
}
|
||||
|
||||
void MapView::setCameraPosition(const Position& pos)
|
||||
{
|
||||
m_customCameraPosition = pos;
|
||||
m_followingCreature = nullptr;
|
||||
requestVisibleTilesCacheUpdate();
|
||||
}
|
||||
|
||||
void MapView::setVisibleDimension(const Size& visibleDimension)
|
||||
{
|
||||
if(visibleDimension.width() % 2 != 1 || visibleDimension.height() % 2 != 1) {
|
||||
logTraceError("visible dimension must be odd");
|
||||
return;
|
||||
}
|
||||
|
||||
if(visibleDimension.width() <= 3 || visibleDimension.height() <= 3) {
|
||||
logTraceError("cannot render less than 3x3 tiles");
|
||||
return;
|
||||
}
|
||||
|
||||
m_visibleDimension = visibleDimension;
|
||||
m_drawDimension = visibleDimension + Size(3,3);
|
||||
m_virtualCenterOffset = (m_drawDimension/2 - Size(1,1)).toPoint();
|
||||
recalculateTileSize();
|
||||
|
||||
dump << m_framebuffer->getSize();
|
||||
dump << visibleDimension * m_tileSize;
|
||||
requestVisibleTilesCacheUpdate();
|
||||
}
|
||||
|
||||
int MapView::getFirstVisibleFloor()
|
||||
{
|
||||
// return forced first visible floor
|
||||
if(m_lockedFirstVisibleFloor != -1)
|
||||
return m_lockedFirstVisibleFloor;
|
||||
|
||||
// if nothing is limiting the view, the first visible floor is 0
|
||||
int firstFloor = 0;
|
||||
Position cameraPosition = getCameraPosition();
|
||||
|
||||
// limits to underground floors while under sea level
|
||||
if(cameraPosition.z > Otc::SEA_LEVEL)
|
||||
firstFloor = Otc::SEA_LEVEL+1;
|
||||
|
||||
// loop in 3x3 tiles around the camera
|
||||
for(int ix = -1; ix <= 1 && firstFloor < cameraPosition.z; ++ix) {
|
||||
for(int iy = -1; iy <= 1 && firstFloor < cameraPosition.z; ++iy) {
|
||||
Position pos(cameraPosition.x + ix, cameraPosition.y + iy, cameraPosition.z);
|
||||
|
||||
// process tiles that we can look through, e.g. windows, doors
|
||||
if((ix == 0 && iy == 0) || g_map.isLookPossible(pos)) {
|
||||
Position upperPos = pos;
|
||||
Position coveredPos = pos;
|
||||
|
||||
coveredPos.coveredUp();
|
||||
upperPos.up();
|
||||
|
||||
while(upperPos.z >= firstFloor) {
|
||||
// check tiles physically above
|
||||
TilePtr tile = g_map.getTile(upperPos);
|
||||
if(tile && tile->limitsFloorsView()) {
|
||||
firstFloor = upperPos.z + 1;
|
||||
break;
|
||||
}
|
||||
|
||||
// check tiles geometrically above
|
||||
tile = g_map.getTile(coveredPos);
|
||||
if(tile && tile->limitsFloorsView()) {
|
||||
firstFloor = coveredPos.z + 1;
|
||||
break;
|
||||
}
|
||||
|
||||
coveredPos.coveredUp();
|
||||
upperPos.up();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return firstFloor;
|
||||
}
|
||||
|
||||
int MapView::getLastVisibleFloor()
|
||||
{
|
||||
Position cameraPosition = getCameraPosition();
|
||||
|
||||
// view only underground floors when below sea level
|
||||
if(cameraPosition.z > Otc::SEA_LEVEL)
|
||||
return Otc::MAX_Z;
|
||||
else
|
||||
return Otc::SEA_LEVEL;
|
||||
}
|
||||
|
||||
Position MapView::getCameraPosition()
|
||||
{
|
||||
if(m_followingCreature)
|
||||
return m_followingCreature->getPosition();
|
||||
return m_customCameraPosition;
|
||||
}
|
||||
|
||||
Point MapView::transformPositionTo2D(const Position& position)
|
||||
{
|
||||
Position cameraPosition = getCameraPosition();
|
||||
return Point((m_virtualCenterOffset.x + (position.x - cameraPosition.x) - (cameraPosition.z - position.z)) * m_tileSize,
|
||||
(m_virtualCenterOffset.y + (position.y - cameraPosition.y) - (cameraPosition.z - position.z)) * m_tileSize);
|
||||
}
|
@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2012 OTClient <https://github.com/edubart/otclient>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef MAPVIEW_H
|
||||
#define MAPVIEW_H
|
||||
|
||||
#include "declarations.h"
|
||||
#include <framework/graphics/declarations.h>
|
||||
#include <framework/luascript/luaobject.h>
|
||||
|
||||
class MapView : public LuaObject
|
||||
{
|
||||
enum {
|
||||
DEFAULT_FRAMBUFFER_SIZE = 2048
|
||||
};
|
||||
|
||||
public:
|
||||
MapView();
|
||||
|
||||
void draw(const Rect& rect);
|
||||
|
||||
private:
|
||||
void recalculateTileSize();
|
||||
bool updateVisibleTilesCache();
|
||||
void requestVisibleTilesCacheUpdate() { m_mustUpdateVisibleTilesCache = true; }
|
||||
|
||||
protected:
|
||||
void onTileUpdate(const Position& pos);
|
||||
|
||||
friend class Map;
|
||||
|
||||
public:
|
||||
void lockFirstVisibleFloor(int firstVisibleFloor);
|
||||
void unlockFirstVisibleFloor();
|
||||
void followCreature(const CreaturePtr& creature);
|
||||
|
||||
void setCameraPosition(const Position& pos);
|
||||
void setVisibleDimension(const Size& visibleDimension);
|
||||
void setAnimated(bool animated) { m_animated = animated; }
|
||||
|
||||
//void zoomIn(float factor);
|
||||
//void zoomOut(float factor);
|
||||
|
||||
bool isFollowingCreature() { return !!m_followingCreature; }
|
||||
|
||||
int getFirstVisibleFloor();
|
||||
int getLastVisibleFloor();
|
||||
Position getCameraPosition();
|
||||
Size getVisibleDimension() { return m_visibleDimension; }
|
||||
Size getVisibleSize() { return m_visibleDimension * m_tileSize; }
|
||||
CreaturePtr getFollowingCreature() { return m_followingCreature; }
|
||||
|
||||
bool isAnimated() { return m_animated; }
|
||||
|
||||
Point transformPositionTo2D(const Position& position);
|
||||
|
||||
MapViewPtr asMapView() { return std::static_pointer_cast<MapView>(shared_from_this()); }
|
||||
|
||||
private:
|
||||
int m_drawFlags;
|
||||
int m_lockedFirstVisibleFloor;
|
||||
int m_cachedFirstVisibleFloor;
|
||||
int m_cachedLastVisibleFloor;
|
||||
int m_tileSize;
|
||||
Size m_drawDimension;
|
||||
Size m_visibleDimension;
|
||||
Point m_virtualCenterOffset;
|
||||
Position m_customCameraPosition;
|
||||
Boolean<true> m_mustUpdateVisibleTilesCache;
|
||||
Boolean<true> m_animated;
|
||||
std::vector<TilePtr> m_cachedVisibleTiles;
|
||||
std::vector<CreaturePtr> m_cachedFloorVisibleCreatures;
|
||||
CreaturePtr m_followingCreature;
|
||||
FrameBufferPtr m_framebuffer;
|
||||
PainterShaderProgramPtr m_shaderProgram;
|
||||
};
|
||||
|
||||
#endif
|
Loading…
Reference in new issue