Merge branch 'master' into outdoors-style
Conflicts: bin/style.js src/map/map.cpp
This commit is contained in:
16
Makefile
16
Makefile
@@ -7,10 +7,12 @@ V ?= 1
|
||||
all: llmr
|
||||
|
||||
# Builds the regular library
|
||||
llmr: config.gypi llmr.gyp
|
||||
llmr: config.gypi llmr.gyp node
|
||||
deps/run_gyp llmr.gyp --depth=. -Goutput_dir=.. --generator-output=./build/llmr -f make
|
||||
make -C build/llmr V=$(V) llmr-x86
|
||||
|
||||
node:
|
||||
@if [ ! `which node` ]; then echo 'error: depends on node.js. please make sure node is on your PATH'; exit 1; fi;
|
||||
|
||||
##### Test cases ###############################################################
|
||||
|
||||
@@ -40,7 +42,7 @@ run-headless-test: build/test/Makefile
|
||||
|
||||
|
||||
# Builds the linux app with make. This is also used by Travis CI
|
||||
linux: config.gypi linux/llmr-app.gyp
|
||||
linux: config.gypi linux/llmr-app.gyp node
|
||||
deps/run_gyp linux/llmr-app.gyp --depth=. -Goutput_dir=.. --generator-output=./build/linux -f make
|
||||
make -C build/linux V=$(V) linuxapp
|
||||
|
||||
@@ -52,17 +54,17 @@ run-linux: linux
|
||||
##### Xcode projects ###########################################################
|
||||
|
||||
# build Mac OS X project for Xcode
|
||||
xproj: config.gypi macosx/llmr-app.gyp
|
||||
xproj: config.gypi macosx/llmr-app.gyp node
|
||||
deps/run_gyp macosx/llmr-app.gyp --depth=. --generator-output=./build -f xcode
|
||||
open ./build/macosx/llmr-app.xcodeproj
|
||||
|
||||
# build iOS project for Xcode
|
||||
iproj: config.gypi ios/llmr-app.gyp
|
||||
iproj: config.gypi ios/llmr-app.gyp node
|
||||
deps/run_gyp ios/llmr-app.gyp --depth=. --generator-output=./build -f xcode
|
||||
open ./build/ios/llmr-app.xcodeproj
|
||||
|
||||
# build Linux project for Xcode (Runs on Mac OS X too, but without platform-specific code)
|
||||
lproj: config.gypi linux/llmr-app.gyp
|
||||
lproj: config.gypi linux/llmr-app.gyp node
|
||||
deps/run_gyp linux/llmr-app.gyp --depth=. --generator-output=./build -f xcode
|
||||
open ./build/linux/llmr-app.xcodeproj
|
||||
|
||||
@@ -72,6 +74,10 @@ lproj: config.gypi linux/llmr-app.gyp
|
||||
clean:
|
||||
-rm -rf ./build/Release
|
||||
-rm -rf ./build/Debug
|
||||
-rm -f include/llmr/shader/shaders.hpp
|
||||
-rm -f include/llmr/style/resources.hpp
|
||||
-rm -f src/style/resources.cpp
|
||||
-rm -rf ~/Library/Developer/Xcode/DerivedData/llmr*
|
||||
|
||||
distclean: clean
|
||||
-rm -rf ./build
|
||||
|
||||
@@ -39,6 +39,8 @@ Because `libpng` isn't included in the iOS SDK, you will need to build a cross-a
|
||||
yourself. Run `./setup-libraries.sh`, which is derived from Mapnik's cross-architecture build
|
||||
scripts. This will also run `./configure`.
|
||||
|
||||
Then, `make iproj` to create and open an Xcode project with an iOS-specific view controller housing and basic gesture support.
|
||||
|
||||
Target devices: iPhone 4 and above (4S, 5, 5c, 5s) and iPad 2 and above (3, 4, mini and/or retina).
|
||||
|
||||
## Ubuntu
|
||||
|
||||
@@ -2,6 +2,10 @@
|
||||
|
||||
module.exports = {
|
||||
"buckets": {
|
||||
"satellite": {
|
||||
"source": "satellite",
|
||||
"type": "raster"
|
||||
},
|
||||
"admin_maritime": {
|
||||
"source": "outdoors",
|
||||
"layer": "admin",
|
||||
|
||||
@@ -94,6 +94,7 @@ public:
|
||||
inline GlyphAtlas &getGlyphAtlas() { return glyphAtlas; }
|
||||
inline uv_loop_t *getLoop() { return loop; }
|
||||
inline time getAnimationTime() const { return animationTime; }
|
||||
inline Texturepool &getTexturepool() { return texturepool; }
|
||||
|
||||
private:
|
||||
// uv async callbacks
|
||||
@@ -106,13 +107,20 @@ private:
|
||||
void loadStyle(const uint8_t *const data, uint32_t bytes);
|
||||
|
||||
void updateTiles();
|
||||
void updateClippingIDs();
|
||||
|
||||
size_t countLayers(const std::vector<LayerDescription>& layers);
|
||||
|
||||
// Prepares a map render by updating the tiles we need for the current view, as well as updating
|
||||
// the stylesheet.
|
||||
void prepare();
|
||||
|
||||
enum RenderPass { Opaque, Translucent };
|
||||
|
||||
// Unconditionally performs a render with the current map state.
|
||||
void render();
|
||||
void renderLayers(const std::vector<LayerDescription>& layers, RenderPass pass);
|
||||
void renderLayer(const LayerDescription& layer_desc, RenderPass pass);
|
||||
|
||||
private:
|
||||
// If cleared, the next time the render thread attempts to render the map, it will *actually*
|
||||
@@ -141,6 +149,8 @@ private:
|
||||
|
||||
bool debug = false;
|
||||
time animationTime = 0;
|
||||
float strata_thickness = 0.0f;
|
||||
int strata = 0;
|
||||
|
||||
private:
|
||||
bool async = false;
|
||||
|
||||
28
include/llmr/map/raster_tile_data.hpp
Normal file
28
include/llmr/map/raster_tile_data.hpp
Normal file
@@ -0,0 +1,28 @@
|
||||
#ifndef LLMR_MAP_RASTER_TILE_DATA
|
||||
#define LLMR_MAP_RASTER_TILE_DATA
|
||||
|
||||
#include <llmr/map/tile_data.hpp>
|
||||
|
||||
#include <llmr/renderer/raster_bucket.hpp>
|
||||
|
||||
|
||||
namespace llmr {
|
||||
|
||||
class RasterTileData : public TileData {
|
||||
friend class TileParser;
|
||||
|
||||
public:
|
||||
RasterTileData(Tile::ID id, Map &map, const std::string url);
|
||||
~RasterTileData();
|
||||
|
||||
virtual void parse();
|
||||
virtual void render(Painter &painter, const LayerDescription& layer_desc);
|
||||
virtual bool hasData(const LayerDescription& layer_desc) const;
|
||||
|
||||
protected:
|
||||
RasterBucket bucket;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -6,10 +6,13 @@
|
||||
#include <llmr/util/noncopyable.hpp>
|
||||
#include <llmr/util/time.hpp>
|
||||
|
||||
#include <list>
|
||||
#include <forward_list>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <set>
|
||||
|
||||
namespace llmr {
|
||||
|
||||
@@ -26,17 +29,19 @@ public:
|
||||
};
|
||||
|
||||
public:
|
||||
Source(Map &map, Painter &painter, Texturepool &texturepool,
|
||||
Source(Map &map, Painter &painter,
|
||||
const char *url = "", Type type = Type::vector, std::vector<uint32_t> zooms = {0},
|
||||
uint32_t tile_size = 512, uint32_t min_zoom = 0, uint32_t max_zoom = 14,
|
||||
bool enabled = true);
|
||||
|
||||
|
||||
|
||||
bool update();
|
||||
void prepare_render(const TransformState &transform, bool is_baselayer = false);
|
||||
void render(time animationTime);
|
||||
size_t prepareRender(const TransformState &transform);
|
||||
void render(const LayerDescription& layer_desc, const BucketDescription &bucket_desc);
|
||||
void finishRender();
|
||||
|
||||
std::forward_list<Tile::ID> getIDs() const;
|
||||
void updateClipIDs(const std::map<Tile::ID, ClipID> &mapping);
|
||||
|
||||
public:
|
||||
bool enabled;
|
||||
@@ -56,7 +61,6 @@ private:
|
||||
private:
|
||||
Map& map;
|
||||
Painter& painter;
|
||||
Texturepool& texturepool;
|
||||
|
||||
const Type type;
|
||||
const std::vector<uint32_t> zooms;
|
||||
@@ -65,8 +69,8 @@ private:
|
||||
const int32_t min_zoom;
|
||||
const int32_t max_zoom;
|
||||
|
||||
std::forward_list<Tile> tiles;
|
||||
std::forward_list<std::weak_ptr<TileData>> tile_data;
|
||||
std::map<Tile::ID, std::unique_ptr<Tile>> tiles;
|
||||
std::map<Tile::ID, std::weak_ptr<TileData>> tile_data;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -7,45 +7,64 @@
|
||||
|
||||
#include <cstdint>
|
||||
#include <forward_list>
|
||||
#include <string>
|
||||
#include <bitset>
|
||||
|
||||
namespace llmr {
|
||||
|
||||
class TileData;
|
||||
|
||||
struct ClipID {
|
||||
explicit ClipID() {}
|
||||
explicit ClipID(const std::bitset<8> &mask, uint8_t length) : mask(mask), length(length) {}
|
||||
explicit ClipID(const std::string &mask, uint8_t length) : mask(mask), length(length) {}
|
||||
std::bitset<8> mask;
|
||||
uint8_t length = 0;
|
||||
};
|
||||
|
||||
class Tile : private util::noncopyable {
|
||||
public:
|
||||
struct ID {
|
||||
int8_t z = 0;
|
||||
int32_t x = 0, y = 0;
|
||||
const int16_t w = 0;
|
||||
const int8_t z = 0;
|
||||
const int32_t x = 0, y = 0;
|
||||
|
||||
inline ID(int8_t z, int32_t x, int32_t y)
|
||||
: z(z), x(x), y(y) {
|
||||
inline explicit ID(int8_t z, int32_t x, int32_t y)
|
||||
: w((x < 0 ? x - (1 << z) + 1 : x) / (1 << z)), z(z), x(x), y(y) {}
|
||||
|
||||
inline uint64_t to_uint64() const {
|
||||
return ((pow(2, z) * y + x) * 32) + z;
|
||||
}
|
||||
|
||||
inline operator uint64_t() const {
|
||||
uint32_t dim = 1 << z;
|
||||
return ((dim * y + x) * 32) + z;
|
||||
inline operator std::string() const {
|
||||
return std::to_string(z) + "/" + std::to_string(x) + "/" + std::to_string(y);
|
||||
}
|
||||
|
||||
inline bool operator==(const ID& rhs) const {
|
||||
return z == rhs.z && x == rhs.x && y == rhs.y;
|
||||
return w == rhs.w && z == rhs.z && x == rhs.x && y == rhs.y;
|
||||
}
|
||||
|
||||
inline bool operator<(const ID &rhs) const {
|
||||
if (w != rhs.w) return w < rhs.w;
|
||||
if (z != rhs.z) return z < rhs.z;
|
||||
if (x != rhs.x) return x < rhs.x;
|
||||
return y < rhs.y;
|
||||
}
|
||||
|
||||
ID parent(int8_t z) const;
|
||||
void normalize();
|
||||
ID normalized() const;
|
||||
std::forward_list<ID> children(int32_t z) const;
|
||||
bool isChildOf(const Tile::ID &id) const;
|
||||
};
|
||||
|
||||
public:
|
||||
Tile(const ID& id);
|
||||
explicit Tile(const ID& id);
|
||||
|
||||
public:
|
||||
const Tile::ID id;
|
||||
uint8_t clip_id;
|
||||
ClipID clip;
|
||||
mat4 matrix;
|
||||
std::shared_ptr<TileData> data;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -2,39 +2,21 @@
|
||||
#define LLMR_MAP_TILE_DATA
|
||||
|
||||
#include <llmr/map/tile.hpp>
|
||||
#include <llmr/util/vec.hpp>
|
||||
#include <llmr/util/mat4.hpp>
|
||||
#include <llmr/util/noncopyable.hpp>
|
||||
#include <llmr/geometry/debug_font_buffer.hpp>
|
||||
#include <llmr/geometry/vao.hpp>
|
||||
#include <llmr/map/vector_tile.hpp>
|
||||
#include <llmr/platform/platform.hpp>
|
||||
|
||||
#include <llmr/renderer/bucket.hpp>
|
||||
|
||||
#include <llmr/geometry/vertex_buffer.hpp>
|
||||
#include <llmr/geometry/elements_buffer.hpp>
|
||||
#include <llmr/geometry/fill_buffer.hpp>
|
||||
#include <llmr/geometry/line_buffer.hpp>
|
||||
#include <llmr/geometry/point_buffer.hpp>
|
||||
#include <llmr/geometry/text_buffer.hpp>
|
||||
#include <llmr/geometry/vao.hpp>
|
||||
#include <llmr/renderer/debug_bucket.hpp>
|
||||
|
||||
#include <cstdint>
|
||||
#include <forward_list>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <atomic>
|
||||
|
||||
namespace llmr {
|
||||
|
||||
class Map;
|
||||
class Raster;
|
||||
class Painter;
|
||||
class LayerDescription;
|
||||
class BucketDescription;
|
||||
|
||||
class PlainShader;
|
||||
|
||||
class TileData : public std::enable_shared_from_this<TileData>,
|
||||
private util::noncopyable {
|
||||
@@ -55,47 +37,37 @@ public:
|
||||
};
|
||||
|
||||
public:
|
||||
TileData(Tile::ID id, Map &map, const std::string url, const bool is_raster);
|
||||
TileData(Tile::ID id, Map &map, const std::string url);
|
||||
~TileData();
|
||||
|
||||
void request();
|
||||
void scheduleParsing();
|
||||
void parse();
|
||||
void cancel();
|
||||
|
||||
void reparse();
|
||||
const std::string toString() const;
|
||||
|
||||
// Override this in the child class.
|
||||
virtual void parse() = 0;
|
||||
virtual void render(Painter &painter, const LayerDescription& layer_desc) = 0;
|
||||
virtual bool hasData(const LayerDescription& layer_desc) const = 0;
|
||||
|
||||
|
||||
public:
|
||||
const Tile::ID id;
|
||||
std::atomic<State> state;
|
||||
std::shared_ptr<Raster> raster;
|
||||
|
||||
// Holds the actual geometries in this tile.
|
||||
DebugFontBuffer debugFontBuffer;
|
||||
VertexArrayObject debugFontArray;
|
||||
|
||||
FillVertexBuffer fillVertexBuffer;
|
||||
LineVertexBuffer lineVertexBuffer;
|
||||
PointVertexBuffer pointVertexBuffer;
|
||||
TextVertexBuffer textVertexBuffer;
|
||||
|
||||
TriangleElementsBuffer triangleElementsBuffer;
|
||||
LineElementsBuffer lineElementsBuffer;
|
||||
PointElementsBuffer pointElementsBuffer;
|
||||
|
||||
// Holds the buckets of this tile.
|
||||
// They contain the location offsets in the buffers stored above
|
||||
std::map<std::string, std::unique_ptr<Bucket>> buckets;
|
||||
|
||||
private:
|
||||
const std::string url;
|
||||
const bool is_raster = false;
|
||||
std::string data;
|
||||
|
||||
protected:
|
||||
Map ↦
|
||||
|
||||
// Stores a request that is in progress.
|
||||
// Request-related information.
|
||||
const std::string url;
|
||||
std::weak_ptr<platform::Request> req;
|
||||
std::string data;
|
||||
|
||||
// Contains the tile ID string for painting debug information.
|
||||
DebugFontBuffer debugFontBuffer;
|
||||
|
||||
public:
|
||||
DebugBucket debugBucket;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
#ifndef LLMR_MAP_TILE_PARSER
|
||||
#define LLMR_MAP_TILE_PARSER
|
||||
|
||||
#include <llmr/map/tile_data.hpp>
|
||||
#include <llmr/map/vector_tile_data.hpp>
|
||||
#include <llmr/map/vector_tile.hpp>
|
||||
#include <llmr/text/placement.hpp>
|
||||
|
||||
namespace llmr {
|
||||
@@ -9,9 +10,12 @@ namespace llmr {
|
||||
class Style;
|
||||
class GlyphAtlas;
|
||||
|
||||
class LayerDescription;
|
||||
class Bucket;
|
||||
|
||||
class TileParser {
|
||||
public:
|
||||
TileParser(const std::string& data, TileData& tile, const Style& style, GlyphAtlas& glyphAtlas, bool is_raster = false);
|
||||
TileParser(const std::string& data, VectorTileData& tile, const Style& style, GlyphAtlas& glyphAtlas);
|
||||
|
||||
private:
|
||||
bool obsolete() const;
|
||||
@@ -22,13 +26,11 @@ private:
|
||||
std::unique_ptr<Bucket> createLineBucket(const VectorTileLayer& layer, const BucketDescription& bucket_desc);
|
||||
std::unique_ptr<Bucket> createPointBucket(const VectorTileLayer& layer, const BucketDescription& bucket_desc);
|
||||
std::unique_ptr<Bucket> createTextBucket(const VectorTileLayer& layer, const BucketDescription& bucket_desc);
|
||||
std::unique_ptr<Bucket> createRasterBucket(const BucketDescription& bucket_desc);
|
||||
template <class Bucket> void addBucketFeatures(Bucket& bucket, const VectorTileLayer& layer, const BucketDescription& bucket_desc);
|
||||
|
||||
private:
|
||||
const VectorTile vector_data;
|
||||
const std::string raster_data;
|
||||
TileData& tile;
|
||||
VectorTileData& tile;
|
||||
const Style& style;
|
||||
GlyphAtlas& glyphAtlas;
|
||||
Faces faces;
|
||||
|
||||
51
include/llmr/map/vector_tile_data.hpp
Normal file
51
include/llmr/map/vector_tile_data.hpp
Normal file
@@ -0,0 +1,51 @@
|
||||
#ifndef LLMR_MAP_VECTOR_TILE_DATA
|
||||
#define LLMR_MAP_VECTOR_TILE_DATA
|
||||
|
||||
#include <llmr/map/tile_data.hpp>
|
||||
|
||||
|
||||
#include <llmr/renderer/bucket.hpp>
|
||||
|
||||
#include <llmr/geometry/vertex_buffer.hpp>
|
||||
#include <llmr/geometry/elements_buffer.hpp>
|
||||
#include <llmr/geometry/fill_buffer.hpp>
|
||||
#include <llmr/geometry/line_buffer.hpp>
|
||||
#include <llmr/geometry/point_buffer.hpp>
|
||||
#include <llmr/geometry/text_buffer.hpp>
|
||||
|
||||
#include <map>
|
||||
|
||||
namespace llmr {
|
||||
|
||||
|
||||
class VectorTileData : public TileData {
|
||||
friend class TileParser;
|
||||
|
||||
public:
|
||||
VectorTileData(Tile::ID id, Map &map, const std::string url);
|
||||
~VectorTileData();
|
||||
|
||||
virtual void parse();
|
||||
virtual void render(Painter &painter, const LayerDescription& layer_desc);
|
||||
virtual bool hasData(const LayerDescription& layer_desc) const;
|
||||
|
||||
protected:
|
||||
// Holds the actual geometries in this tile.
|
||||
FillVertexBuffer fillVertexBuffer;
|
||||
LineVertexBuffer lineVertexBuffer;
|
||||
PointVertexBuffer pointVertexBuffer;
|
||||
TextVertexBuffer textVertexBuffer;
|
||||
|
||||
TriangleElementsBuffer triangleElementsBuffer;
|
||||
LineElementsBuffer lineElementsBuffer;
|
||||
PointElementsBuffer pointElementsBuffer;
|
||||
|
||||
// Holds the buckets of this tile.
|
||||
// They contain the location offsets in the buffers stored above
|
||||
std::map<std::string, std::unique_ptr<Bucket>> buckets;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -12,6 +12,7 @@ class Painter;
|
||||
class Bucket : private util::noncopyable {
|
||||
public:
|
||||
virtual void render(Painter& painter, const std::string& layer_name, const Tile::ID& id) = 0;
|
||||
virtual bool hasData() const = 0;
|
||||
virtual ~Bucket() {}
|
||||
};
|
||||
|
||||
|
||||
37
include/llmr/renderer/debug_bucket.hpp
Normal file
37
include/llmr/renderer/debug_bucket.hpp
Normal file
@@ -0,0 +1,37 @@
|
||||
#ifndef LLMR_RENDERER_DEBUGBUCKET
|
||||
#define LLMR_RENDERER_DEBUGBUCKET
|
||||
|
||||
#include <llmr/renderer/bucket.hpp>
|
||||
#include <llmr/style/bucket_description.hpp>
|
||||
#include <llmr/geometry/debug_font_buffer.hpp>
|
||||
#include <llmr/geometry/vao.hpp>
|
||||
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
#ifndef BUFFER_OFFSET
|
||||
#define BUFFER_OFFSET(i) ((char *)nullptr + (i))
|
||||
#endif
|
||||
|
||||
namespace llmr {
|
||||
|
||||
class PlainShader;
|
||||
|
||||
class DebugBucket : public Bucket {
|
||||
public:
|
||||
DebugBucket(DebugFontBuffer& fontBuffer);
|
||||
|
||||
virtual void render(Painter& painter, const std::string& layer_name, const Tile::ID& id);
|
||||
virtual bool hasData() const;
|
||||
|
||||
void drawLines(PlainShader& shader);
|
||||
void drawPoints(PlainShader& shader);
|
||||
|
||||
private:
|
||||
DebugFontBuffer& fontBuffer;
|
||||
VertexArrayObject array;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -45,9 +45,9 @@ public:
|
||||
~FillBucket();
|
||||
|
||||
virtual void render(Painter& painter, const std::string& layer_name, const Tile::ID& id);
|
||||
virtual bool hasData() const;
|
||||
|
||||
void addGeometry(pbf& data);
|
||||
bool empty() const;
|
||||
void tessellate();
|
||||
|
||||
void drawElements(PlainShader& shader);
|
||||
|
||||
@@ -28,11 +28,11 @@ public:
|
||||
const BucketDescription& bucket_desc);
|
||||
|
||||
virtual void render(Painter& painter, const std::string& layer_name, const Tile::ID& id);
|
||||
virtual bool hasData() const;
|
||||
|
||||
void addGeometry(pbf& data);
|
||||
void addGeometry(const std::vector<Coordinate>& line);
|
||||
|
||||
bool empty() const;
|
||||
bool hasPoints() const;
|
||||
|
||||
void drawLines(LineShader& shader);
|
||||
|
||||
@@ -33,6 +33,9 @@ class PointBucket;
|
||||
class TextBucket;
|
||||
class RasterBucket;
|
||||
|
||||
class LayerDescription;
|
||||
class RasterTileData;
|
||||
|
||||
class Painter : private util::noncopyable {
|
||||
public:
|
||||
Painter(Map &map);
|
||||
@@ -40,32 +43,54 @@ public:
|
||||
|
||||
void setup();
|
||||
void clear();
|
||||
|
||||
// Updates the default matrices to the current viewport dimensions.
|
||||
void changeMatrix();
|
||||
void render(const Tile& tile);
|
||||
|
||||
// Renders a particular layer from a tile.
|
||||
void renderTileLayer(const Tile& tile, const LayerDescription &layer_desc);
|
||||
|
||||
// Renders debug information for a tile.
|
||||
void renderTileDebug(const Tile& tile);
|
||||
|
||||
// Renders the backdrop of the OpenGL view. This also paints in areas where we don't have any
|
||||
// tiles whatsoever.
|
||||
void renderMatte();
|
||||
|
||||
// Renders the red debug frame around a tile, visualizing its perimeter.
|
||||
void renderDebugFrame();
|
||||
|
||||
void renderDebugText(DebugBucket& bucket);
|
||||
void renderDebugText(const std::vector<std::string> &strings);
|
||||
void renderFill(FillBucket& bucket, const std::string& layer_name, const Tile::ID& id);
|
||||
void renderLine(LineBucket& bucket, const std::string& layer_name, const Tile::ID& id);
|
||||
void renderPoint(PointBucket& bucket, const std::string& layer_name, const Tile::ID& id);
|
||||
void renderText(TextBucket& bucket, const std::string& layer_name, const Tile::ID& id);
|
||||
void renderRaster(const std::string& layer_name, const std::shared_ptr<TileData>& tile_data);
|
||||
void renderRaster(RasterBucket& bucket, const std::string& layer_name, const Tile::ID& id);
|
||||
|
||||
void resize();
|
||||
|
||||
// Changes whether debug information is drawn onto the map
|
||||
void setDebug(bool enabled);
|
||||
|
||||
// Opaque/Translucent pass setting
|
||||
void startOpaquePass();
|
||||
void startTranslucentPass();
|
||||
void endPass();
|
||||
|
||||
// Configures the painter strata that is used for early z-culling of fragments.
|
||||
void setStrata(float strata);
|
||||
|
||||
void prepareClippingMask();
|
||||
void drawClippingMask(const mat4& matrix, uint8_t clip_id);
|
||||
void drawClippingMask(const mat4& matrix, const ClipID& clip);
|
||||
void finishClippingMask();
|
||||
|
||||
bool needsAnimation() const;
|
||||
private:
|
||||
void setupShaders();
|
||||
void renderLayers(const std::shared_ptr<TileData>& tile, const std::vector<LayerDescription>& layers);
|
||||
void renderLayer(const std::shared_ptr<TileData>& tile_data, const LayerDescription& layer_desc);
|
||||
void translateLayer(std::array<float, 2> translation, bool reverse = false);
|
||||
void renderDebug(const std::shared_ptr<TileData>& tile);
|
||||
|
||||
void prepareTile(const Tile& tile);
|
||||
void useProgram(uint32_t program);
|
||||
void lineWidth(float lineWidth);
|
||||
void depthMask(bool value);
|
||||
@@ -88,8 +113,8 @@ private:
|
||||
bool gl_depthMask = true;
|
||||
std::array<uint16_t, 2> gl_viewport = {{ 0, 0 }};
|
||||
float strata = 0;
|
||||
const float strata_epsilon = 1.0f / (1 << 16);
|
||||
enum { Opaque, Translucent } pass = Opaque;
|
||||
const float strata_epsilon = 1.0f / (1 << 16);
|
||||
|
||||
std::unique_ptr<PlainShader> plainShader;
|
||||
std::unique_ptr<OutlineShader> outlineShader;
|
||||
|
||||
@@ -28,11 +28,10 @@ public:
|
||||
const BucketDescription& bucket_desc);
|
||||
|
||||
virtual void render(Painter& painter, const std::string& layer_name, const Tile::ID& id);
|
||||
virtual bool hasData() const;
|
||||
|
||||
void addGeometry(pbf& data);
|
||||
|
||||
bool hasPoints() const;
|
||||
|
||||
void drawPoints(PointShader& shader);
|
||||
void drawPoints(DotShader& shader);
|
||||
|
||||
|
||||
@@ -3,16 +3,27 @@
|
||||
|
||||
#include <llmr/renderer/bucket.hpp>
|
||||
#include <llmr/style/bucket_description.hpp>
|
||||
#include <llmr/util/raster.hpp>
|
||||
|
||||
namespace llmr {
|
||||
|
||||
class BucketDescription;
|
||||
class RasterShader;
|
||||
class VertexBuffer;
|
||||
class VertexArrayObject;
|
||||
|
||||
class RasterBucket : public Bucket {
|
||||
public:
|
||||
RasterBucket(const BucketDescription& bucket_desc);
|
||||
RasterBucket(Texturepool &texturepool);
|
||||
|
||||
virtual void render(Painter& painter, const std::string& layer_name, const Tile::ID& id);
|
||||
virtual bool hasData() const;
|
||||
|
||||
bool setImage(const std::string &data);
|
||||
|
||||
void drawRaster(RasterShader& shader, VertexBuffer &vertices, VertexArrayObject &array);
|
||||
|
||||
private:
|
||||
Raster raster;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -29,8 +29,8 @@ public:
|
||||
TriangleElementsBuffer &triangleElementsBuffer,
|
||||
const BucketDescription &bucket_desc, Placement &placement);
|
||||
|
||||
virtual void render(Painter &painter, const std::string &layer_name,
|
||||
const Tile::ID &id);
|
||||
virtual void render(Painter &painter, const std::string &layer_name, const Tile::ID &id);
|
||||
virtual bool hasData() const;
|
||||
|
||||
void addGlyphs(const PlacedGlyphs &glyphs, float placementZoom,
|
||||
PlacementRange placementRange, float zoom);
|
||||
@@ -39,8 +39,6 @@ public:
|
||||
const IndexedFaces &faces,
|
||||
const std::map<Value, Shaping> &shapings);
|
||||
|
||||
bool empty() const;
|
||||
|
||||
void drawGlyphs(TextShader &shader);
|
||||
|
||||
public:
|
||||
|
||||
@@ -11,6 +11,8 @@ public:
|
||||
std::string name;
|
||||
std::string bucket_name;
|
||||
std::vector<LayerDescription> child_layer;
|
||||
|
||||
size_t size() const;
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream&, const LayerDescription& layer);
|
||||
|
||||
@@ -127,6 +127,7 @@ struct TextClass {
|
||||
FunctionProperty size = 12.0f;
|
||||
FunctionProperty rotate = 0.0f;
|
||||
FunctionProperty alwaysVisible = false;
|
||||
FunctionProperty opacity = 1;
|
||||
};
|
||||
|
||||
struct TextProperties {
|
||||
@@ -138,6 +139,7 @@ struct TextProperties {
|
||||
float size = 12.0f;
|
||||
float rotate = 0.0f;
|
||||
bool alwaysVisible = false;
|
||||
float opacity = 1.0;
|
||||
};
|
||||
|
||||
struct BackgroundClass {
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <string>
|
||||
#include <mutex>
|
||||
#include <memory>
|
||||
#include <atomic>
|
||||
|
||||
#include <llmr/util/raster.hpp>
|
||||
#include <llmr/util/vec.hpp>
|
||||
@@ -45,7 +46,7 @@ public:
|
||||
|
||||
public:
|
||||
const float pixelRatio;
|
||||
std::shared_ptr<Raster> raster;
|
||||
Raster raster;
|
||||
|
||||
private:
|
||||
void asyncParseJSON();
|
||||
@@ -57,9 +58,9 @@ private:
|
||||
|
||||
private:
|
||||
Map ↦
|
||||
mutable std::mutex mtx;
|
||||
std::string body;
|
||||
bool loaded = false;
|
||||
std::string image;
|
||||
std::atomic<bool> loaded;
|
||||
std::map<std::string, SpritePosition> pos;
|
||||
};
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@ public:
|
||||
|
||||
void loadJSON(const uint8_t *const data, size_t bytes);
|
||||
|
||||
size_t layerCount() const;
|
||||
void cascade(float z);
|
||||
|
||||
public:
|
||||
|
||||
19
include/llmr/util/clip_ids.hpp
Normal file
19
include/llmr/util/clip_ids.hpp
Normal file
@@ -0,0 +1,19 @@
|
||||
#ifndef LLMR_UTIL_CLIP_IDS
|
||||
#define LLMR_UTIL_CLIP_IDS
|
||||
|
||||
#include <llmr/map/tile.hpp>
|
||||
#include <list>
|
||||
#include <set>
|
||||
#include <map>
|
||||
|
||||
namespace llmr {
|
||||
|
||||
static constexpr uint8_t clipMask[9] { 0x00, 0x80, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC, 0xFE, 0xFF };
|
||||
|
||||
void updateClipIDs(const std::list<Tile *> &array);
|
||||
|
||||
std::map<Tile::ID, ClipID> computeClipIDs(std::forward_list<Tile::ID> array);
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -15,13 +15,11 @@ namespace llmr {
|
||||
class Raster : public std::enable_shared_from_this<Raster> {
|
||||
|
||||
public:
|
||||
Raster(Texturepool &texturepool);
|
||||
~Raster();
|
||||
|
||||
// load image data
|
||||
void load();
|
||||
|
||||
// set shared texture pool
|
||||
void setTexturepool(Texturepool* texturepool);
|
||||
bool load(const std::string &img);
|
||||
|
||||
// bind current texture
|
||||
void bind(bool linear = false);
|
||||
@@ -34,8 +32,6 @@ public:
|
||||
bool needsTransition() const;
|
||||
void updateTransitions(time now);
|
||||
|
||||
inline void setData(const std::string &img) { data = img; }
|
||||
|
||||
public:
|
||||
// loaded image dimensions
|
||||
uint32_t width = 0, height = 0;
|
||||
@@ -60,14 +56,11 @@ private:
|
||||
bool loaded = false;
|
||||
|
||||
// shared texture pool
|
||||
Texturepool* texturepool = nullptr;
|
||||
Texturepool &texturepool;
|
||||
|
||||
// min/mag filter
|
||||
uint32_t filter = 0;
|
||||
|
||||
// the encoded image data.
|
||||
std::string data;
|
||||
|
||||
// the raw pixels
|
||||
char *img = nullptr;
|
||||
|
||||
|
||||
@@ -11,6 +11,23 @@ template<typename T, typename ...Args>
|
||||
return ::std::unique_ptr<T>(new T(::std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
|
||||
template <typename Container, typename ForwardIterator, typename Predicate>
|
||||
void erase_if(Container &container, ForwardIterator it, const ForwardIterator end, Predicate pred) {
|
||||
while (it != end) {
|
||||
if (pred(*it)) {
|
||||
container.erase(it++);
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Container, typename Predicate>
|
||||
void erase_if(Container &container, Predicate pred) {
|
||||
erase_if(container, container.begin(), container.end(), pred);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -21,4 +21,15 @@
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)applicationDidEnterBackground:(UIApplication *)application
|
||||
{
|
||||
[(MBXViewController *)self.window.rootViewController saveState];
|
||||
}
|
||||
|
||||
- (void)applicationWillEnterForeground:(UIApplication *)application
|
||||
{
|
||||
[(MBXViewController *)self.window.rootViewController restoreState];
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
@@ -2,4 +2,7 @@
|
||||
|
||||
@interface MBXViewController : UIViewController
|
||||
|
||||
- (void)saveState;
|
||||
- (void)restoreState;
|
||||
|
||||
@end
|
||||
|
||||
@@ -77,11 +77,29 @@ MBXViewController *viewController = nullptr;
|
||||
|
||||
// Load settings
|
||||
settings = new llmr::Settings_NSUserDefaults();
|
||||
map->setLonLatZoom(settings->longitude, settings->latitude, settings->zoom);
|
||||
map->setAngle(settings->angle);
|
||||
map->setDebug(settings->debug);
|
||||
[self restoreState];
|
||||
}
|
||||
|
||||
- (void)saveState
|
||||
{
|
||||
if (map && settings)
|
||||
{
|
||||
map->getLonLatZoom(settings->longitude, settings->latitude, settings->zoom);
|
||||
settings->angle = map->getAngle();
|
||||
settings->debug = map->getDebug();
|
||||
settings->save();
|
||||
}
|
||||
}
|
||||
|
||||
- (void)restoreState
|
||||
{
|
||||
if (map && settings) {
|
||||
settings->load();
|
||||
map->setLonLatZoom(settings->longitude, settings->latitude, settings->zoom);
|
||||
map->setAngle(settings->angle);
|
||||
map->setDebug(settings->debug);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setupInteraction
|
||||
{
|
||||
@@ -237,13 +255,7 @@ MBXViewController *viewController = nullptr;
|
||||
if (settings)
|
||||
{
|
||||
// Save settings
|
||||
if (map)
|
||||
{
|
||||
map->getLonLatZoom(settings->longitude, settings->latitude, settings->zoom);
|
||||
settings->angle = map->getAngle();
|
||||
settings->debug = map->getDebug();
|
||||
settings->save();
|
||||
}
|
||||
[self saveState];
|
||||
delete settings;
|
||||
settings = nullptr;
|
||||
}
|
||||
|
||||
@@ -10,7 +10,9 @@
|
||||
"type": "executable",
|
||||
"sources": [
|
||||
"./main.m",
|
||||
"./MBXAppDelegate.h",
|
||||
"./MBXAppDelegate.m",
|
||||
"./MBXViewController.h",
|
||||
"./MBXViewController.mm",
|
||||
"../common/settings_nsuserdefaults.hpp",
|
||||
"../common/settings_nsuserdefaults.mm",
|
||||
|
||||
7
llmr.gyp
7
llmr.gyp
@@ -7,6 +7,7 @@
|
||||
{
|
||||
'target_name': 'shaders',
|
||||
'type': 'none',
|
||||
'hard_dependency': 1,
|
||||
'actions': [
|
||||
{
|
||||
'action_name': 'Build Shaders',
|
||||
@@ -25,6 +26,7 @@
|
||||
{
|
||||
'target_name': 'build_stylesheet',
|
||||
'type': 'none',
|
||||
'hard_dependency': 1,
|
||||
'actions': [
|
||||
{
|
||||
'action_name': 'Build Stylesheet',
|
||||
@@ -38,6 +40,11 @@
|
||||
'action': ['<@(node)', 'bin/build-style.js']
|
||||
}
|
||||
],
|
||||
'direct_dependent_settings': {
|
||||
'sources': [
|
||||
'src/style/resources.cpp'
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
'target_name': 'llmr-x86',
|
||||
|
||||
29
setup-libraries-cross.sh
Executable file
29
setup-libraries-cross.sh
Executable file
@@ -0,0 +1,29 @@
|
||||
#!/bin/bash
|
||||
set -e -u
|
||||
|
||||
|
||||
export CXX11=true
|
||||
|
||||
export SYSROOT=`pwd`/../rootfs
|
||||
export SDK_PATH=`pwd`/../arm-linux-gnueabi-linaro_4.8.3-2014.04
|
||||
export V=1
|
||||
|
||||
cd mapnik-packaging/osx/
|
||||
|
||||
source Linaro-softfp.sh
|
||||
echo $PATH
|
||||
if [ ! -f out/build-cpp11-libstdcpp-gcc-arm-linux-gnueabi/lib/libpng.a ] ; then ./scripts/build_png.sh ; fi
|
||||
# if [ ! -f out/build-cpp11-libstdcpp-gcc-arm-linux-gnueabi/lib/libglfw3.a ] ; then ./scripts/build_glfw.sh ; fi
|
||||
if [ ! -f out/build-cpp11-libstdcpp-gcc-arm-linux-gnueabi/lib/libuv.a ] ; then ./scripts/build_libuv.sh ; fi
|
||||
if [ ! -f out/build-cpp11-libstdcpp-gcc-arm-linux-gnueabi/lib/libssl.a ] ; then ./scripts/build_openssl.sh ; fi
|
||||
# if [ ! -f out/build-cpp11-libstdcpp-gcc-x86_64/lib/libcurl.a ] ; then ./scripts/build_curl.sh ; fi
|
||||
# if [ ! -d out/build-cpp11-libstdcpp-gcc-arm-linux-gnueabi/include/boost ] ; then ./scripts/build_boost.sh `pwd`/../../src/ `pwd`/../../linux/ `pwd`/../../common/ ; fi
|
||||
|
||||
|
||||
|
||||
# cd ../../
|
||||
# ./configure \
|
||||
# --pkg-config-root=`pwd`/mapnik-packaging/osx/out/build-cpp11-libstdcpp-gcc-x86_64/lib/pkgconfig \
|
||||
# --boost=`pwd`/mapnik-packaging/osx/out/build-cpp11-libstdcpp-gcc-x86_64
|
||||
|
||||
# fi
|
||||
@@ -3,16 +3,36 @@ set -e -u
|
||||
|
||||
UNAME=$(uname -s);
|
||||
|
||||
MISSING_DEPS=""
|
||||
|
||||
function ensure_dep {
|
||||
if [[ ! `which $1` ]]; then
|
||||
MISSING_DEPS="$MISSING_DEPS $1"
|
||||
fi
|
||||
}
|
||||
|
||||
ensure_dep aclocal
|
||||
ensure_dep cmake
|
||||
ensure_dep automake
|
||||
ensure_dep autoconf
|
||||
ensure_dep pkg-config
|
||||
if [ ${UNAME} = 'Darwin' ]; then
|
||||
if [ ! `which aclocal` ] || [ ! `which automake` ] || [ ! `which autoconf` ] || [ ! `which glibtool` ]; then
|
||||
echo 'autotools commands not found: run "brew install autoconf automake libtool" before continuing'
|
||||
exit 1
|
||||
fi
|
||||
ensure_dep makedepend
|
||||
ensure_dep glibtool
|
||||
elif [ ${UNAME} = 'Linux' ]; then
|
||||
if [ ! `which aclocal` ] || [ ! `which automake` ] || [ ! `which autoconf` ] || [ ! `which libtool` ]; then
|
||||
echo 'autotools commands not found: run "sudo apt-get install automake libtool xutils-dev" before continuing'
|
||||
exit 1
|
||||
ensure_dep libtool
|
||||
ensure_dep gccmakedep
|
||||
fi
|
||||
|
||||
if [[ $MISSING_DEPS != "" ]]; then
|
||||
if [ ${UNAME} = 'Darwin' ]; then
|
||||
echo "Missing build deps: ${MISSING_DEPS}"
|
||||
echo 'Please run "brew install autoconf automake libtool makedepend cmake pkg-config" and then re-run ./setup-libraries.sh'
|
||||
elif [ ${UNAME} = 'Linux' ]; then
|
||||
echo "Missing build deps: ${MISSING_DEPS}"
|
||||
echo 'Please run "sudo apt-get install automake libtool xutils-dev cmake pkg-config" and then re-run ./setup-libraries.sh'
|
||||
fi
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -d 'mapnik-packaging/.git' ]; then
|
||||
|
||||
160
src/map/map.cpp
160
src/map/map.cpp
@@ -5,6 +5,9 @@
|
||||
#include <llmr/style/sprite.hpp>
|
||||
#include <llmr/util/transition.hpp>
|
||||
#include <llmr/util/time.hpp>
|
||||
#include <llmr/util/math.hpp>
|
||||
#include <llmr/util/clip_ids.hpp>
|
||||
#include <llmr/util/string.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
@@ -138,7 +141,6 @@ void Map::setup() {
|
||||
sources.emplace("outdoors",
|
||||
std::unique_ptr<Source>(new Source(*this,
|
||||
painter,
|
||||
texturepool,
|
||||
"http://a.gl-api-us-east-1.tilestream.net/v3/mapbox.mapbox-terrain-v1,mapbox.mapbox-streets-v42-dev/%d/%d/%d.gl.pbf",
|
||||
Source::Type::vector,
|
||||
{{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 }},
|
||||
@@ -150,7 +152,6 @@ void Map::setup() {
|
||||
sources.emplace("satellite",
|
||||
std::unique_ptr<Source>(new Source(*this,
|
||||
painter,
|
||||
texturepool,
|
||||
"https://a.tiles.mapbox.com/v3/justin.hh0gkdfm/%d/%d/%d%s.png256",
|
||||
Source::Type::raster,
|
||||
{{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21 }},
|
||||
@@ -331,7 +332,11 @@ void Map::toggleRaster() {
|
||||
|
||||
if (satellite_source.enabled) {
|
||||
satellite_source.enabled = false;
|
||||
style.appliedClasses.erase(style.appliedClasses.find("satellite"));
|
||||
|
||||
auto style_class = style.appliedClasses.find("satellite");
|
||||
if (style_class != style.appliedClasses.end()) {
|
||||
style.appliedClasses.erase(style_class);
|
||||
}
|
||||
} else {
|
||||
satellite_source.enabled = true;
|
||||
style.appliedClasses.insert("satellite");
|
||||
@@ -350,6 +355,28 @@ void Map::updateTiles() {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void Map::updateClippingIDs() {
|
||||
std::forward_list<Tile::ID> ids;
|
||||
|
||||
for (auto &pair : sources) {
|
||||
const std::unique_ptr<Source> &source = pair.second;
|
||||
if (source->enabled) {
|
||||
ids.splice_after(ids.before_begin(), source->getIDs());
|
||||
}
|
||||
}
|
||||
|
||||
const std::map<Tile::ID, ClipID> clipIDs = computeClipIDs(ids);
|
||||
|
||||
for (auto &pair : sources) {
|
||||
const std::unique_ptr<Source> &source = pair.second;
|
||||
if (source->enabled) {
|
||||
source->updateClipIDs(clipIDs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Map::prepare() {
|
||||
view.make_active();
|
||||
|
||||
@@ -366,14 +393,14 @@ void Map::prepare() {
|
||||
style.sprite->load(kSpriteURL);
|
||||
}
|
||||
|
||||
if (style.sprite && style.sprite->raster->isLoaded() && !style.sprite->raster->textured) {
|
||||
style.sprite->raster->setTexturepool(&texturepool);
|
||||
}
|
||||
|
||||
updateTiles();
|
||||
|
||||
updateClippingIDs();
|
||||
}
|
||||
|
||||
void Map::render() {
|
||||
std::vector<std::string> debug;
|
||||
|
||||
painter.clear();
|
||||
|
||||
painter.resize();
|
||||
@@ -386,28 +413,133 @@ void Map::render() {
|
||||
|
||||
painter.prepareClippingMask();
|
||||
|
||||
bool is_baselayer = true;
|
||||
|
||||
size_t source_id = 0;
|
||||
for (auto &pair : sources) {
|
||||
Source &source = *pair.second;
|
||||
source.prepare_render(state, is_baselayer);
|
||||
is_baselayer = source.enabled ? false : (true && is_baselayer);
|
||||
size_t count = source.prepareRender(state);
|
||||
debug.emplace_back(util::sprintf<100>("source %d: %d\n", source_id, count));
|
||||
source_id++;
|
||||
}
|
||||
|
||||
painter.finishClippingMask();
|
||||
|
||||
// Then, render each source's tiles. TODO: handle more than one
|
||||
// vector source.
|
||||
|
||||
|
||||
// Render per layer in the stylesheet.
|
||||
strata_thickness = 1.0f / (style.layerCount() + 1);
|
||||
strata = 0;
|
||||
|
||||
// - FIRST PASS ------------------------------------------------------------
|
||||
// Render everything top-to-bottom by using reverse iterators. Render opaque
|
||||
// objects first.
|
||||
painter.startOpaquePass();
|
||||
renderLayers(style.layers, Opaque);
|
||||
painter.endPass();
|
||||
|
||||
// - SECOND PASS -----------------------------------------------------------
|
||||
// Make a second pass, rendering translucent objects. This time, we render
|
||||
// bottom-to-top.
|
||||
--strata;
|
||||
painter.startTranslucentPass();
|
||||
renderLayers(style.layers, Translucent);
|
||||
painter.endPass();
|
||||
|
||||
|
||||
// Finalize the rendering, e.g. by calling debug render calls per tile.
|
||||
// This guarantees that we have at least one function per tile called.
|
||||
// When only rendering layers via the stylesheet, it's possible that we don't
|
||||
// ever visit a tile during rendering.
|
||||
for (auto &pair : sources) {
|
||||
Source &source = *pair.second;
|
||||
source.render(animationTime);
|
||||
const std::string &name = pair.first;
|
||||
gl::group group(std::string("debug ") + name);
|
||||
source.finishRender();
|
||||
}
|
||||
|
||||
painter.renderMatte();
|
||||
|
||||
painter.renderDebugText(debug);
|
||||
|
||||
// Schedule another rerender when we definitely need a next frame.
|
||||
if (transform.needsTransition()) {
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
void Map::renderLayers(const std::vector<LayerDescription>& layers, RenderPass pass) {
|
||||
if (pass == Opaque) {
|
||||
typedef std::vector<LayerDescription>::const_reverse_iterator riterator;
|
||||
for (riterator it = layers.rbegin(), end = layers.rend(); it != end; ++it, ++strata) {
|
||||
painter.setStrata(strata * strata_thickness);
|
||||
renderLayer(*it, pass);
|
||||
}
|
||||
} else if (pass == Translucent) {
|
||||
typedef std::vector<LayerDescription>::const_iterator iterator;
|
||||
for (iterator it = layers.begin(), end = layers.end(); it != end; ++it, --strata) {
|
||||
painter.setStrata(strata * strata_thickness);
|
||||
renderLayer(*it, pass);
|
||||
}
|
||||
} else {
|
||||
throw std::runtime_error("unknown render pass");
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Styles>
|
||||
bool is_invisible(const Styles &styles, const LayerDescription &layer_desc) {
|
||||
auto it = styles.find(layer_desc.name);
|
||||
if (it == styles.end()) return true;
|
||||
const auto &properties = it->second;
|
||||
if (!properties.enabled) return true;
|
||||
if (properties.opacity <= 0) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
void Map::renderLayer(const LayerDescription& layer_desc, RenderPass pass) {
|
||||
if (layer_desc.child_layer.size()) {
|
||||
gl::group group(std::string("group: ") + layer_desc.name);
|
||||
// This is a layer group.
|
||||
// TODO: create framebuffer
|
||||
renderLayers(layer_desc.child_layer, pass);
|
||||
// TODO: render framebuffer on previous framebuffer
|
||||
} else {
|
||||
// This is a singular layer. Try to find the bucket associated with
|
||||
// this layer and render it.
|
||||
auto bucket_it = style.buckets.find(layer_desc.bucket_name);
|
||||
if (bucket_it != style.buckets.end()) {
|
||||
const BucketDescription &bucket_desc = bucket_it->second;
|
||||
|
||||
// Abort early if we can already deduce from the bucket type that
|
||||
// we're not going to render anything anyway during this pass.
|
||||
switch (bucket_desc.type) {
|
||||
case BucketType::Fill:
|
||||
if (is_invisible(style.computed.fills, layer_desc)) return;
|
||||
break;
|
||||
case BucketType::Line:
|
||||
if (pass == Opaque) return;
|
||||
if (is_invisible(style.computed.lines, layer_desc)) return;
|
||||
break;
|
||||
case BucketType::Point:
|
||||
if (pass == Opaque) return;
|
||||
if (is_invisible(style.computed.points, layer_desc)) return;
|
||||
break;
|
||||
case BucketType::Text:
|
||||
if (pass == Opaque) return;
|
||||
if (is_invisible(style.computed.texts, layer_desc)) return;
|
||||
break;
|
||||
case BucketType::Raster:
|
||||
if (pass == Translucent) return;
|
||||
if (is_invisible(style.computed.lines, layer_desc)) return;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// Find the source and render all tiles in it.
|
||||
auto source_it = sources.find(bucket_desc.source_name);
|
||||
if (source_it != sources.end()) {
|
||||
const std::unique_ptr<Source> &source = source_it->second;
|
||||
source->render(layer_desc, bucket_desc);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
34
src/map/raster_tile_data.cpp
Normal file
34
src/map/raster_tile_data.cpp
Normal file
@@ -0,0 +1,34 @@
|
||||
#include <llmr/map/map.hpp>
|
||||
#include <llmr/map/raster_tile_data.hpp>
|
||||
#include <llmr/style/layer_description.hpp>
|
||||
|
||||
using namespace llmr;
|
||||
|
||||
|
||||
RasterTileData::RasterTileData(Tile::ID id, Map &map, const std::string url)
|
||||
: TileData(id, map, url),
|
||||
bucket(map.getTexturepool()) {
|
||||
}
|
||||
|
||||
RasterTileData::~RasterTileData() {
|
||||
}
|
||||
|
||||
void RasterTileData::parse() {
|
||||
if (state != State::loaded) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (bucket.setImage(data)) {
|
||||
state = State::parsed;
|
||||
} else {
|
||||
state = State::invalid;
|
||||
}
|
||||
}
|
||||
|
||||
void RasterTileData::render(Painter &painter, const LayerDescription& layer_desc) {
|
||||
bucket.render(painter, layer_desc.name, id);
|
||||
}
|
||||
|
||||
bool RasterTileData::hasData(const LayerDescription& layer_desc) const {
|
||||
return bucket.hasData();
|
||||
}
|
||||
@@ -7,17 +7,21 @@
|
||||
#include <llmr/util/string.hpp>
|
||||
#include <llmr/util/texturepool.hpp>
|
||||
#include <llmr/util/vec.hpp>
|
||||
#include <llmr/util/std.hpp>
|
||||
#include <llmr/geometry/glyph_atlas.hpp>
|
||||
|
||||
|
||||
#include <llmr/map/vector_tile_data.hpp>
|
||||
#include <llmr/map/raster_tile_data.hpp>
|
||||
|
||||
using namespace llmr;
|
||||
|
||||
Source::Source(Map &map, Painter &painter, Texturepool &texturepool,
|
||||
Source::Source(Map &map, Painter &painter,
|
||||
const char *url, Source::Type type, std::vector<uint32_t> zooms, uint32_t tile_size,
|
||||
uint32_t min_zoom, uint32_t max_zoom, bool enabled)
|
||||
: enabled(enabled),
|
||||
map(map),
|
||||
painter(painter),
|
||||
texturepool(texturepool),
|
||||
type(type),
|
||||
zooms(zooms),
|
||||
url(url),
|
||||
@@ -29,45 +33,70 @@ bool Source::update() {
|
||||
return updateTiles();
|
||||
}
|
||||
|
||||
void Source::prepare_render(const TransformState &transform, bool is_baselayer) {
|
||||
void Source::updateClipIDs(const std::map<Tile::ID, ClipID> &mapping) {
|
||||
std::for_each(tiles.begin(), tiles.end(), [&mapping](std::pair<const Tile::ID, std::unique_ptr<Tile>> &pair) {
|
||||
Tile &tile = *pair.second;
|
||||
auto it = mapping.find(tile.id);
|
||||
if (it != mapping.end()) {
|
||||
tile.clip = it->second;
|
||||
} else {
|
||||
tile.clip = ClipID {};
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
size_t Source::prepareRender(const TransformState &transform) {
|
||||
if (!enabled) return 0;
|
||||
|
||||
size_t masks = 0;
|
||||
for (std::pair<const Tile::ID, std::unique_ptr<Tile>> &pair : tiles) {
|
||||
Tile &tile = *pair.second;
|
||||
gl::group group(util::sprintf<32>("mask %d/%d/%d", tile.id.z, tile.id.y, tile.id.z));
|
||||
transform.matrixFor(tile.matrix, tile.id);
|
||||
matrix::multiply(tile.matrix, painter.projMatrix, tile.matrix);
|
||||
painter.drawClippingMask(tile.matrix, tile.clip);
|
||||
masks++;
|
||||
}
|
||||
|
||||
return masks;
|
||||
}
|
||||
|
||||
void Source::render(const LayerDescription& layer_desc, const BucketDescription &bucket_desc) {
|
||||
if (!enabled) return;
|
||||
|
||||
uint8_t i = 1;
|
||||
|
||||
for (Tile& tile : tiles) {
|
||||
gl::group group(std::string("layer: ") + layer_desc.name);
|
||||
for (std::pair<const Tile::ID, std::unique_ptr<Tile>> &pair : tiles) {
|
||||
Tile &tile = *pair.second;
|
||||
if (tile.data && tile.data->state == TileData::State::parsed) {
|
||||
transform.matrixFor(tile.matrix, tile.id);
|
||||
matrix::multiply(tile.matrix, painter.projMatrix, tile.matrix);
|
||||
tile.clip_id = i++;
|
||||
if (is_baselayer) {
|
||||
painter.drawClippingMask(tile.matrix, tile.clip_id);
|
||||
}
|
||||
painter.renderTileLayer(tile, layer_desc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Source::render(time animationTime) {
|
||||
void Source::finishRender() {
|
||||
if (!enabled) return;
|
||||
|
||||
for (const Tile& tile : tiles) {
|
||||
if (tile.data && tile.data->state == TileData::State::parsed) {
|
||||
if (type == Type::raster) {
|
||||
std::shared_ptr<Raster> raster = tile.data->raster;
|
||||
if (raster && !raster->textured) {
|
||||
raster->setTexturepool(&texturepool);
|
||||
raster->beginFadeInTransition();
|
||||
}
|
||||
if (raster && raster->needsTransition()) {
|
||||
raster->updateTransitions(animationTime);
|
||||
}
|
||||
}
|
||||
painter.render(tile);
|
||||
}
|
||||
for (std::pair<const Tile::ID, std::unique_ptr<Tile>> &pair : tiles) {
|
||||
Tile &tile = *pair.second;
|
||||
painter.renderTileDebug(tile);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
std::forward_list<Tile::ID> Source::getIDs() const {
|
||||
std::forward_list<Tile::ID> ptrs;
|
||||
|
||||
std::transform(tiles.begin(), tiles.end(), std::front_inserter(ptrs), [](const std::pair<const Tile::ID, std::unique_ptr<Tile>> &pair) {
|
||||
Tile &tile = *pair.second;
|
||||
return tile.id;
|
||||
});
|
||||
return ptrs;
|
||||
}
|
||||
|
||||
TileData::State Source::hasTile(const Tile::ID& id) {
|
||||
for (const Tile& tile : tiles) {
|
||||
auto it = tiles.find(id);
|
||||
if (it != tiles.end()) {
|
||||
Tile &tile = *it->second;
|
||||
if (tile.id == id && tile.data) {
|
||||
return tile.data->state;
|
||||
}
|
||||
@@ -83,20 +112,17 @@ TileData::State Source::addTile(const Tile::ID& id) {
|
||||
return state;
|
||||
}
|
||||
|
||||
tiles.emplace_front(id);
|
||||
Tile& new_tile = tiles.front();
|
||||
auto pos = tiles.emplace(id, std::make_unique<Tile>(id));
|
||||
Tile& new_tile = *pos.first->second;
|
||||
|
||||
// We couldn't find the tile in the list. Create a new one.
|
||||
// Try to find the associated TileData object.
|
||||
const Tile::ID normalized_id = id.normalized();
|
||||
|
||||
auto it = std::find_if(tile_data.begin(), tile_data.end(), [normalized_id](const std::weak_ptr<TileData>& tile_data) {
|
||||
return !tile_data.expired() && tile_data.lock()->id == normalized_id;
|
||||
});
|
||||
|
||||
auto it = tile_data.find(normalized_id);
|
||||
if (it != tile_data.end()) {
|
||||
// Create a shared_ptr handle. Note that this might be empty!
|
||||
new_tile.data = it->lock();
|
||||
new_tile.data = it->second.lock();
|
||||
}
|
||||
|
||||
if (new_tile.data && new_tile.data->state == TileData::State::obsolete) {
|
||||
@@ -111,13 +137,14 @@ TileData::State Source::addTile(const Tile::ID& id) {
|
||||
|
||||
if (type == Source::Type::vector) {
|
||||
formed_url = util::sprintf<256>(url, normalized_id.z, normalized_id.x, normalized_id.y);
|
||||
new_tile.data = std::make_shared<VectorTileData>(normalized_id, map, formed_url);
|
||||
} else {
|
||||
formed_url = util::sprintf<256>(url, normalized_id.z, normalized_id.x, normalized_id.y, (map.getState().getPixelRatio() > 1.0 ? "@2x" : ""));
|
||||
new_tile.data = std::make_shared<RasterTileData>(normalized_id, map, formed_url);
|
||||
}
|
||||
|
||||
new_tile.data = std::make_shared<TileData>(normalized_id, map, formed_url, (type == Source::Type::raster));
|
||||
new_tile.data->request();
|
||||
tile_data.push_front(new_tile.data);
|
||||
tile_data.emplace(new_tile.data->id, new_tile.data);
|
||||
}
|
||||
|
||||
return new_tile.data->state;
|
||||
@@ -230,31 +257,26 @@ bool Source::updateTiles() {
|
||||
|
||||
// Remove tiles that we definitely don't need, i.e. tiles that are not on
|
||||
// the required list.
|
||||
std::forward_list<Tile::ID> retain_data;
|
||||
tiles.remove_if([&retain, &retain_data, &changed](const Tile & tile) {
|
||||
std::set<Tile::ID> retain_data;
|
||||
std::erase_if(tiles, [&retain, &retain_data, &changed](std::pair<const Tile::ID, std::unique_ptr<Tile>> &pair) {
|
||||
Tile &tile = *pair.second;
|
||||
bool obsolete = std::find(retain.begin(), retain.end(), tile.id) == retain.end();
|
||||
if (obsolete) {
|
||||
changed = true;
|
||||
} else {
|
||||
retain_data.push_front(tile.data->id);
|
||||
retain_data.insert(tile.data->id);
|
||||
}
|
||||
return obsolete;
|
||||
});
|
||||
|
||||
// Sort tiles by zoom level, front to back.
|
||||
// We're painting front-to-back, so we want to draw more detailed tiles first
|
||||
// before filling in other parts with lower zoom levels.
|
||||
tiles.sort([](const Tile & a, const Tile & b) {
|
||||
return a.id.z > b.id.z;
|
||||
});
|
||||
|
||||
// Remove all the expired pointers from the list.
|
||||
tile_data.remove_if([&retain_data](const std::weak_ptr<TileData>& tile_data) {
|
||||
const std::shared_ptr<TileData> tile = tile_data.lock();
|
||||
// Remove all the expired pointers from the set.
|
||||
std::erase_if(tile_data, [&retain_data](std::pair<const Tile::ID, std::weak_ptr<TileData>> &pair) {
|
||||
const std::shared_ptr<TileData> tile = pair.second.lock();
|
||||
if (!tile) {
|
||||
return true;
|
||||
}
|
||||
bool obsolete = std::find(retain_data.begin(), retain_data.end(), tile->id) == retain_data.end();
|
||||
|
||||
bool obsolete = retain_data.find(tile->id) == retain_data.end();
|
||||
if (obsolete) {
|
||||
tile->cancel();
|
||||
return true;
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
|
||||
using namespace llmr;
|
||||
|
||||
#include <iostream>
|
||||
|
||||
Tile::Tile(const ID& id)
|
||||
: id(id) {
|
||||
}
|
||||
@@ -31,14 +33,19 @@ std::forward_list<Tile::ID> Tile::ID::children(int32_t child_z) const {
|
||||
return children;
|
||||
}
|
||||
|
||||
void Tile::ID::normalize() {
|
||||
Tile::ID Tile::ID::normalized() const {
|
||||
int32_t dim = pow(2, z);
|
||||
while (x < 0) x += dim;
|
||||
while (x >= dim) x -= dim;
|
||||
int32_t nx = x, ny = y;
|
||||
while (nx < 0) nx += dim;
|
||||
while (nx >= dim) nx -= dim;
|
||||
return ID { z, nx, ny };
|
||||
}
|
||||
|
||||
Tile::ID Tile::ID::normalized() const {
|
||||
ID pos(z, x, y);
|
||||
pos.normalize();
|
||||
return pos;
|
||||
bool Tile::ID::isChildOf(const Tile::ID &parent) const {
|
||||
if (parent.z >= z || parent.w != w) {
|
||||
return false;
|
||||
}
|
||||
int32_t scale = pow(2, z - parent.z);
|
||||
return parent.x == ((x < 0 ? x - scale + 1 : x) / scale) &&
|
||||
parent.y == y / scale;
|
||||
}
|
||||
|
||||
@@ -1,33 +1,22 @@
|
||||
#include <llmr/map/tile_data.hpp>
|
||||
#include <llmr/map/tile_parser.hpp>
|
||||
#include <llmr/map/map.hpp>
|
||||
|
||||
#include <llmr/geometry/fill_buffer.hpp>
|
||||
#include <llmr/geometry/line_buffer.hpp>
|
||||
#include <llmr/geometry/point_buffer.hpp>
|
||||
#include <llmr/geometry/text_buffer.hpp>
|
||||
#include <llmr/geometry/elements_buffer.hpp>
|
||||
#include <llmr/util/raster.hpp>
|
||||
#include <llmr/util/string.hpp>
|
||||
|
||||
#include <llmr/util/uv.hpp>
|
||||
|
||||
using namespace llmr;
|
||||
|
||||
TileData::TileData(Tile::ID id, Map &map, const std::string url, const bool is_raster)
|
||||
TileData::TileData(Tile::ID id, Map &map, const std::string url)
|
||||
: id(id),
|
||||
state(State::initial),
|
||||
raster(),
|
||||
map(map),
|
||||
url(url),
|
||||
is_raster(is_raster),
|
||||
map(map) {
|
||||
debugBucket(debugFontBuffer) {
|
||||
// Initialize tile debug coordinates
|
||||
const std::string str = util::sprintf<32>("%d/%d/%d", id.z, id.x, id.y);
|
||||
debugFontBuffer.addText(str.c_str(), 50, 200, 5);
|
||||
}
|
||||
|
||||
TileData::~TileData() {
|
||||
map.getGlyphAtlas().removeGlyphs((uint64_t)id);
|
||||
cancel();
|
||||
}
|
||||
|
||||
@@ -51,7 +40,7 @@ void TileData::request() {
|
||||
tile->data.swap(res->body);
|
||||
|
||||
// Schedule tile parsing in another thread
|
||||
tile->scheduleParsing();
|
||||
tile->reparse();
|
||||
} else {
|
||||
fprintf(stderr, "[%s] tile loading failed: %d, %s\n", tile->url.c_str(), res->code, res->error_message.c_str());
|
||||
}
|
||||
@@ -65,7 +54,7 @@ void TileData::cancel() {
|
||||
}
|
||||
}
|
||||
|
||||
void TileData::scheduleParsing() {
|
||||
void TileData::reparse() {
|
||||
// We're creating a new work request. The work request deletes itself after it executed
|
||||
// the after work handler
|
||||
new uv::work<std::shared_ptr<TileData>>(
|
||||
@@ -74,24 +63,3 @@ void TileData::scheduleParsing() {
|
||||
[](std::shared_ptr<TileData> &tile) { tile->map.update(); },
|
||||
shared_from_this());
|
||||
}
|
||||
|
||||
void TileData::parse() {
|
||||
if (state != State::loaded) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// Parsing creates state that is encapsulated in TileParser. While parsing,
|
||||
// the TileParser object writes results into this objects. All other state
|
||||
// is going to be discarded afterwards.
|
||||
TileParser parser(data, *this, map.getStyle(), map.getGlyphAtlas(), is_raster);
|
||||
} catch (const std::exception& ex) {
|
||||
fprintf(stderr, "[%p] exception [%d/%d/%d]... failed: %s\n", this, id.z, id.x, id.y, ex.what());
|
||||
cancel();
|
||||
return;
|
||||
}
|
||||
|
||||
if (state != State::obsolete) {
|
||||
state = State::parsed;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,14 +14,13 @@
|
||||
using namespace llmr;
|
||||
|
||||
|
||||
TileParser::TileParser(const std::string& data, TileData& tile, const Style& style, GlyphAtlas& glyphAtlas, bool is_raster)
|
||||
: vector_data(is_raster ? pbf(0, 0) : pbf((const uint8_t *)data.data(), data.size())),
|
||||
raster_data(is_raster ? data : ""),
|
||||
TileParser::TileParser(const std::string& data, VectorTileData& tile, const Style& style, GlyphAtlas& glyphAtlas)
|
||||
: vector_data(pbf((const uint8_t *)data.data(), data.size())),
|
||||
tile(tile),
|
||||
style(style),
|
||||
glyphAtlas(glyphAtlas),
|
||||
placement(is_raster ? 0 : tile.id.z) {
|
||||
if (!is_raster) parseGlyphs();
|
||||
placement(tile.id.z) {
|
||||
parseGlyphs();
|
||||
parseStyleLayers(style.layers);
|
||||
}
|
||||
|
||||
@@ -37,7 +36,7 @@ void TileParser::parseGlyphs() {
|
||||
GlyphPositions &glyphs = faces[name];
|
||||
for (const VectorTileGlyph &glyph : face.glyphs) {
|
||||
const Rect<uint16_t> rect =
|
||||
glyphAtlas.addGlyph((uint64_t)tile.id, name, glyph);
|
||||
glyphAtlas.addGlyph(tile.id.to_uint64(), name, glyph);
|
||||
glyphs.emplace(glyph.id, Glyph{rect, glyph.metrics});
|
||||
}
|
||||
}
|
||||
@@ -85,31 +84,22 @@ void TileParser::parseStyleLayers(const std::vector<LayerDescription>& layers) {
|
||||
}
|
||||
|
||||
std::unique_ptr<Bucket> TileParser::createBucket(const BucketDescription& bucket_desc) {
|
||||
if (bucket_desc.type == BucketType::Raster) {
|
||||
if (raster_data.length()) {
|
||||
tile.raster = std::make_shared<Raster>();
|
||||
tile.raster->setData(raster_data);
|
||||
tile.raster->load();
|
||||
}
|
||||
return createRasterBucket(bucket_desc);
|
||||
} else {
|
||||
auto layer_it = vector_data.layers.find(bucket_desc.source_layer);
|
||||
if (layer_it != vector_data.layers.end()) {
|
||||
const VectorTileLayer& layer = layer_it->second;
|
||||
if (bucket_desc.type == BucketType::Fill) {
|
||||
return createFillBucket(layer, bucket_desc);
|
||||
} else if (bucket_desc.type == BucketType::Line) {
|
||||
return createLineBucket(layer, bucket_desc);
|
||||
} else if (bucket_desc.type == BucketType::Point) {
|
||||
return createPointBucket(layer, bucket_desc);
|
||||
} else if (bucket_desc.type == BucketType::Text) {
|
||||
return createTextBucket(layer, bucket_desc);
|
||||
} else {
|
||||
throw std::runtime_error("unknown bucket type");
|
||||
}
|
||||
auto layer_it = vector_data.layers.find(bucket_desc.source_layer);
|
||||
if (layer_it != vector_data.layers.end()) {
|
||||
const VectorTileLayer& layer = layer_it->second;
|
||||
if (bucket_desc.type == BucketType::Fill) {
|
||||
return createFillBucket(layer, bucket_desc);
|
||||
} else if (bucket_desc.type == BucketType::Line) {
|
||||
return createLineBucket(layer, bucket_desc);
|
||||
} else if (bucket_desc.type == BucketType::Point) {
|
||||
return createPointBucket(layer, bucket_desc);
|
||||
} else if (bucket_desc.type == BucketType::Text) {
|
||||
return createTextBucket(layer, bucket_desc);
|
||||
} else {
|
||||
// The layer specified in the bucket does not exist. Do nothing.
|
||||
throw std::runtime_error("unknown bucket type");
|
||||
}
|
||||
} else {
|
||||
// The layer specified in the bucket does not exist. Do nothing.
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
@@ -183,10 +173,3 @@ std::unique_ptr<Bucket> TileParser::createTextBucket(const VectorTileLayer& laye
|
||||
|
||||
return std::move(bucket);
|
||||
}
|
||||
|
||||
std::unique_ptr<Bucket> TileParser::createRasterBucket(const BucketDescription& bucket_desc) {
|
||||
std::unique_ptr<RasterBucket> bucket = std::make_unique<RasterBucket>(bucket_desc);
|
||||
// Raster buckets are just empty dummies so that they behave
|
||||
// similarly to vector buckets in styling configurations.
|
||||
return obsolete() ? nullptr : std::move(bucket);
|
||||
}
|
||||
|
||||
55
src/map/vector_tile_data.cpp
Normal file
55
src/map/vector_tile_data.cpp
Normal file
@@ -0,0 +1,55 @@
|
||||
#include <llmr/map/vector_tile_data.hpp>
|
||||
|
||||
#include <llmr/map/map.hpp>
|
||||
#include <llmr/map/tile_parser.hpp>
|
||||
|
||||
using namespace llmr;
|
||||
|
||||
VectorTileData::VectorTileData(Tile::ID id, Map &map, const std::string url)
|
||||
: TileData(id, map, url) {
|
||||
}
|
||||
|
||||
VectorTileData::~VectorTileData() {
|
||||
map.getGlyphAtlas().removeGlyphs(id.to_uint64());
|
||||
}
|
||||
|
||||
void VectorTileData::parse() {
|
||||
if (state != State::loaded) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// Parsing creates state that is encapsulated in TileParser. While parsing,
|
||||
// the TileParser object writes results into this objects. All other state
|
||||
// is going to be discarded afterwards.
|
||||
TileParser parser(data, *this, map.getStyle(), map.getGlyphAtlas());
|
||||
} catch (const std::exception& ex) {
|
||||
fprintf(stderr, "[%p] exception [%d/%d/%d]... failed: %s\n", this, id.z, id.x, id.y, ex.what());
|
||||
cancel();
|
||||
return;
|
||||
}
|
||||
|
||||
if (state != State::obsolete) {
|
||||
state = State::parsed;
|
||||
}
|
||||
}
|
||||
|
||||
void VectorTileData::render(Painter &painter, const LayerDescription& layer_desc) {
|
||||
auto databucket_it = buckets.find(layer_desc.bucket_name);
|
||||
if (databucket_it != buckets.end()) {
|
||||
assert(databucket_it->second);
|
||||
databucket_it->second->render(painter, layer_desc.name, id);
|
||||
}
|
||||
}
|
||||
|
||||
bool VectorTileData::hasData(const LayerDescription& layer_desc) const {
|
||||
if (state != State::parsed) return false;
|
||||
|
||||
auto databucket_it = buckets.find(layer_desc.bucket_name);
|
||||
if (databucket_it != buckets.end()) {
|
||||
assert(databucket_it->second);
|
||||
return databucket_it->second->hasData();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
33
src/renderer/debug_bucket.cpp
Normal file
33
src/renderer/debug_bucket.cpp
Normal file
@@ -0,0 +1,33 @@
|
||||
#include <llmr/renderer/debug_bucket.hpp>
|
||||
|
||||
#include <llmr/renderer/painter.hpp>
|
||||
|
||||
#include <llmr/platform/gl.hpp>
|
||||
|
||||
#include <cassert>
|
||||
|
||||
struct geometry_too_long_exception : std::exception {};
|
||||
|
||||
using namespace llmr;
|
||||
|
||||
DebugBucket::DebugBucket(DebugFontBuffer& fontBuffer)
|
||||
: fontBuffer(fontBuffer) {
|
||||
}
|
||||
|
||||
void DebugBucket::render(Painter& painter, const std::string& /*layer_name*/, const Tile::ID& /*id*/) {
|
||||
painter.renderDebugText(*this);
|
||||
}
|
||||
|
||||
bool DebugBucket::hasData() const {
|
||||
return fontBuffer.index() > 0;
|
||||
}
|
||||
|
||||
void DebugBucket::drawLines(PlainShader& shader) {
|
||||
array.bind(shader, fontBuffer, BUFFER_OFFSET(0));
|
||||
glDrawArrays(GL_LINES, 0, (GLsizei)(fontBuffer.index()));
|
||||
}
|
||||
|
||||
void DebugBucket::drawPoints(PlainShader& shader) {
|
||||
array.bind(shader, fontBuffer, BUFFER_OFFSET(0));
|
||||
glDrawArrays(GL_POINTS, 0, (GLsizei)(fontBuffer.index()));
|
||||
}
|
||||
@@ -206,8 +206,8 @@ void FillBucket::render(Painter& painter, const std::string& layer_name, const T
|
||||
painter.renderFill(*this, layer_name, id);
|
||||
}
|
||||
|
||||
bool FillBucket::empty() const {
|
||||
return triangleGroups.empty() && lineGroups.empty();
|
||||
bool FillBucket::hasData() const {
|
||||
return !triangleGroups.empty() || !lineGroups.empty();
|
||||
}
|
||||
|
||||
void FillBucket::drawElements(PlainShader& shader) {
|
||||
|
||||
@@ -348,8 +348,8 @@ void LineBucket::render(Painter& painter, const std::string& layer_name, const T
|
||||
painter.renderLine(*this, layer_name, id);
|
||||
}
|
||||
|
||||
bool LineBucket::empty() const {
|
||||
return triangleGroups.empty();
|
||||
bool LineBucket::hasData() const {
|
||||
return !triangleGroups.empty() || !pointGroups.empty();
|
||||
}
|
||||
|
||||
bool LineBucket::hasPoints() const {
|
||||
|
||||
@@ -1,27 +1,15 @@
|
||||
#include <cassert>
|
||||
#include <algorithm>
|
||||
|
||||
|
||||
#include <llmr/renderer/painter.hpp>
|
||||
#include <llmr/util/std.hpp>
|
||||
|
||||
#include <llmr/renderer/fill_bucket.hpp>
|
||||
#include <llmr/renderer/line_bucket.hpp>
|
||||
#include <llmr/renderer/point_bucket.hpp>
|
||||
#include <llmr/renderer/text_bucket.hpp>
|
||||
#include <llmr/renderer/raster_bucket.hpp>
|
||||
|
||||
#include <llmr/map/map.hpp>
|
||||
#include <llmr/map/transform.hpp>
|
||||
#include <llmr/geometry/debug_font_buffer.hpp>
|
||||
#include <llmr/geometry/glyph_atlas.hpp>
|
||||
#include <llmr/platform/gl.hpp>
|
||||
#include <llmr/style/style.hpp>
|
||||
#include <llmr/style/sprite.hpp>
|
||||
#include <llmr/util/raster.hpp>
|
||||
|
||||
#include <llmr/util/std.hpp>
|
||||
#include <llmr/util/string.hpp>
|
||||
#include <llmr/util/timer.hpp>
|
||||
#include <llmr/util/time.hpp>
|
||||
#include <llmr/util/clip_ids.hpp>
|
||||
|
||||
#include <cassert>
|
||||
#include <algorithm>
|
||||
|
||||
using namespace llmr;
|
||||
|
||||
@@ -129,39 +117,6 @@ void Painter::changeMatrix() {
|
||||
matrix::multiply(nativeMatrix, projMatrix, nativeMatrix);
|
||||
}
|
||||
|
||||
void Painter::prepareClippingMask() {
|
||||
gl::start_group("clipping masks");
|
||||
useProgram(plainShader->program);
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
depthMask(false);
|
||||
glDepthRange(1.0f, 1.0f);
|
||||
|
||||
coveringPlainArray.bind(*plainShader, tileStencilBuffer, BUFFER_OFFSET(0));
|
||||
}
|
||||
|
||||
void Painter::drawClippingMask(const mat4& matrix, uint8_t clip_id) {
|
||||
gl::group group("mask");
|
||||
plainShader->setMatrix(matrix);
|
||||
|
||||
Color background = map.getStyle().computed.background.color;
|
||||
background[0] *= map.getStyle().computed.background.opacity;
|
||||
background[1] *= map.getStyle().computed.background.opacity;
|
||||
background[2] *= map.getStyle().computed.background.opacity;
|
||||
background[3] *= map.getStyle().computed.background.opacity;
|
||||
|
||||
plainShader->setColor(background);
|
||||
|
||||
glStencilFunc(GL_ALWAYS, clip_id, 0xFF);
|
||||
glDrawArrays(GL_TRIANGLES, 0, (GLsizei)tileStencilBuffer.index());
|
||||
}
|
||||
|
||||
void Painter::finishClippingMask() {
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
depthMask(true);
|
||||
glStencilMask(0x0);
|
||||
gl::end_group();
|
||||
}
|
||||
|
||||
void Painter::clear() {
|
||||
gl::group group("clear");
|
||||
glStencilMask(0xFF);
|
||||
@@ -175,85 +130,43 @@ void Painter::clear() {
|
||||
#endif
|
||||
}
|
||||
|
||||
void Painter::render(const Tile& tile) {
|
||||
gl::group group(util::sprintf<32>("render %d/%d/%d", tile.id.z, tile.id.y, tile.id.z));
|
||||
|
||||
assert(tile.data);
|
||||
if (tile.data->state != TileData::State::parsed) {
|
||||
return;
|
||||
}
|
||||
|
||||
frameHistory.record(map.getAnimationTime(), map.getState().getNormalizedZoom());
|
||||
|
||||
matrix = tile.matrix;
|
||||
glStencilFunc(GL_EQUAL, tile.clip_id, 0xFF);
|
||||
|
||||
renderLayers(tile.data, map.getStyle().layers);
|
||||
|
||||
if (debug) {
|
||||
renderDebug(tile.data);
|
||||
}
|
||||
}
|
||||
|
||||
void Painter::renderLayers(const std::shared_ptr<TileData>& tile_data, const std::vector<LayerDescription>& layers) {
|
||||
float strata_thickness = 1.0f / (layers.size() + 1);
|
||||
|
||||
// - FIRST PASS ------------------------------------------------------------
|
||||
// Render everything top-to-bottom by using reverse iterators. Render opaque
|
||||
// objects first.
|
||||
void Painter::startOpaquePass() {
|
||||
gl::start_group("opaque pass");
|
||||
pass = Opaque;
|
||||
glDisable(GL_BLEND);
|
||||
depthMask(true);
|
||||
}
|
||||
|
||||
typedef std::vector<LayerDescription>::const_reverse_iterator riterator;
|
||||
int i = 0;
|
||||
for (riterator it = layers.rbegin(), end = layers.rend(); it != end; ++it, ++i) {
|
||||
strata = i * strata_thickness;
|
||||
renderLayer(tile_data, *it);
|
||||
}
|
||||
gl::end_group();
|
||||
|
||||
// - SECOND PASS -----------------------------------------------------------
|
||||
// Make a second pass, rendering translucent objects. This time, we render
|
||||
// bottom-to-top.
|
||||
void Painter::startTranslucentPass() {
|
||||
gl::start_group("translucent pass");
|
||||
pass = Translucent;
|
||||
glEnable(GL_BLEND);
|
||||
depthMask(false);
|
||||
}
|
||||
|
||||
typedef std::vector<LayerDescription>::const_iterator iterator;
|
||||
--i;
|
||||
for (iterator it = layers.begin(), end = layers.end(); it != end; ++it, --i) {
|
||||
strata = i * strata_thickness;
|
||||
renderLayer(tile_data, *it);
|
||||
}
|
||||
void Painter::endPass() {
|
||||
gl::end_group();
|
||||
}
|
||||
|
||||
void Painter::renderLayer(const std::shared_ptr<TileData>& tile_data, const LayerDescription& layer_desc) {
|
||||
if (layer_desc.child_layer.size()) {
|
||||
// This is a layer group.
|
||||
// TODO: create framebuffer
|
||||
renderLayers(tile_data, layer_desc.child_layer);
|
||||
// TODO: render framebuffer on previous framebuffer
|
||||
} else {
|
||||
// This is a singular layer. Try to find the bucket associated with
|
||||
// this layer and render it.
|
||||
auto bucket_it = map.getStyle().buckets.find(layer_desc.bucket_name);
|
||||
if (bucket_it != map.getStyle().buckets.end()) {
|
||||
if (bucket_it->second.type == BucketType::Raster) {
|
||||
if (tile_data && tile_data->raster) {
|
||||
renderRaster(layer_desc.name, tile_data);
|
||||
}
|
||||
} else {
|
||||
auto databucket_it = tile_data->buckets.find(layer_desc.bucket_name);
|
||||
if (databucket_it != tile_data->buckets.end()) {
|
||||
assert(databucket_it->second);
|
||||
databucket_it->second->render(*this, layer_desc.name, tile_data->id);
|
||||
}
|
||||
}
|
||||
}
|
||||
void Painter::setStrata(float value) {
|
||||
strata = value;
|
||||
}
|
||||
|
||||
void Painter::prepareTile(const Tile& tile) {
|
||||
matrix = tile.matrix;
|
||||
|
||||
GLint id = (GLint)tile.clip.mask.to_ulong();
|
||||
GLuint mask = clipMask[tile.clip.length];
|
||||
glStencilFunc(GL_EQUAL, id, mask);
|
||||
}
|
||||
|
||||
void Painter::renderTileLayer(const Tile& tile, const LayerDescription &layer_desc) {
|
||||
assert(tile.data);
|
||||
if (tile.data->hasData(layer_desc)) {
|
||||
gl::group group(util::sprintf<32>("render %d/%d/%d", tile.id.z, tile.id.y, tile.id.z));
|
||||
prepareTile(tile);
|
||||
tile.data->render(*this, layer_desc);
|
||||
frameHistory.record(map.getAnimationTime(), map.getState().getNormalizedZoom());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -271,477 +184,13 @@ void Painter::translateLayer(std::array<float, 2> translation, bool reverse) {
|
||||
}
|
||||
}
|
||||
|
||||
void Painter::renderRaster(const std::string& layer_name, const std::shared_ptr<TileData>& tile_data) {
|
||||
if (pass == Opaque) return;
|
||||
|
||||
auto raster_properties = map.getStyle().computed.rasters;
|
||||
auto raster_properties_it = raster_properties.find(layer_name);
|
||||
if (raster_properties_it == raster_properties.end()) return;
|
||||
|
||||
const RasterProperties& properties = raster_properties_it->second;
|
||||
if (!properties.enabled) return;
|
||||
|
||||
gl::group group(layer_name + " (raster)");
|
||||
|
||||
useProgram(rasterShader->program);
|
||||
rasterShader->setMatrix(matrix);
|
||||
rasterShader->setImage(0);
|
||||
rasterShader->setOpacity(properties.opacity * tile_data->raster->opacity);
|
||||
tile_data->raster->bind(true);
|
||||
|
||||
coveringRasterArray.bind(*rasterShader, tileStencilBuffer, BUFFER_OFFSET(0));
|
||||
glDepthRange(strata, 1.0f);
|
||||
glDrawArrays(GL_TRIANGLES, 0, (GLsizei)tileStencilBuffer.index());
|
||||
}
|
||||
|
||||
void Painter::renderFill(FillBucket& bucket, const std::string& layer_name, const Tile::ID& id) {
|
||||
// Abort early.
|
||||
if (bucket.empty()) return;
|
||||
|
||||
auto fill_properties = map.getStyle().computed.fills;
|
||||
auto fill_properties_it = fill_properties.find(layer_name);
|
||||
if (fill_properties_it == fill_properties.end()) return;
|
||||
|
||||
const FillProperties& properties = fill_properties_it->second;
|
||||
if (!properties.enabled) return;
|
||||
|
||||
Color fill_color = properties.fill_color;
|
||||
fill_color[0] *= properties.opacity;
|
||||
fill_color[1] *= properties.opacity;
|
||||
fill_color[2] *= properties.opacity;
|
||||
fill_color[3] *= properties.opacity;
|
||||
|
||||
Color stroke_color = properties.stroke_color;
|
||||
stroke_color[0] *= properties.opacity;
|
||||
stroke_color[1] *= properties.opacity;
|
||||
stroke_color[2] *= properties.opacity;
|
||||
stroke_color[3] *= properties.opacity;
|
||||
|
||||
bool outline = properties.antialias && properties.stroke_color != properties.fill_color;
|
||||
bool fringeline = properties.antialias && properties.stroke_color == properties.fill_color;
|
||||
if (fringeline) {
|
||||
outline = true;
|
||||
stroke_color = fill_color;
|
||||
}
|
||||
|
||||
translateLayer(properties.translate);
|
||||
|
||||
gl::group group(layer_name + " (fill)");
|
||||
// Because we're drawing top-to-bottom, and we update the stencil mask
|
||||
// below, we have to draw the outline first (!)
|
||||
if (outline && pass == Translucent) {
|
||||
useProgram(outlineShader->program);
|
||||
outlineShader->setMatrix(matrix);
|
||||
lineWidth(2.0f); // This is always fixed and does not depend on the pixelRatio!
|
||||
|
||||
outlineShader->setColor(stroke_color);
|
||||
|
||||
// Draw the entire line
|
||||
outlineShader->setWorld({{
|
||||
static_cast<float>(map.getState().getFramebufferWidth()),
|
||||
static_cast<float>(map.getState().getFramebufferHeight())
|
||||
}});
|
||||
glDepthRange(strata, 1.0f);
|
||||
bucket.drawVertices(*outlineShader);
|
||||
} else if (fringeline) {
|
||||
// // We're only drawing to the first seven bits (== support a maximum of
|
||||
// // 127 overlapping polygons in one place before we get rendering errors).
|
||||
// glStencilMask(0x3F);
|
||||
// glClear(GL_STENCIL_BUFFER_BIT);
|
||||
|
||||
// // Draw front facing triangles. Wherever the 0x80 bit is 1, we are
|
||||
// // increasing the lower 7 bits by one if the triangle is a front-facing
|
||||
// // triangle. This means that all visible polygons should be in CCW
|
||||
// // orientation, while all holes (see below) are in CW orientation.
|
||||
// glStencilFunc(GL_EQUAL, 0x80, 0x80);
|
||||
|
||||
// // When we do a nonzero fill, we count the number of times a pixel is
|
||||
// // covered by a counterclockwise polygon, and subtract the number of
|
||||
// // times it is "uncovered" by a clockwise polygon.
|
||||
// glStencilOp(GL_KEEP, GL_KEEP, GL_INCR_WRAP);
|
||||
}
|
||||
|
||||
if ((fill_color[3] >= 1.0f) == (pass == Opaque)) {
|
||||
auto &sprite = map.getStyle().sprite;
|
||||
if (properties.image.size() && sprite && sprite->isLoaded()) {
|
||||
// Draw texture fill
|
||||
ImagePosition imagePos = sprite->getPosition(properties.image, true);
|
||||
|
||||
float factor = 8.0 / pow(2, map.getState().getIntegerZoom() - id.z);
|
||||
float mix = fmod(map.getState().getZoom(), 1.0);
|
||||
|
||||
std::array<float, 2> imageSize = {{
|
||||
imagePos.size.x * factor,
|
||||
imagePos.size.y *factor
|
||||
}
|
||||
};
|
||||
|
||||
std::array<float, 2> offset = {{
|
||||
(float)fmod(id.x * 4096, imageSize[0]),
|
||||
(float)fmod(id.y * 4096, imageSize[1])
|
||||
}
|
||||
};
|
||||
|
||||
useProgram(patternShader->program);
|
||||
patternShader->setMatrix(matrix);
|
||||
patternShader->setOffset(offset);
|
||||
patternShader->setPatternSize(imageSize);
|
||||
patternShader->setPatternTopLeft({{ imagePos.tl.x, imagePos.tl.y }});
|
||||
patternShader->setPatternBottomRight({{ imagePos.br.x, imagePos.br.y }});
|
||||
patternShader->setColor(fill_color);
|
||||
patternShader->setMix(mix);
|
||||
sprite->raster->bind(true);
|
||||
|
||||
// Draw the actual triangles into the color & stencil buffer.
|
||||
glDepthRange(strata + strata_epsilon, 1.0f);
|
||||
bucket.drawElements(*patternShader);
|
||||
} else {
|
||||
// Only draw the fill when it's either opaque and we're drawing opaque
|
||||
// fragments or when it's translucent and we're drawing translucent
|
||||
// fragments
|
||||
// Draw filling rectangle.
|
||||
useProgram(plainShader->program);
|
||||
plainShader->setMatrix(matrix);
|
||||
plainShader->setColor(fill_color);
|
||||
|
||||
// Draw the actual triangles into the color & stencil buffer.
|
||||
glDepthRange(strata + strata_epsilon, 1.0f);
|
||||
bucket.drawElements(*plainShader);
|
||||
}
|
||||
}
|
||||
|
||||
// Because we're drawing top-to-bottom, and we update the stencil mask
|
||||
// below, we have to draw the outline first (!)
|
||||
if (fringeline && pass == Translucent) {
|
||||
useProgram(outlineShader->program);
|
||||
outlineShader->setMatrix(matrix);
|
||||
lineWidth(2.0f); // This is always fixed and does not depend on the pixelRatio!
|
||||
|
||||
outlineShader->setColor(fill_color);
|
||||
|
||||
// Draw the entire line
|
||||
outlineShader->setWorld({{
|
||||
static_cast<float>(map.getState().getFramebufferWidth()),
|
||||
static_cast<float>(map.getState().getFramebufferHeight())
|
||||
}});
|
||||
|
||||
glDepthRange(strata + strata_epsilon, 1.0f);
|
||||
bucket.drawVertices(*outlineShader);
|
||||
}
|
||||
|
||||
translateLayer(properties.translate, true);
|
||||
}
|
||||
|
||||
void Painter::renderLine(LineBucket& bucket, const std::string& layer_name, const Tile::ID& /*id*/) {
|
||||
// Abort early.
|
||||
if (pass == Opaque) return;
|
||||
if (bucket.empty()) return;
|
||||
|
||||
auto line_properties = map.getStyle().computed.lines;
|
||||
auto line_properties_it = line_properties.find(layer_name);
|
||||
if (line_properties_it == line_properties.end()) return;
|
||||
|
||||
const LineProperties& properties = line_properties_it->second;
|
||||
if (!properties.enabled) return;
|
||||
|
||||
float width = properties.width;
|
||||
float offset = properties.offset / 2;
|
||||
|
||||
// These are the radii of the line. We are limiting it to 16, which will result
|
||||
// in a point size of 64 on retina.
|
||||
float inset = fmin((fmax(-1, offset - width / 2 - 0.5) + 1), 16.0f);
|
||||
float outset = fmin(offset + width / 2 + 0.5, 16.0f);
|
||||
|
||||
Color color = properties.color;
|
||||
color[0] *= properties.opacity;
|
||||
color[1] *= properties.opacity;
|
||||
color[2] *= properties.opacity;
|
||||
color[3] *= properties.opacity;
|
||||
|
||||
float dash_length = properties.dash_array[0];
|
||||
float dash_gap = properties.dash_array[1];
|
||||
|
||||
translateLayer(properties.translate);
|
||||
|
||||
gl::group group(layer_name + " (line)");
|
||||
glDepthRange(strata, 1.0f);
|
||||
|
||||
// We're only drawing end caps + round line joins if the line is > 2px. Otherwise, they aren't visible anyway.
|
||||
if (bucket.hasPoints() && outset > 1.0f) {
|
||||
useProgram(linejoinShader->program);
|
||||
linejoinShader->setMatrix(matrix);
|
||||
linejoinShader->setColor(color);
|
||||
linejoinShader->setWorld({{
|
||||
map.getState().getFramebufferWidth() * 0.5f,
|
||||
map.getState().getFramebufferHeight() * 0.5f
|
||||
}
|
||||
});
|
||||
linejoinShader->setLineWidth({{
|
||||
((outset - 0.25f) * map.getState().getPixelRatio()),
|
||||
((inset - 0.25f) * map.getState().getPixelRatio())
|
||||
}
|
||||
});
|
||||
|
||||
float pointSize = ceil(map.getState().getPixelRatio() * outset * 2.0);
|
||||
#if defined(GL_ES_VERSION_2_0)
|
||||
linejoinShader->setSize(pointSize);
|
||||
#else
|
||||
glPointSize(pointSize);
|
||||
#endif
|
||||
bucket.drawPoints(*linejoinShader);
|
||||
}
|
||||
|
||||
// var imagePos = properties.image && imageSprite.getPosition(properties.image);
|
||||
bool imagePos = false;
|
||||
if (imagePos) {
|
||||
// var factor = 8 / Math.pow(2, painter.transform.zoom - params.z);
|
||||
|
||||
// imageSprite.bind(gl, true);
|
||||
|
||||
// //factor = Math.pow(2, 4 - painter.transform.zoom + params.z);
|
||||
// gl.switchShader(painter.linepatternShader, painter.translatedMatrix || painter.posMatrix, painter.extrudeMatrix);
|
||||
// shader = painter.linepatternShader;
|
||||
// glUniform2fv(painter.linepatternShader.u_pattern_size, [imagePos.size[0] * factor, imagePos.size[1] ]);
|
||||
// glUniform2fv(painter.linepatternShader.u_pattern_tl, imagePos.tl);
|
||||
// glUniform2fv(painter.linepatternShader.u_pattern_br, imagePos.br);
|
||||
// glUniform1f(painter.linepatternShader.u_fade, painter.transform.z % 1.0);
|
||||
|
||||
} else {
|
||||
useProgram(lineShader->program);
|
||||
lineShader->setMatrix(matrix);
|
||||
lineShader->setExtrudeMatrix(extrudeMatrix);
|
||||
lineShader->setDashArray({{ dash_length, dash_gap }});
|
||||
lineShader->setLineWidth({{ outset, inset }});
|
||||
lineShader->setRatio(map.getState().getPixelRatio());
|
||||
lineShader->setColor(color);
|
||||
bucket.drawLines(*lineShader);
|
||||
}
|
||||
|
||||
translateLayer(properties.translate, true);
|
||||
}
|
||||
|
||||
void Painter::renderPoint(PointBucket& bucket, const std::string& layer_name, const Tile::ID& /*id&*/) {
|
||||
// Abort early.
|
||||
if (!bucket.hasPoints()) return;
|
||||
if (pass == Opaque) return;
|
||||
|
||||
auto point_properties = map.getStyle().computed.points;
|
||||
auto point_properties_it = point_properties.find(layer_name);
|
||||
if (point_properties_it == point_properties.end()) return;
|
||||
|
||||
const PointProperties& properties = point_properties_it->second;
|
||||
if (!properties.enabled) return;
|
||||
|
||||
translateLayer(properties.translate);
|
||||
|
||||
gl::group group(layer_name + " (point)");
|
||||
|
||||
Color color = properties.color;
|
||||
color[0] *= properties.opacity;
|
||||
color[1] *= properties.opacity;
|
||||
color[2] *= properties.opacity;
|
||||
color[3] *= properties.opacity;
|
||||
|
||||
auto &sprite = map.getStyle().sprite;
|
||||
ImagePosition imagePos;
|
||||
|
||||
if (properties.image.length() && sprite && sprite->isLoaded()) {
|
||||
std::string sized_image = properties.image;
|
||||
if (properties.size) {
|
||||
sized_image.append("-");
|
||||
sized_image.append(std::to_string(static_cast<int>(std::round(properties.size))));
|
||||
}
|
||||
imagePos = sprite->getPosition(sized_image, false);
|
||||
}
|
||||
|
||||
if (!imagePos.size) {
|
||||
useProgram(dotShader->program);
|
||||
dotShader->setMatrix(matrix);
|
||||
dotShader->setColor(color);
|
||||
|
||||
const float pointSize = (properties.radius ? properties.radius * 2 : 8) * map.getState().getPixelRatio();
|
||||
#if defined(GL_ES_VERSION_2_0)
|
||||
dotShader->setSize(pointSize);
|
||||
#else
|
||||
glPointSize(pointSize);
|
||||
glEnable(GL_POINT_SPRITE);
|
||||
#endif
|
||||
dotShader->setBlur((properties.blur ? properties.blur : 1.5) / pointSize);
|
||||
|
||||
glDepthRange(strata, 1.0f);
|
||||
bucket.drawPoints(*dotShader);
|
||||
} else {
|
||||
useProgram(pointShader->program);
|
||||
pointShader->setMatrix(matrix);
|
||||
pointShader->setColor(color);
|
||||
|
||||
pointShader->setImage(0);
|
||||
pointShader->setPointTopLeft({{ imagePos.tl.x, imagePos.tl.y }});
|
||||
pointShader->setPointBottomRight({{ imagePos.br.x, imagePos.br.y }});
|
||||
|
||||
sprite->raster->bind(map.getState().isChanging());
|
||||
|
||||
const float pointSize = (properties.size ? properties.size : (imagePos.size.x / map.getState().getPixelRatio())) * 1.4142135623730951 * map.getState().getPixelRatio();
|
||||
#if defined(GL_ES_VERSION_2_0)
|
||||
pointShader->setSize(pointSize);
|
||||
#else
|
||||
glPointSize(pointSize);
|
||||
glEnable(GL_POINT_SPRITE);
|
||||
#endif
|
||||
|
||||
glDepthRange(strata, 1.0f);
|
||||
bucket.drawPoints(*pointShader);
|
||||
}
|
||||
|
||||
translateLayer(properties.translate, true);
|
||||
}
|
||||
|
||||
void Painter::renderText(TextBucket& bucket, const std::string& layer_name, const Tile::ID& /*id*/) {
|
||||
// Abort early.
|
||||
if (pass == Opaque) return;
|
||||
if (bucket.empty()) return;
|
||||
|
||||
auto text_properties = map.getStyle().computed.texts;
|
||||
auto text_properties_it = text_properties.find(layer_name);
|
||||
if (text_properties_it == text_properties.end()) return;
|
||||
|
||||
const TextProperties& properties = text_properties_it->second;
|
||||
if (!properties.enabled) return;
|
||||
|
||||
translateLayer(properties.translate);
|
||||
|
||||
gl::group group(layer_name + " (text)");
|
||||
|
||||
mat4 exMatrix;
|
||||
matrix::copy(exMatrix, projMatrix);
|
||||
if (bucket.geom_desc.path == TextPathType::Curve) {
|
||||
matrix::rotate_z(exMatrix, exMatrix, map.getState().getAngle());
|
||||
}
|
||||
|
||||
const float rotate = properties.rotate;
|
||||
if (rotate != 0.0f) {
|
||||
matrix::rotate_z(exMatrix, exMatrix, rotate);
|
||||
}
|
||||
|
||||
// If layerStyle.size > bucket.info.fontSize then labels may collide
|
||||
float fontSize = fmin(properties.size, bucket.geom_desc.font_size);
|
||||
matrix::scale(exMatrix, exMatrix, fontSize / 24.0f, fontSize / 24.0f, 1.0f);
|
||||
|
||||
useProgram(textShader->program);
|
||||
textShader->setMatrix(matrix);
|
||||
textShader->setExtrudeMatrix(exMatrix);
|
||||
|
||||
map.getGlyphAtlas().bind();
|
||||
textShader->setTextureSize({{static_cast<float>(map.getGlyphAtlas().width),
|
||||
static_cast<float>(map.getGlyphAtlas().height)}});
|
||||
|
||||
textShader->setGamma(2.5f / fontSize / map.getState().getPixelRatio());
|
||||
|
||||
// Convert the -pi..pi to an int8 range.
|
||||
float angle = round((map.getState().getAngle() + rotate) / M_PI * 128);
|
||||
|
||||
// adjust min/max zooms for variable font sies
|
||||
float zoomAdjust = log(fontSize / bucket.geom_desc.font_size) / log(2);
|
||||
|
||||
textShader->setAngle((int32_t)(angle + 256) % 256);
|
||||
textShader->setFlip(bucket.geom_desc.path == TextPathType::Curve ? 1 : 0);
|
||||
textShader->setZoom((map.getState().getNormalizedZoom() - zoomAdjust) * 10); // current zoom level
|
||||
|
||||
// Label fading
|
||||
const time duration = 300_milliseconds;
|
||||
const time currentTime = util::now();
|
||||
|
||||
std::deque<FrameSnapshot> &history = frameHistory.history;
|
||||
|
||||
// Remove frames until only one is outside the duration, or until there are only three
|
||||
while (history.size() > 3 && history[1].timestamp + duration < currentTime) {
|
||||
history.pop_front();
|
||||
}
|
||||
|
||||
if (history[1].timestamp + duration < currentTime) {
|
||||
history[0].z = history[1].z;
|
||||
}
|
||||
|
||||
size_t frameLen = history.size();
|
||||
assert("there should never be less than three frames in the history" && frameLen >= 3);
|
||||
|
||||
// Find the range of zoom levels we want to fade between
|
||||
float startingZ = history.front().z;
|
||||
const FrameSnapshot lastFrame = history.back();
|
||||
float endingZ = lastFrame.z;
|
||||
float lowZ = fmin(startingZ, endingZ);
|
||||
float highZ = fmax(startingZ, endingZ);
|
||||
|
||||
// Calculate the speed of zooming, and how far it would zoom in terms of zoom levels in one duration
|
||||
float zoomDiff = endingZ - history[1].z,
|
||||
timeDiff = lastFrame.timestamp - history[1].timestamp;
|
||||
if (timeDiff > duration) timeDiff = 1;
|
||||
float fadedist = zoomDiff / (timeDiff / duration);
|
||||
|
||||
if (isnan(fadedist)) fprintf(stderr, "fadedist should never be NaN\n");
|
||||
|
||||
// At end of a zoom when the zoom stops changing continue pretending to zoom at that speed
|
||||
// bump is how much farther it would have been if it had continued zooming at the same rate
|
||||
float bump = (currentTime - lastFrame.timestamp) / duration * fadedist;
|
||||
|
||||
textShader->setFadeDist(fadedist * 10);
|
||||
textShader->setMinFadeZoom(floor(lowZ * 10));
|
||||
textShader->setMaxFadeZoom(floor(highZ * 10));
|
||||
textShader->setFadeZoom((map.getState().getZoom() + bump) * 10);
|
||||
|
||||
// We're drawing in the translucent pass which is bottom-to-top, so we need
|
||||
// to draw the halo first.
|
||||
if (properties.halo[3] > 0.0f) {
|
||||
textShader->setColor(properties.halo);
|
||||
textShader->setBuffer(properties.haloRadius);
|
||||
glDepthRange(strata, 1.0f);
|
||||
bucket.drawGlyphs(*textShader);
|
||||
}
|
||||
|
||||
// Then, we draw the text over the halo
|
||||
textShader->setColor(properties.color);
|
||||
textShader->setBuffer((256.0f - 64.0f) / 256.0f);
|
||||
glDepthRange(strata + strata_epsilon, 1.0f);
|
||||
bucket.drawGlyphs(*textShader);
|
||||
|
||||
translateLayer(properties.translate, true);
|
||||
}
|
||||
|
||||
void Painter::renderDebug(const TileData::Ptr& tile_data) {
|
||||
gl::group group("debug");
|
||||
|
||||
// Disable depth test and don't count this towards the depth buffer,
|
||||
// but *don't* disable stencil test, as we want to clip the red tile border
|
||||
// to the tile viewport.
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
|
||||
useProgram(plainShader->program);
|
||||
plainShader->setMatrix(matrix);
|
||||
|
||||
// draw tile outline
|
||||
tileBorderArray.bind(*plainShader, tileBorderBuffer, BUFFER_OFFSET(0));
|
||||
plainShader->setColor(1.0f, 0.0f, 0.0f, 1.0f);
|
||||
lineWidth(4.0f * map.getState().getPixelRatio());
|
||||
glDrawArrays(GL_LINE_STRIP, 0, (GLsizei)tileBorderBuffer.index());
|
||||
|
||||
// draw debug info
|
||||
tile_data->debugFontArray.bind(*plainShader, tile_data->debugFontBuffer, BUFFER_OFFSET(0));
|
||||
plainShader->setColor(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
lineWidth(4.0f * map.getState().getPixelRatio());
|
||||
glDrawArrays(GL_LINES, 0, (GLsizei)tile_data->debugFontBuffer.index());
|
||||
plainShader->setColor(0.0f, 0.0f, 0.0f, 1.0f);
|
||||
lineWidth(2.0f * map.getState().getPixelRatio());
|
||||
glDrawArrays(GL_LINES, 0, (GLsizei)tile_data->debugFontBuffer.index());
|
||||
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
}
|
||||
|
||||
void Painter::renderMatte() {
|
||||
gl::group group("matte");
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glStencilFunc(GL_EQUAL, 0x0, 0xFF);
|
||||
|
||||
Color white = {{ 0.9, 0.9, 0.9, 1 }};
|
||||
// Color white = {{ 0.9, 0.9, 0.9, 1 }};
|
||||
Color white = {{ 1, 1, 0, 1 }};
|
||||
|
||||
useProgram(plainShader->program);
|
||||
plainShader->setMatrix(nativeMatrix);
|
||||
|
||||
42
src/renderer/painter_clipping.cpp
Normal file
42
src/renderer/painter_clipping.cpp
Normal file
@@ -0,0 +1,42 @@
|
||||
#include <llmr/renderer/painter.hpp>
|
||||
#include <llmr/renderer/fill_bucket.hpp>
|
||||
#include <llmr/map/map.hpp>
|
||||
#include <llmr/util/clip_ids.hpp>
|
||||
|
||||
using namespace llmr;
|
||||
|
||||
void Painter::prepareClippingMask() {
|
||||
gl::start_group("clipping masks");
|
||||
useProgram(plainShader->program);
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
depthMask(false);
|
||||
glDepthRange(1.0f, 1.0f);
|
||||
|
||||
coveringPlainArray.bind(*plainShader, tileStencilBuffer, BUFFER_OFFSET(0));
|
||||
}
|
||||
|
||||
void Painter::drawClippingMask(const mat4& matrix, const ClipID &clip) {
|
||||
plainShader->setMatrix(matrix);
|
||||
|
||||
GLint id = clip.mask.to_ulong();
|
||||
GLuint mask = clipMask[clip.length];
|
||||
glStencilFunc(GL_ALWAYS, id, mask);
|
||||
glStencilMask(0xFF);
|
||||
|
||||
Color background = map.getStyle().computed.background.color;
|
||||
background[0] *= map.getStyle().computed.background.opacity;
|
||||
background[1] *= map.getStyle().computed.background.opacity;
|
||||
background[2] *= map.getStyle().computed.background.opacity;
|
||||
background[3] *= map.getStyle().computed.background.opacity;
|
||||
|
||||
plainShader->setColor(background);
|
||||
|
||||
glDrawArrays(GL_TRIANGLES, 0, (GLsizei)tileStencilBuffer.index());
|
||||
}
|
||||
|
||||
void Painter::finishClippingMask() {
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
depthMask(true);
|
||||
glStencilMask(0x0);
|
||||
gl::end_group();
|
||||
}
|
||||
97
src/renderer/painter_debug.cpp
Normal file
97
src/renderer/painter_debug.cpp
Normal file
@@ -0,0 +1,97 @@
|
||||
#include <llmr/renderer/painter.hpp>
|
||||
#include <llmr/renderer/debug_bucket.hpp>
|
||||
#include <llmr/map/map.hpp>
|
||||
#include <llmr/util/string.hpp>
|
||||
|
||||
using namespace llmr;
|
||||
|
||||
void Painter::renderTileDebug(const Tile& tile) {
|
||||
gl::group group(util::sprintf<32>("debug %d/%d/%d", tile.id.z, tile.id.y, tile.id.z));
|
||||
assert(tile.data);
|
||||
if (debug) {
|
||||
prepareTile(tile);
|
||||
renderDebugText(tile.data->debugBucket);
|
||||
renderDebugFrame();
|
||||
}
|
||||
}
|
||||
|
||||
void Painter::renderDebugText(DebugBucket& bucket) {
|
||||
gl::group group("debug text");
|
||||
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
|
||||
useProgram(plainShader->program);
|
||||
plainShader->setMatrix(matrix);
|
||||
|
||||
// Draw white outline
|
||||
plainShader->setColor(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
lineWidth(4.0f * map.getState().getPixelRatio());
|
||||
bucket.drawLines(*plainShader);
|
||||
|
||||
#ifndef GL_ES_VERSION_2_0
|
||||
// Draw line "end caps"
|
||||
glPointSize(2);
|
||||
bucket.drawPoints(*plainShader);
|
||||
#endif
|
||||
|
||||
// Draw black text.
|
||||
plainShader->setColor(0.0f, 0.0f, 0.0f, 1.0f);
|
||||
lineWidth(2.0f * map.getState().getPixelRatio());
|
||||
bucket.drawLines(*plainShader);
|
||||
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
}
|
||||
|
||||
void Painter::renderDebugFrame() {
|
||||
gl::group group("debug frame");
|
||||
|
||||
// Disable depth test and don't count this towards the depth buffer,
|
||||
// but *don't* disable stencil test, as we want to clip the red tile border
|
||||
// to the tile viewport.
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
|
||||
useProgram(plainShader->program);
|
||||
plainShader->setMatrix(matrix);
|
||||
|
||||
// draw tile outline
|
||||
tileBorderArray.bind(*plainShader, tileBorderBuffer, BUFFER_OFFSET(0));
|
||||
plainShader->setColor(1.0f, 0.0f, 0.0f, 1.0f);
|
||||
lineWidth(4.0f * map.getState().getPixelRatio());
|
||||
glDrawArrays(GL_LINE_STRIP, 0, (GLsizei)tileBorderBuffer.index());
|
||||
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
}
|
||||
|
||||
void Painter::renderDebugText(const std::vector<std::string> &strings) {
|
||||
gl::group group("debug text");
|
||||
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glStencilFunc(GL_ALWAYS, 0xFF, 0xFF);
|
||||
|
||||
useProgram(plainShader->program);
|
||||
plainShader->setMatrix(nativeMatrix);
|
||||
|
||||
DebugFontBuffer debugFontBuffer;
|
||||
int line = 25;
|
||||
for (const std::string &str : strings) {
|
||||
debugFontBuffer.addText(str.c_str(), 10, line, 0.75);
|
||||
line += 20;
|
||||
}
|
||||
|
||||
// draw debug info
|
||||
VertexArrayObject debugFontArray;
|
||||
debugFontArray.bind(*plainShader, debugFontBuffer, BUFFER_OFFSET(0));
|
||||
plainShader->setColor(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
lineWidth(4.0f * map.getState().getPixelRatio());
|
||||
glDrawArrays(GL_LINES, 0, (GLsizei)debugFontBuffer.index());
|
||||
#ifndef GL_ES_VERSION_2_0
|
||||
glPointSize(2);
|
||||
glDrawArrays(GL_POINTS, 0, (GLsizei)debugFontBuffer.index());
|
||||
#endif
|
||||
plainShader->setColor(0.0f, 0.0f, 0.0f, 1.0f);
|
||||
lineWidth(2.0f * map.getState().getPixelRatio());
|
||||
glDrawArrays(GL_LINES, 0, (GLsizei)debugFontBuffer.index());
|
||||
|
||||
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
}
|
||||
143
src/renderer/painter_fill.cpp
Normal file
143
src/renderer/painter_fill.cpp
Normal file
@@ -0,0 +1,143 @@
|
||||
#include <llmr/renderer/painter.hpp>
|
||||
#include <llmr/renderer/fill_bucket.hpp>
|
||||
#include <llmr/map/map.hpp>
|
||||
#include <llmr/style/sprite.hpp>
|
||||
|
||||
using namespace llmr;
|
||||
|
||||
void Painter::renderFill(FillBucket& bucket, const std::string& layer_name, const Tile::ID& id) {
|
||||
// Abort early.
|
||||
if (!bucket.hasData()) return;
|
||||
|
||||
auto fill_properties = map.getStyle().computed.fills;
|
||||
auto fill_properties_it = fill_properties.find(layer_name);
|
||||
if (fill_properties_it == fill_properties.end()) return;
|
||||
|
||||
const FillProperties& properties = fill_properties_it->second;
|
||||
if (!properties.enabled) return;
|
||||
|
||||
Color fill_color = properties.fill_color;
|
||||
fill_color[0] *= properties.opacity;
|
||||
fill_color[1] *= properties.opacity;
|
||||
fill_color[2] *= properties.opacity;
|
||||
fill_color[3] *= properties.opacity;
|
||||
|
||||
Color stroke_color = properties.stroke_color;
|
||||
stroke_color[0] *= properties.opacity;
|
||||
stroke_color[1] *= properties.opacity;
|
||||
stroke_color[2] *= properties.opacity;
|
||||
stroke_color[3] *= properties.opacity;
|
||||
|
||||
bool outline = properties.antialias && properties.stroke_color != properties.fill_color;
|
||||
bool fringeline = properties.antialias && properties.stroke_color == properties.fill_color;
|
||||
if (fringeline) {
|
||||
outline = true;
|
||||
stroke_color = fill_color;
|
||||
}
|
||||
|
||||
translateLayer(properties.translate);
|
||||
|
||||
// Because we're drawing top-to-bottom, and we update the stencil mask
|
||||
// below, we have to draw the outline first (!)
|
||||
if (outline && pass == Translucent) {
|
||||
useProgram(outlineShader->program);
|
||||
outlineShader->setMatrix(matrix);
|
||||
lineWidth(2.0f); // This is always fixed and does not depend on the pixelRatio!
|
||||
|
||||
outlineShader->setColor(stroke_color);
|
||||
|
||||
// Draw the entire line
|
||||
outlineShader->setWorld({{
|
||||
static_cast<float>(map.getState().getFramebufferWidth()),
|
||||
static_cast<float>(map.getState().getFramebufferHeight())
|
||||
}});
|
||||
glDepthRange(strata, 1.0f);
|
||||
bucket.drawVertices(*outlineShader);
|
||||
} else if (fringeline) {
|
||||
// // We're only drawing to the first seven bits (== support a maximum of
|
||||
// // 127 overlapping polygons in one place before we get rendering errors).
|
||||
// glStencilMask(0x3F);
|
||||
// glClear(GL_STENCIL_BUFFER_BIT);
|
||||
|
||||
// // Draw front facing triangles. Wherever the 0x80 bit is 1, we are
|
||||
// // increasing the lower 7 bits by one if the triangle is a front-facing
|
||||
// // triangle. This means that all visible polygons should be in CCW
|
||||
// // orientation, while all holes (see below) are in CW orientation.
|
||||
// glStencilFunc(GL_EQUAL, 0x80, 0x80);
|
||||
|
||||
// // When we do a nonzero fill, we count the number of times a pixel is
|
||||
// // covered by a counterclockwise polygon, and subtract the number of
|
||||
// // times it is "uncovered" by a clockwise polygon.
|
||||
// glStencilOp(GL_KEEP, GL_KEEP, GL_INCR_WRAP);
|
||||
}
|
||||
|
||||
if ((fill_color[3] >= 1.0f) == (pass == Opaque)) {
|
||||
auto &sprite = map.getStyle().sprite;
|
||||
if (properties.image.size() && sprite && sprite->isLoaded()) {
|
||||
// Draw texture fill
|
||||
ImagePosition imagePos = sprite->getPosition(properties.image, true);
|
||||
|
||||
float factor = 8.0 / pow(2, map.getState().getIntegerZoom() - id.z);
|
||||
float mix = fmod(map.getState().getZoom(), 1.0);
|
||||
|
||||
std::array<float, 2> imageSize = {{
|
||||
imagePos.size.x * factor,
|
||||
imagePos.size.y *factor
|
||||
}
|
||||
};
|
||||
|
||||
std::array<float, 2> offset = {{
|
||||
(float)fmod(id.x * 4096, imageSize[0]),
|
||||
(float)fmod(id.y * 4096, imageSize[1])
|
||||
}
|
||||
};
|
||||
|
||||
useProgram(patternShader->program);
|
||||
patternShader->setMatrix(matrix);
|
||||
patternShader->setOffset(offset);
|
||||
patternShader->setPatternSize(imageSize);
|
||||
patternShader->setPatternTopLeft({{ imagePos.tl.x, imagePos.tl.y }});
|
||||
patternShader->setPatternBottomRight({{ imagePos.br.x, imagePos.br.y }});
|
||||
patternShader->setColor(fill_color);
|
||||
patternShader->setMix(mix);
|
||||
sprite->raster.bind(true);
|
||||
|
||||
// Draw the actual triangles into the color & stencil buffer.
|
||||
glDepthRange(strata + strata_epsilon, 1.0f);
|
||||
bucket.drawElements(*patternShader);
|
||||
} else {
|
||||
// Only draw the fill when it's either opaque and we're drawing opaque
|
||||
// fragments or when it's translucent and we're drawing translucent
|
||||
// fragments
|
||||
// Draw filling rectangle.
|
||||
useProgram(plainShader->program);
|
||||
plainShader->setMatrix(matrix);
|
||||
plainShader->setColor(fill_color);
|
||||
|
||||
// Draw the actual triangles into the color & stencil buffer.
|
||||
glDepthRange(strata + strata_epsilon, 1.0f);
|
||||
bucket.drawElements(*plainShader);
|
||||
}
|
||||
}
|
||||
|
||||
// Because we're drawing top-to-bottom, and we update the stencil mask
|
||||
// below, we have to draw the outline first (!)
|
||||
if (fringeline && pass == Translucent) {
|
||||
useProgram(outlineShader->program);
|
||||
outlineShader->setMatrix(matrix);
|
||||
lineWidth(2.0f); // This is always fixed and does not depend on the pixelRatio!
|
||||
|
||||
outlineShader->setColor(fill_color);
|
||||
|
||||
// Draw the entire line
|
||||
outlineShader->setWorld({{
|
||||
static_cast<float>(map.getState().getFramebufferWidth()),
|
||||
static_cast<float>(map.getState().getFramebufferHeight())
|
||||
}});
|
||||
|
||||
glDepthRange(strata + strata_epsilon, 1.0f);
|
||||
bucket.drawVertices(*outlineShader);
|
||||
}
|
||||
|
||||
translateLayer(properties.translate, true);
|
||||
}
|
||||
92
src/renderer/painter_line.cpp
Normal file
92
src/renderer/painter_line.cpp
Normal file
@@ -0,0 +1,92 @@
|
||||
#include <llmr/renderer/painter.hpp>
|
||||
#include <llmr/renderer/line_bucket.hpp>
|
||||
#include <llmr/map/map.hpp>
|
||||
|
||||
using namespace llmr;
|
||||
|
||||
void Painter::renderLine(LineBucket& bucket, const std::string& layer_name, const Tile::ID& /*id*/) {
|
||||
// Abort early.
|
||||
if (pass == Opaque) return;
|
||||
if (!bucket.hasData()) return;
|
||||
|
||||
auto line_properties = map.getStyle().computed.lines;
|
||||
auto line_properties_it = line_properties.find(layer_name);
|
||||
if (line_properties_it == line_properties.end()) return;
|
||||
|
||||
const LineProperties& properties = line_properties_it->second;
|
||||
if (!properties.enabled) return;
|
||||
|
||||
float width = properties.width;
|
||||
float offset = properties.offset / 2;
|
||||
|
||||
// These are the radii of the line. We are limiting it to 16, which will result
|
||||
// in a point size of 64 on retina.
|
||||
float inset = fmin((fmax(-1, offset - width / 2 - 0.5) + 1), 16.0f);
|
||||
float outset = fmin(offset + width / 2 + 0.5, 16.0f);
|
||||
|
||||
Color color = properties.color;
|
||||
color[0] *= properties.opacity;
|
||||
color[1] *= properties.opacity;
|
||||
color[2] *= properties.opacity;
|
||||
color[3] *= properties.opacity;
|
||||
|
||||
float dash_length = properties.dash_array[0];
|
||||
float dash_gap = properties.dash_array[1];
|
||||
|
||||
translateLayer(properties.translate);
|
||||
|
||||
glDepthRange(strata, 1.0f);
|
||||
|
||||
// We're only drawing end caps + round line joins if the line is > 2px. Otherwise, they aren't visible anyway.
|
||||
if (bucket.hasPoints() && outset > 1.0f) {
|
||||
useProgram(linejoinShader->program);
|
||||
linejoinShader->setMatrix(matrix);
|
||||
linejoinShader->setColor(color);
|
||||
linejoinShader->setWorld({{
|
||||
map.getState().getFramebufferWidth() * 0.5f,
|
||||
map.getState().getFramebufferHeight() * 0.5f
|
||||
}
|
||||
});
|
||||
linejoinShader->setLineWidth({{
|
||||
((outset - 0.25f) * map.getState().getPixelRatio()),
|
||||
((inset - 0.25f) * map.getState().getPixelRatio())
|
||||
}
|
||||
});
|
||||
|
||||
float pointSize = ceil(map.getState().getPixelRatio() * outset * 2.0);
|
||||
#if defined(GL_ES_VERSION_2_0)
|
||||
linejoinShader->setSize(pointSize);
|
||||
#else
|
||||
glPointSize(pointSize);
|
||||
#endif
|
||||
bucket.drawPoints(*linejoinShader);
|
||||
}
|
||||
|
||||
// var imagePos = properties.image && imageSprite.getPosition(properties.image);
|
||||
bool imagePos = false;
|
||||
if (imagePos) {
|
||||
// var factor = 8 / Math.pow(2, painter.transform.zoom - params.z);
|
||||
|
||||
// imageSprite.bind(gl, true);
|
||||
|
||||
// //factor = Math.pow(2, 4 - painter.transform.zoom + params.z);
|
||||
// gl.switchShader(painter.linepatternShader, painter.translatedMatrix || painter.posMatrix, painter.extrudeMatrix);
|
||||
// shader = painter.linepatternShader;
|
||||
// glUniform2fv(painter.linepatternShader.u_pattern_size, [imagePos.size[0] * factor, imagePos.size[1] ]);
|
||||
// glUniform2fv(painter.linepatternShader.u_pattern_tl, imagePos.tl);
|
||||
// glUniform2fv(painter.linepatternShader.u_pattern_br, imagePos.br);
|
||||
// glUniform1f(painter.linepatternShader.u_fade, painter.transform.z % 1.0);
|
||||
|
||||
} else {
|
||||
useProgram(lineShader->program);
|
||||
lineShader->setMatrix(matrix);
|
||||
lineShader->setExtrudeMatrix(extrudeMatrix);
|
||||
lineShader->setDashArray({{ dash_length, dash_gap }});
|
||||
lineShader->setLineWidth({{ outset, inset }});
|
||||
lineShader->setRatio(map.getState().getPixelRatio());
|
||||
lineShader->setColor(color);
|
||||
bucket.drawLines(*lineShader);
|
||||
}
|
||||
|
||||
translateLayer(properties.translate, true);
|
||||
}
|
||||
80
src/renderer/painter_point.cpp
Normal file
80
src/renderer/painter_point.cpp
Normal file
@@ -0,0 +1,80 @@
|
||||
#include <llmr/renderer/painter.hpp>
|
||||
#include <llmr/renderer/point_bucket.hpp>
|
||||
#include <llmr/map/map.hpp>
|
||||
#include <llmr/style/sprite.hpp>
|
||||
|
||||
using namespace llmr;
|
||||
|
||||
void Painter::renderPoint(PointBucket& bucket, const std::string& layer_name, const Tile::ID& /*id&*/) {
|
||||
// Abort early.
|
||||
if (!bucket.hasData()) return;
|
||||
if (pass == Opaque) return;
|
||||
|
||||
auto point_properties = map.getStyle().computed.points;
|
||||
auto point_properties_it = point_properties.find(layer_name);
|
||||
if (point_properties_it == point_properties.end()) return;
|
||||
|
||||
const PointProperties& properties = point_properties_it->second;
|
||||
if (!properties.enabled) return;
|
||||
|
||||
translateLayer(properties.translate);
|
||||
|
||||
Color color = properties.color;
|
||||
color[0] *= properties.opacity;
|
||||
color[1] *= properties.opacity;
|
||||
color[2] *= properties.opacity;
|
||||
color[3] *= properties.opacity;
|
||||
|
||||
auto &sprite = map.getStyle().sprite;
|
||||
ImagePosition imagePos;
|
||||
|
||||
if (properties.image.length() && sprite && sprite->isLoaded()) {
|
||||
std::string sized_image = properties.image;
|
||||
if (properties.size) {
|
||||
sized_image.append("-");
|
||||
sized_image.append(std::to_string(static_cast<int>(std::round(properties.size))));
|
||||
}
|
||||
imagePos = sprite->getPosition(sized_image, false);
|
||||
}
|
||||
|
||||
if (!imagePos.size) {
|
||||
useProgram(dotShader->program);
|
||||
dotShader->setMatrix(matrix);
|
||||
dotShader->setColor(color);
|
||||
|
||||
const float pointSize = (properties.radius ? properties.radius * 2 : 8) * map.getState().getPixelRatio();
|
||||
#if defined(GL_ES_VERSION_2_0)
|
||||
dotShader->setSize(pointSize);
|
||||
#else
|
||||
glPointSize(pointSize);
|
||||
glEnable(GL_POINT_SPRITE);
|
||||
#endif
|
||||
dotShader->setBlur((properties.blur ? properties.blur : 1.5) / pointSize);
|
||||
|
||||
glDepthRange(strata, 1.0f);
|
||||
bucket.drawPoints(*dotShader);
|
||||
} else {
|
||||
useProgram(pointShader->program);
|
||||
pointShader->setMatrix(matrix);
|
||||
pointShader->setColor(color);
|
||||
|
||||
pointShader->setImage(0);
|
||||
pointShader->setPointTopLeft({{ imagePos.tl.x, imagePos.tl.y }});
|
||||
pointShader->setPointBottomRight({{ imagePos.br.x, imagePos.br.y }});
|
||||
|
||||
sprite->raster.bind(map.getState().isChanging());
|
||||
|
||||
const float pointSize = (properties.size ? properties.size : (imagePos.size.x / map.getState().getPixelRatio())) * 1.4142135623730951 * map.getState().getPixelRatio();
|
||||
#if defined(GL_ES_VERSION_2_0)
|
||||
pointShader->setSize(pointSize);
|
||||
#else
|
||||
glPointSize(pointSize);
|
||||
glEnable(GL_POINT_SPRITE);
|
||||
#endif
|
||||
|
||||
glDepthRange(strata, 1.0f);
|
||||
bucket.drawPoints(*pointShader);
|
||||
}
|
||||
|
||||
translateLayer(properties.translate, true);
|
||||
}
|
||||
28
src/renderer/painter_raster.cpp
Normal file
28
src/renderer/painter_raster.cpp
Normal file
@@ -0,0 +1,28 @@
|
||||
#include <llmr/renderer/painter.hpp>
|
||||
#include <llmr/renderer/raster_bucket.hpp>
|
||||
#include <llmr/map/map.hpp>
|
||||
|
||||
using namespace llmr;
|
||||
|
||||
void Painter::renderRaster(RasterBucket& bucket, const std::string& layer_name, const Tile::ID& /*id*/) {
|
||||
if (pass == Translucent) return;
|
||||
|
||||
auto raster_properties = map.getStyle().computed.rasters;
|
||||
auto raster_properties_it = raster_properties.find(layer_name);
|
||||
if (raster_properties_it == raster_properties.end()) return;
|
||||
|
||||
const RasterProperties& properties = raster_properties_it->second;
|
||||
if (!properties.enabled) return;
|
||||
|
||||
depthMask(false);
|
||||
|
||||
useProgram(rasterShader->program);
|
||||
rasterShader->setMatrix(matrix);
|
||||
rasterShader->setOpacity(1);
|
||||
// rasterShader->setOpacity(properties.opacity * tile_data->raster->opacity);
|
||||
|
||||
glDepthRange(strata + strata_epsilon, 1.0f);
|
||||
bucket.drawRaster(*rasterShader, tileStencilBuffer, coveringRasterArray);
|
||||
|
||||
depthMask(true);
|
||||
}
|
||||
114
src/renderer/painter_text.cpp
Normal file
114
src/renderer/painter_text.cpp
Normal file
@@ -0,0 +1,114 @@
|
||||
#include <llmr/renderer/painter.hpp>
|
||||
#include <llmr/renderer/text_bucket.hpp>
|
||||
#include <llmr/map/map.hpp>
|
||||
|
||||
using namespace llmr;
|
||||
|
||||
void Painter::renderText(TextBucket& bucket, const std::string& layer_name, const Tile::ID& /*id*/) {
|
||||
// Abort early.
|
||||
if (pass == Opaque) return;
|
||||
if (!bucket.hasData()) return;
|
||||
|
||||
auto text_properties = map.getStyle().computed.texts;
|
||||
auto text_properties_it = text_properties.find(layer_name);
|
||||
if (text_properties_it == text_properties.end()) return;
|
||||
|
||||
const TextProperties& properties = text_properties_it->second;
|
||||
if (!properties.enabled) return;
|
||||
|
||||
translateLayer(properties.translate);
|
||||
|
||||
mat4 exMatrix;
|
||||
matrix::copy(exMatrix, projMatrix);
|
||||
if (bucket.geom_desc.path == TextPathType::Curve) {
|
||||
matrix::rotate_z(exMatrix, exMatrix, map.getState().getAngle());
|
||||
}
|
||||
|
||||
const float rotate = properties.rotate;
|
||||
if (rotate != 0.0f) {
|
||||
matrix::rotate_z(exMatrix, exMatrix, rotate);
|
||||
}
|
||||
|
||||
// If layerStyle.size > bucket.info.fontSize then labels may collide
|
||||
float fontSize = fmin(properties.size, bucket.geom_desc.font_size);
|
||||
matrix::scale(exMatrix, exMatrix, fontSize / 24.0f, fontSize / 24.0f, 1.0f);
|
||||
|
||||
useProgram(textShader->program);
|
||||
textShader->setMatrix(matrix);
|
||||
textShader->setExtrudeMatrix(exMatrix);
|
||||
|
||||
map.getGlyphAtlas().bind();
|
||||
textShader->setTextureSize({{static_cast<float>(map.getGlyphAtlas().width),
|
||||
static_cast<float>(map.getGlyphAtlas().height)}});
|
||||
|
||||
textShader->setGamma(2.5f / fontSize / map.getState().getPixelRatio());
|
||||
|
||||
// Convert the -pi..pi to an int8 range.
|
||||
float angle = round((map.getState().getAngle() + rotate) / M_PI * 128);
|
||||
|
||||
// adjust min/max zooms for variable font sies
|
||||
float zoomAdjust = log(fontSize / bucket.geom_desc.font_size) / log(2);
|
||||
|
||||
textShader->setAngle((int32_t)(angle + 256) % 256);
|
||||
textShader->setFlip(bucket.geom_desc.path == TextPathType::Curve ? 1 : 0);
|
||||
textShader->setZoom((map.getState().getNormalizedZoom() - zoomAdjust) * 10); // current zoom level
|
||||
|
||||
// Label fading
|
||||
const time duration = 300_milliseconds;
|
||||
const time currentTime = util::now();
|
||||
|
||||
std::deque<FrameSnapshot> &history = frameHistory.history;
|
||||
|
||||
// Remove frames until only one is outside the duration, or until there are only three
|
||||
while (history.size() > 3 && history[1].timestamp + duration < currentTime) {
|
||||
history.pop_front();
|
||||
}
|
||||
|
||||
if (history[1].timestamp + duration < currentTime) {
|
||||
history[0].z = history[1].z;
|
||||
}
|
||||
|
||||
size_t frameLen = history.size();
|
||||
assert("there should never be less than three frames in the history" && frameLen >= 3);
|
||||
|
||||
// Find the range of zoom levels we want to fade between
|
||||
float startingZ = history.front().z;
|
||||
const FrameSnapshot lastFrame = history.back();
|
||||
float endingZ = lastFrame.z;
|
||||
float lowZ = fmin(startingZ, endingZ);
|
||||
float highZ = fmax(startingZ, endingZ);
|
||||
|
||||
// Calculate the speed of zooming, and how far it would zoom in terms of zoom levels in one duration
|
||||
float zoomDiff = endingZ - history[1].z,
|
||||
timeDiff = lastFrame.timestamp - history[1].timestamp;
|
||||
if (timeDiff > duration) timeDiff = 1;
|
||||
float fadedist = zoomDiff / (timeDiff / duration);
|
||||
|
||||
if (isnan(fadedist)) fprintf(stderr, "fadedist should never be NaN\n");
|
||||
|
||||
// At end of a zoom when the zoom stops changing continue pretending to zoom at that speed
|
||||
// bump is how much farther it would have been if it had continued zooming at the same rate
|
||||
float bump = (currentTime - lastFrame.timestamp) / duration * fadedist;
|
||||
|
||||
textShader->setFadeDist(fadedist * 10);
|
||||
textShader->setMinFadeZoom(floor(lowZ * 10));
|
||||
textShader->setMaxFadeZoom(floor(highZ * 10));
|
||||
textShader->setFadeZoom((map.getState().getZoom() + bump) * 10);
|
||||
|
||||
// We're drawing in the translucent pass which is bottom-to-top, so we need
|
||||
// to draw the halo first.
|
||||
if (properties.halo[3] > 0.0f) {
|
||||
textShader->setColor(properties.halo);
|
||||
textShader->setBuffer(properties.haloRadius);
|
||||
glDepthRange(strata, 1.0f);
|
||||
bucket.drawGlyphs(*textShader);
|
||||
}
|
||||
|
||||
// Then, we draw the text over the halo
|
||||
textShader->setColor(properties.color);
|
||||
textShader->setBuffer((256.0f - 64.0f) / 256.0f);
|
||||
glDepthRange(strata + strata_epsilon, 1.0f);
|
||||
bucket.drawGlyphs(*textShader);
|
||||
|
||||
translateLayer(properties.translate, true);
|
||||
}
|
||||
@@ -41,7 +41,7 @@ void PointBucket::render(Painter& painter, const std::string& layer_name, const
|
||||
painter.renderPoint(*this, layer_name, id);
|
||||
}
|
||||
|
||||
bool PointBucket::hasPoints() const {
|
||||
bool PointBucket::hasData() const {
|
||||
return vertex_end > 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,12 +3,25 @@
|
||||
|
||||
using namespace llmr;
|
||||
|
||||
RasterBucket::RasterBucket(const BucketDescription &) {
|
||||
RasterBucket::RasterBucket(Texturepool &texturepool)
|
||||
: raster(texturepool) {
|
||||
}
|
||||
|
||||
void RasterBucket::render(Painter &, const std::string &, const Tile::ID &) {
|
||||
// The painter renders rasters directly. This function is a no-op
|
||||
// and exists just to satisfy the abstract superclass requirements.
|
||||
// That way, we can avoid having raster buckets pass around
|
||||
// references to tile data and/or rasters directly.
|
||||
void RasterBucket::render(Painter &painter, const std::string &layer_name, const Tile::ID &id) {
|
||||
painter.renderRaster(*this, layer_name, id);
|
||||
}
|
||||
|
||||
bool RasterBucket::setImage(const std::string &data) {
|
||||
return raster.load(data);
|
||||
}
|
||||
|
||||
void RasterBucket::drawRaster(RasterShader& shader, VertexBuffer &vertices, VertexArrayObject &array) {
|
||||
raster.bind(true);
|
||||
shader.setImage(0);
|
||||
array.bind(shader, vertices, BUFFER_OFFSET(0));
|
||||
glDrawArrays(GL_TRIANGLES, 0, (GLsizei)vertices.index());
|
||||
}
|
||||
|
||||
bool RasterBucket::hasData() const {
|
||||
return raster.isLoaded();
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
#include <llmr/geometry/geometry.hpp>
|
||||
|
||||
#include <llmr/renderer/painter.hpp>
|
||||
#include <llmr/shader/text_shader.hpp>
|
||||
#include <llmr/style/style.hpp>
|
||||
#include <llmr/map/vector_tile.hpp>
|
||||
#include <llmr/text/placement.hpp>
|
||||
@@ -14,8 +13,6 @@
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#define BUFFER_OFFSET(i) ((char *) nullptr + (i))
|
||||
|
||||
#include <cassert>
|
||||
|
||||
using namespace llmr;
|
||||
@@ -148,7 +145,9 @@ void TextBucket::render(Painter &painter, const std::string &layer_name,
|
||||
painter.renderText(*this, layer_name, id);
|
||||
}
|
||||
|
||||
bool TextBucket::empty() const { return triangleGroups.empty(); }
|
||||
bool TextBucket::hasData() const {
|
||||
return !triangleGroups.empty();
|
||||
}
|
||||
|
||||
void TextBucket::drawGlyphs(TextShader &shader) {
|
||||
char *vertex_index = BUFFER_OFFSET(vertex_start * vertexBuffer.itemSize);
|
||||
|
||||
@@ -7,40 +7,40 @@ using namespace llmr;
|
||||
|
||||
const shader_source llmr::shaders[SHADER_COUNT] = {
|
||||
{
|
||||
"#version 120\nattribute vec2 a_pos;\n\nuniform mat4 u_matrix;\nuniform float u_size;\n\nvoid main(void) {\n gl_Position = u_matrix * vec4(a_pos, 0, 1);\n gl_PointSize = u_size;\n}\n",
|
||||
"#version 120\nuniform vec4 u_color;\nuniform float u_blur;\n\nvoid main() {\n\tfloat dist = length(gl_PointCoord - 0.5);\n\tfloat t = smoothstep(0.5, 0.5 - u_blur, dist);\n\n gl_FragColor = u_color * t;\n}\n",
|
||||
"#version 120\nattribute vec2 a_pos;\nuniform mat4 u_matrix;\nuniform float u_size;\nvoid main ()\n{\n vec4 tmpvar_1;\n tmpvar_1.zw = vec2(0.0, 1.0);\n tmpvar_1.xy = a_pos;\n gl_Position = (u_matrix * tmpvar_1);\n gl_PointSize = u_size;\n}\n\n",
|
||||
"#version 120\nuniform vec4 u_color;\nuniform float u_blur;\nvoid main ()\n{\n vec2 x_1;\n x_1 = (gl_PointCoord - 0.5);\n float tmpvar_2;\n tmpvar_2 = clamp (((\n sqrt(dot (x_1, x_1))\n - 0.5) / (\n (0.5 - u_blur)\n - 0.5)), 0.0, 1.0);\n gl_FragColor = (u_color * (tmpvar_2 * (tmpvar_2 * \n (3.0 - (2.0 * tmpvar_2))\n )));\n}\n\n",
|
||||
},
|
||||
{
|
||||
"#version 120\n// floor(127 / 2) == 63.0\n// the maximum allowed miter limit is 2.0 at the moment. the extrude normal is\n// stored in a byte (-128..127). we scale regular normals up to length 63, but\n// there are also \"special\" normals that have a bigger length (of up to 126 in\n// this case).\n// #define scale 63.0\n#define scale 0.015873016\n\nattribute vec2 a_pos;\nattribute vec2 a_extrude;\nattribute float a_linesofar;\n\n// posmatrix is for the vertex position, exmatrix is for rotating and projecting\n// the extrusion vector.\nuniform mat4 u_matrix;\nuniform mat4 u_exmatrix;\n\n// shared\nuniform float u_ratio;\nuniform vec2 u_linewidth;\nuniform vec4 u_color;\n\nvarying vec2 v_normal;\nvarying float v_linesofar;\n\nvoid main() {\n // We store the texture normals in the most insignificant bit\n // transform y so that 0 => -1 and 1 => 1\n // In the texture normal, x is 0 if the normal points straight up/down and 1 if it's a round cap\n // y is 1 if the normal points up, and -1 if it points down\n vec2 normal = mod(a_pos, 2.0);\n normal.y = sign(normal.y - 0.5);\n v_normal = normal;\n\n // Scale the extrusion vector down to a normal and then up by the line width\n // of this vertex.\n vec4 dist = vec4(u_linewidth.s * a_extrude * scale, 0.0, 0.0);\n\n // Remove the texture normal bit of the position before scaling it with the\n // model/view matrix. Add the extrusion vector *after* the model/view matrix\n // because we're extruding the line in pixel space, regardless of the current\n // tile's zoom level.\n gl_Position = u_matrix * vec4(floor(a_pos * 0.5), 0.0, 1.0) + u_exmatrix * dist;\n v_linesofar = a_linesofar * u_ratio;\n}",
|
||||
"#version 120\nuniform vec2 u_linewidth;\nuniform vec4 u_color;\n\nuniform vec2 u_dasharray;\n\nvarying vec2 v_normal;\nvarying float v_linesofar;\n\nvoid main() {\n // Calculate the distance of the pixel from the line in pixels.\n float dist = length(v_normal) * u_linewidth.s;\n\n // Calculate the antialiasing fade factor. This is either when fading in\n // the line in case of an offset line (v_linewidth.t) or when fading out\n // (v_linewidth.s)\n float alpha = clamp(min(dist - (u_linewidth.t - 1.0), u_linewidth.s - dist), 0.0, 1.0);\n\n // Calculate the antialiasing fade factor based on distance to the dash.\n // Only affects alpha when line is dashed\n float pos = mod(v_linesofar, u_dasharray.x + u_dasharray.y);\n alpha *= max(step(0.0, -u_dasharray.y), clamp(min(pos, u_dasharray.x - pos), 0.0, 1.0));\n\n gl_FragColor = u_color * alpha;\n}",
|
||||
"#version 120\nattribute vec2 a_pos;\nattribute vec2 a_extrude;\nattribute float a_linesofar;\nuniform mat4 u_matrix;\nuniform mat4 u_exmatrix;\nuniform float u_ratio;\nuniform vec2 u_linewidth;\nvarying vec2 v_normal;\nvarying float v_linesofar;\nvoid main ()\n{\n vec2 normal_1;\n vec2 tmpvar_2;\n tmpvar_2 = (vec2(mod (a_pos, 2.0)));\n normal_1.x = tmpvar_2.x;\n normal_1.y = sign((tmpvar_2.y - 0.5));\n v_normal = normal_1;\n vec4 tmpvar_3;\n tmpvar_3.zw = vec2(0.0, 0.0);\n tmpvar_3.xy = ((u_linewidth.x * a_extrude) * 0.015873);\n vec4 tmpvar_4;\n tmpvar_4.zw = vec2(0.0, 1.0);\n tmpvar_4.xy = floor((a_pos * 0.5));\n gl_Position = ((u_matrix * tmpvar_4) + (u_exmatrix * tmpvar_3));\n v_linesofar = (a_linesofar * u_ratio);\n}\n\n",
|
||||
"#version 120\nuniform vec2 u_linewidth;\nuniform vec4 u_color;\nuniform vec2 u_dasharray;\nvarying vec2 v_normal;\nvarying float v_linesofar;\nvoid main ()\n{\n float tmpvar_1;\n tmpvar_1 = (sqrt(dot (v_normal, v_normal)) * u_linewidth.x);\n float tmpvar_2;\n tmpvar_2 = (float(mod (v_linesofar, (u_dasharray.x + u_dasharray.y))));\n gl_FragColor = (u_color * (clamp (\n min ((tmpvar_1 - (u_linewidth.y - 1.0)), (u_linewidth.x - tmpvar_1))\n , 0.0, 1.0) * max (\n float((-(u_dasharray.y) >= 0.0))\n , \n clamp (min (tmpvar_2, (u_dasharray.x - tmpvar_2)), 0.0, 1.0)\n )));\n}\n\n",
|
||||
},
|
||||
{
|
||||
"#version 120\nattribute vec2 a_pos;\n\nuniform mat4 u_matrix;\nuniform vec2 u_world;\nuniform float u_size;\n\nvarying vec2 v_pos;\n\nvoid main() {\n gl_Position = u_matrix * vec4(floor(a_pos / 2.0), 0.0, 1.0);\n v_pos = (gl_Position.xy + 1.0) * u_world;\n gl_PointSize = u_size;\n}\n",
|
||||
"#version 120\nuniform vec4 u_color;\nuniform vec2 u_linewidth;\n\nvarying vec2 v_pos;\n\nvoid main() {\n float dist = length(v_pos - gl_FragCoord.xy);\n\n // Calculate the antialiasing fade factor. This is either when fading in\n // the line in case of an offset line (v_linewidth.t) or when fading out\n // (v_linewidth.s)\n float alpha = clamp(min(dist - (u_linewidth.t - 1.0), u_linewidth.s - dist), 0.0, 1.0);\n gl_FragColor = u_color * alpha;\n}\n",
|
||||
"#version 120\nattribute vec2 a_pos;\nuniform mat4 u_matrix;\nuniform vec2 u_world;\nuniform float u_size;\nvarying vec2 v_pos;\nvoid main ()\n{\n vec4 tmpvar_1;\n tmpvar_1.zw = vec2(0.0, 1.0);\n tmpvar_1.xy = floor((a_pos / 2.0));\n vec4 tmpvar_2;\n tmpvar_2 = (u_matrix * tmpvar_1);\n gl_Position = tmpvar_2;\n v_pos = ((tmpvar_2.xy + 1.0) * u_world);\n gl_PointSize = u_size;\n}\n\n",
|
||||
"#version 120\nuniform vec4 u_color;\nuniform vec2 u_linewidth;\nvarying vec2 v_pos;\nvoid main ()\n{\n float tmpvar_1;\n vec2 x_2;\n x_2 = (v_pos - gl_FragCoord.xy);\n tmpvar_1 = sqrt(dot (x_2, x_2));\n gl_FragColor = (u_color * clamp (min (\n (tmpvar_1 - (u_linewidth.y - 1.0))\n , \n (u_linewidth.x - tmpvar_1)\n ), 0.0, 1.0));\n}\n\n",
|
||||
},
|
||||
{
|
||||
"#version 120\nattribute vec2 a_pos;\nuniform mat4 u_matrix;\nuniform vec2 u_world;\n\nvarying vec2 v_pos;\n\nvoid main() {\n gl_Position = u_matrix * vec4(a_pos, 0, 1);\n v_pos = (gl_Position.xy + 1.0) / 2.0 * u_world;\n}\n",
|
||||
"#version 120\nuniform vec4 u_color;\n\nvarying vec2 v_pos;\n\nvoid main() {\n float dist = length(v_pos - gl_FragCoord.xy);\n float alpha = smoothstep(1.0, 0.0, dist);\n gl_FragColor = u_color * alpha;\n}\n",
|
||||
"#version 120\nattribute vec2 a_pos;\nuniform mat4 u_matrix;\nuniform vec2 u_world;\nvarying vec2 v_pos;\nvoid main ()\n{\n vec4 tmpvar_1;\n tmpvar_1.zw = vec2(0.0, 1.0);\n tmpvar_1.xy = a_pos;\n vec4 tmpvar_2;\n tmpvar_2 = (u_matrix * tmpvar_1);\n gl_Position = tmpvar_2;\n v_pos = (((tmpvar_2.xy + 1.0) / 2.0) * u_world);\n}\n\n",
|
||||
"#version 120\nuniform vec4 u_color;\nvarying vec2 v_pos;\nvoid main ()\n{\n vec2 x_1;\n x_1 = (v_pos - gl_FragCoord.xy);\n float tmpvar_2;\n tmpvar_2 = clamp (((\n sqrt(dot (x_1, x_1))\n - 1.0) / -1.0), 0.0, 1.0);\n gl_FragColor = (u_color * (tmpvar_2 * (tmpvar_2 * \n (3.0 - (2.0 * tmpvar_2))\n )));\n}\n\n",
|
||||
},
|
||||
{
|
||||
"#version 120\nuniform mat4 u_matrix;\n\nattribute vec2 a_pos;\n\nvarying vec2 v_pos;\n\nvoid main() {\n v_pos = a_pos;\n gl_Position = u_matrix * vec4(a_pos, 0, 1);\n}\n",
|
||||
"#version 120\nuniform vec4 u_color;\n\nuniform vec2 u_offset;\nuniform vec2 u_pattern_size;\nuniform vec2 u_pattern_tl;\nuniform vec2 u_pattern_br;\nuniform float u_mix;\n\n\nuniform sampler2D u_image;\n\nvarying vec2 v_pos;\n\nvoid main() {\n\n vec2 imagecoord = mod((v_pos + u_offset) / u_pattern_size, 1.0);\n vec2 pos = mix(u_pattern_tl, u_pattern_br, imagecoord);\n vec4 color1 = texture2D(u_image, pos);\n\n vec2 imagecoord2 = mod(imagecoord * 2.0, 1.0);\n vec2 pos2 = mix(u_pattern_tl, u_pattern_br, imagecoord2);\n vec4 color2 = texture2D(u_image, pos2);\n\n vec4 color = mix(color1, color2, u_mix);\n gl_FragColor = color + u_color * (1.0 - color.a);\n}\n",
|
||||
"#version 120\nuniform mat4 u_matrix;\nattribute vec2 a_pos;\nvarying vec2 v_pos;\nvoid main ()\n{\n v_pos = a_pos;\n vec4 tmpvar_1;\n tmpvar_1.zw = vec2(0.0, 1.0);\n tmpvar_1.xy = a_pos;\n gl_Position = (u_matrix * tmpvar_1);\n}\n\n",
|
||||
"#version 120\nuniform vec4 u_color;\nuniform vec2 u_offset;\nuniform vec2 u_pattern_size;\nuniform vec2 u_pattern_tl;\nuniform vec2 u_pattern_br;\nuniform float u_mix;\nuniform sampler2D u_image;\nvarying vec2 v_pos;\nvoid main ()\n{\n vec2 tmpvar_1;\n tmpvar_1 = (vec2(mod (((v_pos + u_offset) / u_pattern_size), 1.0)));\n vec4 tmpvar_2;\n tmpvar_2 = mix (texture2D (u_image, mix (u_pattern_tl, u_pattern_br, tmpvar_1)), texture2D (u_image, mix (u_pattern_tl, u_pattern_br, (vec2(mod (\n (tmpvar_1 * 2.0)\n , 1.0))))), u_mix);\n gl_FragColor = (tmpvar_2 + (u_color * (1.0 - tmpvar_2.w)));\n}\n\n",
|
||||
},
|
||||
{
|
||||
"#version 120\nattribute vec2 a_pos;\n\nuniform mat4 u_matrix;\n\nvoid main() {\n gl_Position = u_matrix * vec4(a_pos, 0, 1);\n}\n",
|
||||
"#version 120\nuniform vec4 u_color;\n\nvoid main() {\n gl_FragColor = u_color;\n}\n",
|
||||
"#version 120\nattribute vec2 a_pos;\nuniform mat4 u_matrix;\nvoid main ()\n{\n vec4 tmpvar_1;\n tmpvar_1.zw = vec2(0.0, 1.0);\n tmpvar_1.xy = a_pos;\n gl_Position = (u_matrix * tmpvar_1);\n}\n\n",
|
||||
"#version 120\nuniform vec4 u_color;\nvoid main ()\n{\n gl_FragColor = u_color;\n}\n\n",
|
||||
},
|
||||
{
|
||||
"#version 120\nattribute vec2 a_pos;\n\nuniform mat4 u_matrix;\nuniform float u_size;\n\nvoid main() {\n gl_Position = u_matrix * vec4(a_pos, 0, 1);\n gl_PointSize = u_size;\n}\n",
|
||||
"#version 120\n#define root2 1.4142135623730951\n\nuniform sampler2D u_image;\nuniform vec2 u_tl;\nuniform vec2 u_br;\nuniform vec4 u_color;\n\nuniform vec2 pos;\nuniform float inbounds;\nuniform vec4 color;\n\nvoid main() {\n vec2 pos = (gl_PointCoord * 2.0 - 1.0) * root2 / 2.0 + 0.5;\n\n float inbounds = step(0.0, pos.x) * step(0.0, pos.y) *\n (1.0 - step(1.0, pos.x)) * (1.0 - step(1.0, pos.y));\n\n vec4 color = texture2D(u_image, mix(u_tl, u_br, pos)) * inbounds;\n\n gl_FragColor = color * u_color;\n}\n",
|
||||
"#version 120\nattribute vec2 a_pos;\nuniform mat4 u_matrix;\nuniform float u_size;\nvoid main ()\n{\n vec4 tmpvar_1;\n tmpvar_1.zw = vec2(0.0, 1.0);\n tmpvar_1.xy = a_pos;\n gl_Position = (u_matrix * tmpvar_1);\n gl_PointSize = u_size;\n}\n\n",
|
||||
"#version 120\nuniform sampler2D u_image;\nuniform vec2 u_tl;\nuniform vec2 u_br;\nuniform vec4 u_color;\nvoid main ()\n{\n vec2 tmpvar_1;\n tmpvar_1 = (((\n ((gl_PointCoord * 2.0) - 1.0)\n * 1.41421) / 2.0) + 0.5);\n gl_FragColor = ((texture2D (u_image, mix (u_tl, u_br, tmpvar_1)) * (\n ((float((tmpvar_1.x >= 0.0)) * float((tmpvar_1.y >= 0.0))) * (1.0 - float((tmpvar_1.x >= 1.0))))\n * \n (1.0 - float((tmpvar_1.y >= 1.0)))\n )) * u_color);\n}\n\n",
|
||||
},
|
||||
{
|
||||
"#version 120\nuniform mat4 u_matrix;\n\nattribute vec2 a_pos;\n\nvarying vec2 v_pos;\n\nvoid main() {\n gl_Position = u_matrix * vec4(a_pos, 0, 1);\n v_pos = a_pos / 4096.0;\n}\n",
|
||||
"#version 120\nuniform sampler2D u_image;\nuniform float u_opacity;\n\nvarying vec2 v_pos;\n\nvoid main() {\n gl_FragColor = texture2D(u_image, v_pos) * u_opacity;\n}\n",
|
||||
"#version 120\nuniform mat4 u_matrix;\nattribute vec2 a_pos;\nvarying vec2 v_pos;\nvoid main ()\n{\n vec4 tmpvar_1;\n tmpvar_1.zw = vec2(0.0, 1.0);\n tmpvar_1.xy = a_pos;\n gl_Position = (u_matrix * tmpvar_1);\n v_pos = (a_pos / 4096.0);\n}\n\n",
|
||||
"#version 120\nuniform sampler2D u_image;\nuniform float u_opacity;\nvarying vec2 v_pos;\nvoid main ()\n{\n gl_FragColor = (texture2D (u_image, v_pos) * u_opacity);\n}\n\n",
|
||||
},
|
||||
{
|
||||
"#version 120\nattribute vec2 a_pos;\nattribute vec2 a_offset;\nattribute vec4 a_data1;\nattribute vec4 a_data2;\n\n\n// posmatrix is for the vertex position, exmatrix is for rotating and projecting\n// the extrusion vector.\nuniform mat4 u_matrix;\nuniform mat4 u_exmatrix;\nuniform float u_angle;\nuniform float u_zoom;\nuniform float u_flip;\nuniform float u_fadedist;\nuniform float u_minfadezoom;\nuniform float u_maxfadezoom;\nuniform float u_fadezoom;\n\nuniform vec2 u_texsize;\n\nvarying vec2 v_tex;\nvarying float v_alpha;\n\nvoid main() {\n vec2 a_tex = a_data1.xy;\n float a_labelminzoom = a_data1[2];\n float a_angle = a_data1[3];\n vec2 a_zoom = a_data2.st;\n vec2 a_range = a_data2.pq;\n\n float rev = 0.0;\n\n // u_angle is angle of the map, -128..128 representing 0..2PI\n // a_angle is angle of the label, 0..256 representing 0..2PI, where 0 is horizontal text\n float rotated = mod(a_angle + u_angle, 256.0);\n // if the label rotates with the map, and if the rotated label is upside down, hide it\n if (u_flip > 0.0 && rotated >= 64.0 && rotated < 192.0) rev = 1.0;\n\n // If the label should be invisible, we move the vertex outside\n // of the view plane so that the triangle gets clipped. This makes it easier\n // for us to create degenerate triangle strips.\n // u_zoom is the current zoom level adjusted for the change in font size\n float z = 2.0 - step(a_zoom[0], u_zoom) - (1.0 - step(a_zoom[1], u_zoom)) + rev;\n\n // fade out labels\n float alpha = clamp((u_fadezoom - a_labelminzoom) / u_fadedist, 0.0, 1.0);\n\n if (u_fadedist >= 0.0) {\n v_alpha = alpha;\n } else {\n v_alpha = 1.0 - alpha;\n }\n if (u_maxfadezoom < a_labelminzoom) {\n v_alpha = 0.0;\n }\n if (u_minfadezoom >= a_labelminzoom) {\n v_alpha = 1.0;\n }\n\n // if label has been faded out, clip it\n z += step(v_alpha, 0.0);\n\n // all the angles are 0..256 representing 0..2PI\n // hide if (angle >= a_rangeend && angle < rangestart)\n z += (1.0 - step(a_range[0], u_angle)) * step(a_range[1], u_angle);\n\n gl_Position = u_matrix * vec4(a_pos, 0, 1) + u_exmatrix * vec4(a_offset / 64.0, z, 0);\n v_tex = a_tex * 4.0 / u_texsize;\n}\n",
|
||||
"#version 120\nuniform sampler2D u_texture;\nuniform vec4 u_color;\nuniform float u_buffer;\nuniform float u_gamma;\n\nvarying vec2 v_tex;\nvarying float v_alpha;\n\nvoid main() {\n float dist = texture2D(u_texture, v_tex).a;\n float alpha = smoothstep(u_buffer - u_gamma, u_buffer + u_gamma, dist) * v_alpha;\n gl_FragColor = u_color * alpha;\n}\n",
|
||||
"#version 120\nattribute vec2 a_pos;\nattribute vec2 a_offset;\nattribute vec4 a_data1;\nattribute vec4 a_data2;\nuniform mat4 u_matrix;\nuniform mat4 u_exmatrix;\nuniform float u_angle;\nuniform float u_zoom;\nuniform float u_flip;\nuniform float u_fadedist;\nuniform float u_minfadezoom;\nuniform float u_maxfadezoom;\nuniform float u_fadezoom;\nuniform vec2 u_texsize;\nvarying vec2 v_tex;\nvarying float v_alpha;\nvoid main ()\n{\n float rev_1;\n rev_1 = 0.0;\n float tmpvar_2;\n tmpvar_2 = (float(mod ((a_data1.w + u_angle), 256.0)));\n if ((((u_flip > 0.0) && (tmpvar_2 >= 64.0)) && (tmpvar_2 < 192.0))) {\n rev_1 = 1.0;\n };\n float tmpvar_3;\n tmpvar_3 = (((2.0 - \n float((u_zoom >= a_data2.x))\n ) - (1.0 - \n float((u_zoom >= a_data2.y))\n )) + rev_1);\n float tmpvar_4;\n tmpvar_4 = clamp (((u_fadezoom - a_data1.z) / u_fadedist), 0.0, 1.0);\n if ((u_fadedist >= 0.0)) {\n v_alpha = tmpvar_4;\n } else {\n v_alpha = (1.0 - tmpvar_4);\n };\n if ((u_maxfadezoom < a_data1.z)) {\n v_alpha = 0.0;\n };\n if ((u_minfadezoom >= a_data1.z)) {\n v_alpha = 1.0;\n };\n vec4 tmpvar_5;\n tmpvar_5.zw = vec2(0.0, 1.0);\n tmpvar_5.xy = a_pos;\n vec4 tmpvar_6;\n tmpvar_6.w = 0.0;\n tmpvar_6.xy = (a_offset / 64.0);\n tmpvar_6.z = ((tmpvar_3 + float(\n (0.0 >= v_alpha)\n )) + ((1.0 - \n float((u_angle >= a_data2.z))\n ) * float(\n (u_angle >= a_data2.w)\n )));\n gl_Position = ((u_matrix * tmpvar_5) + (u_exmatrix * tmpvar_6));\n v_tex = ((a_data1.xy * 4.0) / u_texsize);\n}\n\n",
|
||||
"#version 120\nuniform sampler2D u_texture;\nuniform vec4 u_color;\nuniform float u_buffer;\nuniform float u_gamma;\nvarying vec2 v_tex;\nvarying float v_alpha;\nvoid main ()\n{\n float edge0_1;\n edge0_1 = (u_buffer - u_gamma);\n float tmpvar_2;\n tmpvar_2 = clamp (((texture2D (u_texture, v_tex).w - edge0_1) / (\n (u_buffer + u_gamma)\n - edge0_1)), 0.0, 1.0);\n gl_FragColor = (u_color * ((tmpvar_2 * \n (tmpvar_2 * (3.0 - (2.0 * tmpvar_2)))\n ) * v_alpha));\n}\n\n",
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
@@ -7,40 +7,40 @@ using namespace llmr;
|
||||
|
||||
const shader_source llmr::shaders[SHADER_COUNT] = {
|
||||
{
|
||||
"precision highp float;\nattribute vec2 a_pos;\n\nuniform mat4 u_matrix;\nuniform float u_size;\n\nvoid main(void) {\n gl_Position = u_matrix * vec4(a_pos, 0, 1);\n gl_PointSize = u_size;\n}\n",
|
||||
"precision highp float;\nuniform vec4 u_color;\nuniform float u_blur;\n\nvoid main() {\n\tfloat dist = length(gl_PointCoord - 0.5);\n\tfloat t = smoothstep(0.5, 0.5 - u_blur, dist);\n\n gl_FragColor = u_color * t;\n}\n",
|
||||
"precision highp float;\nattribute vec2 a_pos;\nuniform mat4 u_matrix;\nuniform float u_size;\nvoid main ()\n{\n vec4 tmpvar_1;\n tmpvar_1.zw = vec2(0.0, 1.0);\n tmpvar_1.xy = a_pos;\n gl_Position = (u_matrix * tmpvar_1);\n gl_PointSize = u_size;\n}\n\n",
|
||||
"precision highp float;\nuniform vec4 u_color;\nuniform float u_blur;\nvoid main ()\n{\n mediump vec2 x_1;\n x_1 = (gl_PointCoord - 0.5);\n mediump float tmpvar_2;\n tmpvar_2 = clamp (((\n sqrt(dot (x_1, x_1))\n - 0.5) / (\n (0.5 - u_blur)\n - 0.5)), 0.0, 1.0);\n gl_FragColor = (u_color * (tmpvar_2 * (tmpvar_2 * \n (3.0 - (2.0 * tmpvar_2))\n )));\n}\n\n",
|
||||
},
|
||||
{
|
||||
"precision highp float;\n// floor(127 / 2) == 63.0\n// the maximum allowed miter limit is 2.0 at the moment. the extrude normal is\n// stored in a byte (-128..127). we scale regular normals up to length 63, but\n// there are also \"special\" normals that have a bigger length (of up to 126 in\n// this case).\n// #define scale 63.0\n#define scale 0.015873016\n\nattribute vec2 a_pos;\nattribute vec2 a_extrude;\nattribute float a_linesofar;\n\n// posmatrix is for the vertex position, exmatrix is for rotating and projecting\n// the extrusion vector.\nuniform mat4 u_matrix;\nuniform mat4 u_exmatrix;\n\n// shared\nuniform float u_ratio;\nuniform vec2 u_linewidth;\nuniform vec4 u_color;\n\nvarying vec2 v_normal;\nvarying float v_linesofar;\n\nvoid main() {\n // We store the texture normals in the most insignificant bit\n // transform y so that 0 => -1 and 1 => 1\n // In the texture normal, x is 0 if the normal points straight up/down and 1 if it's a round cap\n // y is 1 if the normal points up, and -1 if it points down\n vec2 normal = mod(a_pos, 2.0);\n normal.y = sign(normal.y - 0.5);\n v_normal = normal;\n\n // Scale the extrusion vector down to a normal and then up by the line width\n // of this vertex.\n vec4 dist = vec4(u_linewidth.s * a_extrude * scale, 0.0, 0.0);\n\n // Remove the texture normal bit of the position before scaling it with the\n // model/view matrix. Add the extrusion vector *after* the model/view matrix\n // because we're extruding the line in pixel space, regardless of the current\n // tile's zoom level.\n gl_Position = u_matrix * vec4(floor(a_pos * 0.5), 0.0, 1.0) + u_exmatrix * dist;\n v_linesofar = a_linesofar * u_ratio;\n}",
|
||||
"precision highp float;\nuniform vec2 u_linewidth;\nuniform vec4 u_color;\n\nuniform vec2 u_dasharray;\n\nvarying vec2 v_normal;\nvarying float v_linesofar;\n\nvoid main() {\n // Calculate the distance of the pixel from the line in pixels.\n float dist = length(v_normal) * u_linewidth.s;\n\n // Calculate the antialiasing fade factor. This is either when fading in\n // the line in case of an offset line (v_linewidth.t) or when fading out\n // (v_linewidth.s)\n float alpha = clamp(min(dist - (u_linewidth.t - 1.0), u_linewidth.s - dist), 0.0, 1.0);\n\n // Calculate the antialiasing fade factor based on distance to the dash.\n // Only affects alpha when line is dashed\n float pos = mod(v_linesofar, u_dasharray.x + u_dasharray.y);\n alpha *= max(step(0.0, -u_dasharray.y), clamp(min(pos, u_dasharray.x - pos), 0.0, 1.0));\n\n gl_FragColor = u_color * alpha;\n}",
|
||||
"precision highp float;\nattribute vec2 a_pos;\nattribute vec2 a_extrude;\nattribute float a_linesofar;\nuniform mat4 u_matrix;\nuniform mat4 u_exmatrix;\nuniform float u_ratio;\nuniform vec2 u_linewidth;\nvarying vec2 v_normal;\nvarying float v_linesofar;\nvoid main ()\n{\n vec2 normal_1;\n vec2 tmpvar_2;\n tmpvar_2 = (vec2(mod (a_pos, 2.0)));\n normal_1.x = tmpvar_2.x;\n normal_1.y = sign((tmpvar_2.y - 0.5));\n v_normal = normal_1;\n vec4 tmpvar_3;\n tmpvar_3.zw = vec2(0.0, 0.0);\n tmpvar_3.xy = ((u_linewidth.x * a_extrude) * 0.015873);\n vec4 tmpvar_4;\n tmpvar_4.zw = vec2(0.0, 1.0);\n tmpvar_4.xy = floor((a_pos * 0.5));\n gl_Position = ((u_matrix * tmpvar_4) + (u_exmatrix * tmpvar_3));\n v_linesofar = (a_linesofar * u_ratio);\n}\n\n",
|
||||
"precision highp float;\nuniform vec2 u_linewidth;\nuniform vec4 u_color;\nuniform vec2 u_dasharray;\nvarying vec2 v_normal;\nvarying float v_linesofar;\nvoid main ()\n{\n float tmpvar_1;\n tmpvar_1 = (sqrt(dot (v_normal, v_normal)) * u_linewidth.x);\n float tmpvar_2;\n tmpvar_2 = (float(mod (v_linesofar, (u_dasharray.x + u_dasharray.y))));\n gl_FragColor = (u_color * (clamp (\n min ((tmpvar_1 - (u_linewidth.y - 1.0)), (u_linewidth.x - tmpvar_1))\n , 0.0, 1.0) * max (\n float((-(u_dasharray.y) >= 0.0))\n , \n clamp (min (tmpvar_2, (u_dasharray.x - tmpvar_2)), 0.0, 1.0)\n )));\n}\n\n",
|
||||
},
|
||||
{
|
||||
"precision highp float;\nattribute vec2 a_pos;\n\nuniform mat4 u_matrix;\nuniform vec2 u_world;\nuniform float u_size;\n\nvarying vec2 v_pos;\n\nvoid main() {\n gl_Position = u_matrix * vec4(floor(a_pos / 2.0), 0.0, 1.0);\n v_pos = (gl_Position.xy + 1.0) * u_world;\n gl_PointSize = u_size;\n}\n",
|
||||
"precision highp float;\nuniform vec4 u_color;\nuniform vec2 u_linewidth;\n\nvarying vec2 v_pos;\n\nvoid main() {\n float dist = length(v_pos - gl_FragCoord.xy);\n\n // Calculate the antialiasing fade factor. This is either when fading in\n // the line in case of an offset line (v_linewidth.t) or when fading out\n // (v_linewidth.s)\n float alpha = clamp(min(dist - (u_linewidth.t - 1.0), u_linewidth.s - dist), 0.0, 1.0);\n gl_FragColor = u_color * alpha;\n}\n",
|
||||
"precision highp float;\nattribute vec2 a_pos;\nuniform mat4 u_matrix;\nuniform vec2 u_world;\nuniform float u_size;\nvarying highp vec2 v_pos;\nvoid main ()\n{\n vec4 tmpvar_1;\n tmpvar_1.zw = vec2(0.0, 1.0);\n tmpvar_1.xy = floor((a_pos / 2.0));\n vec4 tmpvar_2;\n tmpvar_2 = (u_matrix * tmpvar_1);\n gl_Position = tmpvar_2;\n v_pos = ((tmpvar_2.xy + 1.0) * u_world);\n gl_PointSize = u_size;\n}\n\n",
|
||||
"precision highp float;\nuniform vec4 u_color;\nuniform vec2 u_linewidth;\nvarying vec2 v_pos;\nvoid main ()\n{\n highp float tmpvar_1;\n highp vec2 x_2;\n x_2 = (v_pos - gl_FragCoord.xy);\n tmpvar_1 = sqrt(dot (x_2, x_2));\n highp vec4 tmpvar_3;\n tmpvar_3 = (u_color * clamp (min (\n (tmpvar_1 - (u_linewidth.y - 1.0))\n , \n (u_linewidth.x - tmpvar_1)\n ), 0.0, 1.0));\n gl_FragColor = tmpvar_3;\n}\n\n",
|
||||
},
|
||||
{
|
||||
"precision highp float;\nattribute vec2 a_pos;\nuniform mat4 u_matrix;\nuniform vec2 u_world;\n\nvarying vec2 v_pos;\n\nvoid main() {\n gl_Position = u_matrix * vec4(a_pos, 0, 1);\n v_pos = (gl_Position.xy + 1.0) / 2.0 * u_world;\n}\n",
|
||||
"precision highp float;\nuniform vec4 u_color;\n\nvarying vec2 v_pos;\n\nvoid main() {\n float dist = length(v_pos - gl_FragCoord.xy);\n float alpha = smoothstep(1.0, 0.0, dist);\n gl_FragColor = u_color * alpha;\n}\n",
|
||||
"precision highp float;\nattribute vec2 a_pos;\nuniform mat4 u_matrix;\nuniform vec2 u_world;\nvarying highp vec2 v_pos;\nvoid main ()\n{\n vec4 tmpvar_1;\n tmpvar_1.zw = vec2(0.0, 1.0);\n tmpvar_1.xy = a_pos;\n vec4 tmpvar_2;\n tmpvar_2 = (u_matrix * tmpvar_1);\n gl_Position = tmpvar_2;\n v_pos = (((tmpvar_2.xy + 1.0) / 2.0) * u_world);\n}\n\n",
|
||||
"precision highp float;\nuniform vec4 u_color;\nvarying vec2 v_pos;\nvoid main ()\n{\n highp vec2 x_1;\n x_1 = (v_pos - gl_FragCoord.xy);\n highp float tmpvar_2;\n tmpvar_2 = clamp (((\n sqrt(dot (x_1, x_1))\n - 1.0) / -1.0), 0.0, 1.0);\n highp vec4 tmpvar_3;\n tmpvar_3 = (u_color * (tmpvar_2 * (tmpvar_2 * \n (3.0 - (2.0 * tmpvar_2))\n )));\n gl_FragColor = tmpvar_3;\n}\n\n",
|
||||
},
|
||||
{
|
||||
"precision highp float;\nuniform mat4 u_matrix;\n\nattribute vec2 a_pos;\n\nvarying vec2 v_pos;\n\nvoid main() {\n v_pos = a_pos;\n gl_Position = u_matrix * vec4(a_pos, 0, 1);\n}\n",
|
||||
"precision highp float;\nuniform vec4 u_color;\n\nuniform vec2 u_offset;\nuniform vec2 u_pattern_size;\nuniform vec2 u_pattern_tl;\nuniform vec2 u_pattern_br;\nuniform float u_mix;\n\n\nuniform sampler2D u_image;\n\nvarying vec2 v_pos;\n\nvoid main() {\n\n vec2 imagecoord = mod((v_pos + u_offset) / u_pattern_size, 1.0);\n vec2 pos = mix(u_pattern_tl, u_pattern_br, imagecoord);\n vec4 color1 = texture2D(u_image, pos);\n\n vec2 imagecoord2 = mod(imagecoord * 2.0, 1.0);\n vec2 pos2 = mix(u_pattern_tl, u_pattern_br, imagecoord2);\n vec4 color2 = texture2D(u_image, pos2);\n\n vec4 color = mix(color1, color2, u_mix);\n gl_FragColor = color + u_color * (1.0 - color.a);\n}\n",
|
||||
"precision highp float;\nuniform mat4 u_matrix;\nattribute vec2 a_pos;\nvarying vec2 v_pos;\nvoid main ()\n{\n v_pos = a_pos;\n vec4 tmpvar_1;\n tmpvar_1.zw = vec2(0.0, 1.0);\n tmpvar_1.xy = a_pos;\n gl_Position = (u_matrix * tmpvar_1);\n}\n\n",
|
||||
"precision highp float;\nuniform vec4 u_color;\nuniform vec2 u_offset;\nuniform vec2 u_pattern_size;\nuniform vec2 u_pattern_tl;\nuniform vec2 u_pattern_br;\nuniform float u_mix;\nuniform sampler2D u_image;\nvarying vec2 v_pos;\nvoid main ()\n{\n vec2 tmpvar_1;\n tmpvar_1 = (vec2(mod (((v_pos + u_offset) / u_pattern_size), 1.0)));\n lowp vec4 tmpvar_2;\n tmpvar_2 = mix (texture2D (u_image, mix (u_pattern_tl, u_pattern_br, tmpvar_1)), texture2D (u_image, mix (u_pattern_tl, u_pattern_br, (vec2(mod (\n (tmpvar_1 * 2.0)\n , 1.0))))), u_mix);\n lowp vec4 tmpvar_3;\n tmpvar_3 = (tmpvar_2 + (u_color * (1.0 - tmpvar_2.w)));\n gl_FragColor = tmpvar_3;\n}\n\n",
|
||||
},
|
||||
{
|
||||
"precision highp float;\nattribute vec2 a_pos;\n\nuniform mat4 u_matrix;\n\nvoid main() {\n gl_Position = u_matrix * vec4(a_pos, 0, 1);\n}\n",
|
||||
"precision highp float;\nuniform vec4 u_color;\n\nvoid main() {\n gl_FragColor = u_color;\n}\n",
|
||||
"precision highp float;\nattribute vec2 a_pos;\nuniform mat4 u_matrix;\nvoid main ()\n{\n vec4 tmpvar_1;\n tmpvar_1.zw = vec2(0.0, 1.0);\n tmpvar_1.xy = a_pos;\n gl_Position = (u_matrix * tmpvar_1);\n}\n\n",
|
||||
"precision highp float;\nuniform vec4 u_color;\nvoid main ()\n{\n gl_FragColor = u_color;\n}\n\n",
|
||||
},
|
||||
{
|
||||
"precision highp float;\nattribute vec2 a_pos;\n\nuniform mat4 u_matrix;\nuniform float u_size;\n\nvoid main() {\n gl_Position = u_matrix * vec4(a_pos, 0, 1);\n gl_PointSize = u_size;\n}\n",
|
||||
"precision highp float;\n#define root2 1.4142135623730951\n\nuniform sampler2D u_image;\nuniform vec2 u_tl;\nuniform vec2 u_br;\nuniform vec4 u_color;\n\nuniform vec2 pos;\nuniform float inbounds;\nuniform vec4 color;\n\nvoid main() {\n vec2 pos = (gl_PointCoord * 2.0 - 1.0) * root2 / 2.0 + 0.5;\n\n float inbounds = step(0.0, pos.x) * step(0.0, pos.y) *\n (1.0 - step(1.0, pos.x)) * (1.0 - step(1.0, pos.y));\n\n vec4 color = texture2D(u_image, mix(u_tl, u_br, pos)) * inbounds;\n\n gl_FragColor = color * u_color;\n}\n",
|
||||
"precision highp float;\nattribute vec2 a_pos;\nuniform mat4 u_matrix;\nuniform float u_size;\nvoid main ()\n{\n vec4 tmpvar_1;\n tmpvar_1.zw = vec2(0.0, 1.0);\n tmpvar_1.xy = a_pos;\n gl_Position = (u_matrix * tmpvar_1);\n gl_PointSize = u_size;\n}\n\n",
|
||||
"precision highp float;\nuniform sampler2D u_image;\nuniform vec2 u_tl;\nuniform vec2 u_br;\nuniform vec4 u_color;\nvoid main ()\n{\n mediump vec2 tmpvar_1;\n tmpvar_1 = (((\n ((gl_PointCoord * 2.0) - 1.0)\n * 1.41421) / 2.0) + 0.5);\n mediump vec2 tmpvar_2;\n tmpvar_2 = mix (u_tl, u_br, tmpvar_1);\n lowp vec4 tmpvar_3;\n tmpvar_3 = texture2D (u_image, tmpvar_2);\n gl_FragColor = ((tmpvar_3 * (\n ((float((tmpvar_1.x >= 0.0)) * float((tmpvar_1.y >= 0.0))) * (1.0 - float((tmpvar_1.x >= 1.0))))\n * \n (1.0 - float((tmpvar_1.y >= 1.0)))\n )) * u_color);\n}\n\n",
|
||||
},
|
||||
{
|
||||
"precision highp float;\nuniform mat4 u_matrix;\n\nattribute vec2 a_pos;\n\nvarying vec2 v_pos;\n\nvoid main() {\n gl_Position = u_matrix * vec4(a_pos, 0, 1);\n v_pos = a_pos / 4096.0;\n}\n",
|
||||
"precision highp float;\nuniform sampler2D u_image;\nuniform float u_opacity;\n\nvarying vec2 v_pos;\n\nvoid main() {\n gl_FragColor = texture2D(u_image, v_pos) * u_opacity;\n}\n",
|
||||
"precision highp float;\nuniform mat4 u_matrix;\nattribute vec2 a_pos;\nvarying vec2 v_pos;\nvoid main ()\n{\n vec4 tmpvar_1;\n tmpvar_1.zw = vec2(0.0, 1.0);\n tmpvar_1.xy = a_pos;\n gl_Position = (u_matrix * tmpvar_1);\n v_pos = (a_pos / 4096.0);\n}\n\n",
|
||||
"precision highp float;\nuniform sampler2D u_image;\nuniform float u_opacity;\nvarying vec2 v_pos;\nvoid main ()\n{\n lowp vec4 tmpvar_1;\n tmpvar_1 = (texture2D (u_image, v_pos) * u_opacity);\n gl_FragColor = tmpvar_1;\n}\n\n",
|
||||
},
|
||||
{
|
||||
"precision highp float;\nattribute vec2 a_pos;\nattribute vec2 a_offset;\nattribute vec4 a_data1;\nattribute vec4 a_data2;\n\n\n// posmatrix is for the vertex position, exmatrix is for rotating and projecting\n// the extrusion vector.\nuniform mat4 u_matrix;\nuniform mat4 u_exmatrix;\nuniform float u_angle;\nuniform float u_zoom;\nuniform float u_flip;\nuniform float u_fadedist;\nuniform float u_minfadezoom;\nuniform float u_maxfadezoom;\nuniform float u_fadezoom;\n\nuniform vec2 u_texsize;\n\nvarying vec2 v_tex;\nvarying float v_alpha;\n\nvoid main() {\n vec2 a_tex = a_data1.xy;\n float a_labelminzoom = a_data1[2];\n float a_angle = a_data1[3];\n vec2 a_zoom = a_data2.st;\n vec2 a_range = a_data2.pq;\n\n float rev = 0.0;\n\n // u_angle is angle of the map, -128..128 representing 0..2PI\n // a_angle is angle of the label, 0..256 representing 0..2PI, where 0 is horizontal text\n float rotated = mod(a_angle + u_angle, 256.0);\n // if the label rotates with the map, and if the rotated label is upside down, hide it\n if (u_flip > 0.0 && rotated >= 64.0 && rotated < 192.0) rev = 1.0;\n\n // If the label should be invisible, we move the vertex outside\n // of the view plane so that the triangle gets clipped. This makes it easier\n // for us to create degenerate triangle strips.\n // u_zoom is the current zoom level adjusted for the change in font size\n float z = 2.0 - step(a_zoom[0], u_zoom) - (1.0 - step(a_zoom[1], u_zoom)) + rev;\n\n // fade out labels\n float alpha = clamp((u_fadezoom - a_labelminzoom) / u_fadedist, 0.0, 1.0);\n\n if (u_fadedist >= 0.0) {\n v_alpha = alpha;\n } else {\n v_alpha = 1.0 - alpha;\n }\n if (u_maxfadezoom < a_labelminzoom) {\n v_alpha = 0.0;\n }\n if (u_minfadezoom >= a_labelminzoom) {\n v_alpha = 1.0;\n }\n\n // if label has been faded out, clip it\n z += step(v_alpha, 0.0);\n\n // all the angles are 0..256 representing 0..2PI\n // hide if (angle >= a_rangeend && angle < rangestart)\n z += (1.0 - step(a_range[0], u_angle)) * step(a_range[1], u_angle);\n\n gl_Position = u_matrix * vec4(a_pos, 0, 1) + u_exmatrix * vec4(a_offset / 64.0, z, 0);\n v_tex = a_tex * 4.0 / u_texsize;\n}\n",
|
||||
"precision highp float;\nuniform sampler2D u_texture;\nuniform vec4 u_color;\nuniform float u_buffer;\nuniform float u_gamma;\n\nvarying vec2 v_tex;\nvarying float v_alpha;\n\nvoid main() {\n float dist = texture2D(u_texture, v_tex).a;\n float alpha = smoothstep(u_buffer - u_gamma, u_buffer + u_gamma, dist) * v_alpha;\n gl_FragColor = u_color * alpha;\n}\n",
|
||||
"precision highp float;\nattribute vec2 a_pos;\nattribute vec2 a_offset;\nattribute vec4 a_data1;\nattribute vec4 a_data2;\nuniform mat4 u_matrix;\nuniform mat4 u_exmatrix;\nuniform float u_angle;\nuniform float u_zoom;\nuniform float u_flip;\nuniform float u_fadedist;\nuniform float u_minfadezoom;\nuniform float u_maxfadezoom;\nuniform float u_fadezoom;\nuniform vec2 u_texsize;\nvarying vec2 v_tex;\nvarying float v_alpha;\nvoid main ()\n{\n float rev_1;\n rev_1 = 0.0;\n float tmpvar_2;\n tmpvar_2 = (float(mod ((a_data1.w + u_angle), 256.0)));\n if ((((u_flip > 0.0) && (tmpvar_2 >= 64.0)) && (tmpvar_2 < 192.0))) {\n rev_1 = 1.0;\n };\n float tmpvar_3;\n tmpvar_3 = (((2.0 - \n float((u_zoom >= a_data2.x))\n ) - (1.0 - \n float((u_zoom >= a_data2.y))\n )) + rev_1);\n float tmpvar_4;\n tmpvar_4 = clamp (((u_fadezoom - a_data1.z) / u_fadedist), 0.0, 1.0);\n if ((u_fadedist >= 0.0)) {\n v_alpha = tmpvar_4;\n } else {\n v_alpha = (1.0 - tmpvar_4);\n };\n if ((u_maxfadezoom < a_data1.z)) {\n v_alpha = 0.0;\n };\n if ((u_minfadezoom >= a_data1.z)) {\n v_alpha = 1.0;\n };\n vec4 tmpvar_5;\n tmpvar_5.zw = vec2(0.0, 1.0);\n tmpvar_5.xy = a_pos;\n vec4 tmpvar_6;\n tmpvar_6.w = 0.0;\n tmpvar_6.xy = (a_offset / 64.0);\n tmpvar_6.z = ((tmpvar_3 + float(\n (0.0 >= v_alpha)\n )) + ((1.0 - \n float((u_angle >= a_data2.z))\n ) * float(\n (u_angle >= a_data2.w)\n )));\n gl_Position = ((u_matrix * tmpvar_5) + (u_exmatrix * tmpvar_6));\n v_tex = ((a_data1.xy * 4.0) / u_texsize);\n}\n\n",
|
||||
"precision highp float;\nuniform sampler2D u_texture;\nuniform vec4 u_color;\nuniform float u_buffer;\nuniform float u_gamma;\nvarying vec2 v_tex;\nvarying float v_alpha;\nvoid main ()\n{\n float edge0_1;\n edge0_1 = (u_buffer - u_gamma);\n lowp float tmpvar_2;\n tmpvar_2 = clamp (((texture2D (u_texture, v_tex).w - edge0_1) / (\n (u_buffer + u_gamma)\n - edge0_1)), 0.0, 1.0);\n lowp vec4 tmpvar_3;\n tmpvar_3 = (u_color * ((tmpvar_2 * \n (tmpvar_2 * (3.0 - (2.0 * tmpvar_2)))\n ) * v_alpha));\n gl_FragColor = tmpvar_3;\n}\n\n",
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
@@ -2,6 +2,16 @@
|
||||
|
||||
#include <iostream>
|
||||
|
||||
using namespace llmr;
|
||||
|
||||
size_t LayerDescription::size() const {
|
||||
size_t count = 1;
|
||||
for (const LayerDescription &layer : child_layer) {
|
||||
count += layer.size();
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
std::ostream& llmr::operator<<(std::ostream& os, const LayerDescription& layer) {
|
||||
os << "Structure: " << layer.name << std::endl;
|
||||
os << " - bucket_name: " << layer.bucket_name << std::endl;
|
||||
|
||||
@@ -15,14 +15,20 @@ SpritePosition::SpritePosition(uint16_t x, uint16_t y, uint16_t width, uint16_t
|
||||
y(y),
|
||||
width(width),
|
||||
height(height),
|
||||
pixelRatio(pixelRatio) {}
|
||||
pixelRatio(pixelRatio) {
|
||||
}
|
||||
|
||||
ImagePosition::ImagePosition(const vec2<uint16_t>& size, vec2<float> tl, vec2<float> br)
|
||||
: size(size),
|
||||
tl(tl),
|
||||
br(br) {}
|
||||
|
||||
Sprite::Sprite(Map &map, float pixelRatio) : pixelRatio(pixelRatio), raster(std::make_shared<Raster>()), map(map) {}
|
||||
Sprite::Sprite(Map &map, float pixelRatio)
|
||||
: pixelRatio(pixelRatio),
|
||||
raster(map.getTexturepool()),
|
||||
map(map),
|
||||
loaded(false) {
|
||||
}
|
||||
|
||||
void Sprite::load(const std::string& base_url) {
|
||||
std::shared_ptr<Sprite> sprite = shared_from_this();
|
||||
@@ -40,7 +46,7 @@ void Sprite::load(const std::string& base_url) {
|
||||
|
||||
platform::request_http(base_url + suffix + ".png", [sprite](platform::Response *res) {
|
||||
if (res->code == 200) {
|
||||
sprite->raster->setData(res->body);
|
||||
sprite->image.swap(res->body);
|
||||
sprite->asyncParseImage();
|
||||
} else {
|
||||
fprintf(stderr, "failed to load sprite image\n");
|
||||
@@ -49,8 +55,7 @@ void Sprite::load(const std::string& base_url) {
|
||||
}
|
||||
|
||||
void Sprite::complete(std::shared_ptr<Sprite> &sprite) {
|
||||
std::lock_guard<std::mutex> lock(sprite->mtx);
|
||||
if (sprite->raster && sprite->raster->isLoaded() && sprite->pos.size()) {
|
||||
if (sprite->raster.isLoaded() && sprite->pos.size()) {
|
||||
sprite->loaded = true;
|
||||
sprite->map.update();
|
||||
fprintf(stderr, "sprite loaded\n");
|
||||
@@ -58,7 +63,6 @@ void Sprite::complete(std::shared_ptr<Sprite> &sprite) {
|
||||
}
|
||||
|
||||
bool Sprite::isLoaded() const {
|
||||
std::lock_guard<std::mutex> lock(mtx);
|
||||
return loaded;
|
||||
}
|
||||
|
||||
@@ -71,14 +75,14 @@ void Sprite::asyncParseJSON() {
|
||||
}
|
||||
|
||||
void Sprite::parseImage(std::shared_ptr<Sprite> &sprite) {
|
||||
sprite->raster->load();
|
||||
sprite->raster.load(sprite->image);
|
||||
sprite->image.clear();
|
||||
}
|
||||
|
||||
void Sprite::parseJSON(std::shared_ptr<Sprite> &sprite) {
|
||||
std::lock_guard<std::mutex> lock(sprite->mtx);
|
||||
|
||||
rapidjson::Document d;
|
||||
d.Parse<0>(sprite->body.c_str());
|
||||
sprite->body.clear();
|
||||
|
||||
if (d.IsObject()) {
|
||||
for (rapidjson::Value::ConstMemberIterator itr = d.MemberBegin(); itr != d.MemberEnd(); ++itr) {
|
||||
@@ -105,7 +109,7 @@ void Sprite::parseJSON(std::shared_ptr<Sprite> &sprite) {
|
||||
}
|
||||
|
||||
ImagePosition Sprite::getPosition(const std::string& name, bool repeating) {
|
||||
if (!this->raster || !this->raster->isLoaded()) return {};
|
||||
if (!isLoaded()) return {};
|
||||
|
||||
// `repeating` indicates that the image will be used in a repeating pattern
|
||||
// repeating pattern images are assumed to have a 1px padding that mirrors the opposite edge
|
||||
@@ -122,12 +126,12 @@ ImagePosition Sprite::getPosition(const std::string& name, bool repeating) {
|
||||
pos.height
|
||||
},
|
||||
{
|
||||
(float)(pos.x + offset) / raster->width,
|
||||
(float)(pos.y + offset) / raster->height
|
||||
(float)(pos.x + offset) / raster.width,
|
||||
(float)(pos.y + offset) / raster.height
|
||||
},
|
||||
{
|
||||
(float)(pos.x + pos.width - 2 * offset) / raster->width,
|
||||
(float)(pos.y + pos.height - 2 * offset) / raster->height
|
||||
(float)(pos.x + pos.width - 2 * offset) / raster.width,
|
||||
(float)(pos.y + pos.height - 2 * offset) / raster.height
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -103,6 +103,7 @@ void Style::cascade(float z) {
|
||||
text.haloRadius = layer.haloRadius.evaluate<float>(z);
|
||||
text.rotate = layer.rotate.evaluate<float>(z);
|
||||
text.alwaysVisible = layer.alwaysVisible.evaluate<bool>(z);
|
||||
text.opacity = layer.opacity.evaluate<float>(z);
|
||||
}
|
||||
|
||||
// Cascade raster classes
|
||||
@@ -123,6 +124,14 @@ void Style::cascade(float z) {
|
||||
}
|
||||
}
|
||||
|
||||
size_t Style::layerCount() const {
|
||||
size_t count = 0;
|
||||
for (const LayerDescription &layer : layers) {
|
||||
count += layer.size();
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
void Style::loadJSON(const uint8_t *const data, size_t bytes) {
|
||||
rapidjson::Document doc;
|
||||
|
||||
|
||||
194
src/util/clip_ids.cpp
Normal file
194
src/util/clip_ids.cpp
Normal file
@@ -0,0 +1,194 @@
|
||||
#include <llmr/util/clip_ids.hpp>
|
||||
#include <llmr/map/tile.hpp>
|
||||
|
||||
#include <llmr/util/math.hpp>
|
||||
|
||||
#include <list>
|
||||
#include <vector>
|
||||
#include <bitset>
|
||||
#include <cassert>
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
|
||||
namespace llmr {
|
||||
|
||||
struct TileHierarchy {
|
||||
TileHierarchy(Tile::ID id, std::list<TileHierarchy> &&children)
|
||||
: id(id), children(std::move(children)) {}
|
||||
|
||||
const Tile::ID id;
|
||||
ClipID clip;
|
||||
std::list<TileHierarchy> children;
|
||||
};
|
||||
|
||||
std::list<TileHierarchy> partition(std::forward_list<Tile::ID> &&array) {
|
||||
if (array.empty()) {
|
||||
// We don't have to update the clipping mask because there are no tiles
|
||||
// anyway.
|
||||
return {};
|
||||
}
|
||||
|
||||
int8_t minZ = array.begin()->z;
|
||||
|
||||
std::list<TileHierarchy> result;
|
||||
std::forward_list<Tile::ID> remainder;
|
||||
auto remainder_it = remainder.before_begin();
|
||||
|
||||
while (!array.empty()) {
|
||||
const Tile::ID id = array.front();
|
||||
array.pop_front();
|
||||
if (id.z == minZ) {
|
||||
std::forward_list<Tile::ID> children;
|
||||
auto children_it = children.before_begin();
|
||||
|
||||
array.remove_if([&id, &children, &children_it](const Tile::ID &child) {
|
||||
if (child.isChildOf(id)) {
|
||||
children_it = children.insert_after(children_it, child);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
result.emplace_back(id, partition(std::move(children)));
|
||||
} else {
|
||||
remainder_it = remainder.insert_after(remainder_it, id);
|
||||
}
|
||||
}
|
||||
|
||||
// Concatenate the remainder.
|
||||
if (!array.empty()) {
|
||||
result.splice(result.begin(), partition(std::move(remainder)));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
uint8_t prefix(std::list<TileHierarchy> &array, TileHierarchy *parent = nullptr) {
|
||||
if (array.empty()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool all_children_are_immediate = true;
|
||||
uint8_t max_child_prefix_length = 0;
|
||||
|
||||
struct Huffman {
|
||||
explicit Huffman(int prefix_length, TileHierarchy *item)
|
||||
: prefix_length(prefix_length), children(1, item) {}
|
||||
uint8_t prefix_length;
|
||||
std::vector<TileHierarchy *> children;
|
||||
};
|
||||
|
||||
// Create a temporary structure that we use for sorting the prefix tree.
|
||||
std::list<Huffman> huffman;
|
||||
std::transform(array.begin(), array.end(), std::back_inserter(huffman), [parent, &all_children_are_immediate, &max_child_prefix_length](TileHierarchy &item) {
|
||||
uint8_t prefix_length = prefix(item.children, &item);
|
||||
|
||||
if (prefix_length > max_child_prefix_length) {
|
||||
max_child_prefix_length = prefix_length;
|
||||
}
|
||||
|
||||
if (!parent || item.id.z != parent->id.z + 1) {
|
||||
all_children_are_immediate = false;
|
||||
}
|
||||
|
||||
return Huffman { prefix_length + 1, &item };
|
||||
});
|
||||
|
||||
while (huffman.size() > 1) {
|
||||
huffman.sort([](const Huffman &a, const Huffman &b) {
|
||||
return a.prefix_length < b.prefix_length;
|
||||
});
|
||||
|
||||
Huffman &first = *huffman.begin();
|
||||
Huffman &second = *(++huffman.begin());
|
||||
|
||||
assert(&first != &second);
|
||||
|
||||
// Prefix with 0
|
||||
std::for_each(first.children.begin(), first.children.end(), [](TileHierarchy *child) {
|
||||
child->clip.mask >>= 1;
|
||||
child->clip.mask.set(7, false); // noop
|
||||
child->clip.length++;
|
||||
});
|
||||
first.prefix_length++;
|
||||
|
||||
// Prefix with 1
|
||||
std::for_each(second.children.begin(), second.children.end(), [](TileHierarchy *child) {
|
||||
child->clip.mask >>= 1;
|
||||
child->clip.mask.set(7, true);
|
||||
child->clip.length++;
|
||||
});
|
||||
second.prefix_length++;
|
||||
|
||||
second.children.insert(second.children.end(), first.children.begin(), first.children.end());
|
||||
second.prefix_length = first.prefix_length + second.prefix_length;
|
||||
|
||||
// Remove the first child as we've just merged it into the second version.
|
||||
huffman.erase(huffman.begin());
|
||||
}
|
||||
|
||||
uint8_t prefix_length = 0;
|
||||
|
||||
// Filter out all-zero bits
|
||||
bool filter_zero = !all_children_are_immediate || array.size() != 4;
|
||||
|
||||
for (TileHierarchy &item : array) {
|
||||
if (filter_zero && !item.clip.mask.any()) {
|
||||
// Make sure we don't have a prefix that is all zeros.
|
||||
// item.clip.mask |= (0x80 >> item.length);
|
||||
item.clip.mask.set(7 - item.clip.length);
|
||||
item.clip.length++;
|
||||
}
|
||||
|
||||
if (item.clip.length > prefix_length) {
|
||||
prefix_length = item.clip.length;
|
||||
}
|
||||
}
|
||||
|
||||
return max_child_prefix_length + prefix_length;
|
||||
}
|
||||
|
||||
void propagate(std::map<Tile::ID, ClipID> &mapping, std::list<TileHierarchy> &array, const ClipID &parent = ClipID{}) {
|
||||
for (auto &item : array) {
|
||||
item.clip.mask >>= parent.length;
|
||||
item.clip.mask |= parent.mask;
|
||||
item.clip.length += parent.length;
|
||||
auto result = mapping.emplace(item.id, item.clip);
|
||||
assert("Tried to insert a duplicate item" && result.second == true);
|
||||
propagate(mapping, item.children, const_cast<const ClipID &>(item.clip));
|
||||
};
|
||||
}
|
||||
|
||||
void updateClipIDs(const std::list<Tile *> &array) {
|
||||
std::forward_list<Tile::ID> ids;
|
||||
std::transform(array.begin(), array.end(), std::front_inserter(ids), [](Tile *item) {
|
||||
return item->id;
|
||||
});
|
||||
|
||||
const std::map<Tile::ID, ClipID> mapping = computeClipIDs(ids);
|
||||
|
||||
std::for_each(array.begin(), array.end(), [&mapping](Tile *item) {
|
||||
auto it = mapping.find(item->id);
|
||||
if (it != mapping.end()) {
|
||||
item->clip = it->second;
|
||||
} else {
|
||||
item->clip = ClipID {};
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
std::map<Tile::ID, ClipID> computeClipIDs(std::forward_list<Tile::ID> array) {
|
||||
// Sort by zoom level and make sure that we don't have duplicate elements.
|
||||
array.sort();
|
||||
array.unique();
|
||||
|
||||
std::list<TileHierarchy> hierarchy = partition(std::move(array));
|
||||
prefix(hierarchy);
|
||||
|
||||
std::map<Tile::ID, ClipID> mapping;
|
||||
propagate(mapping, hierarchy);
|
||||
return mapping;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -13,11 +13,15 @@
|
||||
|
||||
using namespace llmr;
|
||||
|
||||
Raster::Raster(Texturepool &texturepool)
|
||||
: texturepool(texturepool)
|
||||
{}
|
||||
|
||||
Raster::~Raster() {
|
||||
if (img && !textured) {
|
||||
free(img);
|
||||
} else if (textured) {
|
||||
texturepool->removeTextureID(texture);
|
||||
texturepool.removeTextureID(texture);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,17 +30,14 @@ bool Raster::isLoaded() const {
|
||||
return loaded;
|
||||
}
|
||||
|
||||
void Raster::load() {
|
||||
bool Raster::load(const std::string &data) {
|
||||
loadImage(data);
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mtx);
|
||||
if (img) {
|
||||
loaded = true;
|
||||
}
|
||||
std::lock_guard<std::mutex> lock(mtx);
|
||||
if (img) {
|
||||
loaded = true;
|
||||
}
|
||||
|
||||
data.clear();
|
||||
return loaded;
|
||||
}
|
||||
|
||||
struct Buffer {
|
||||
@@ -154,9 +155,6 @@ void Raster::loadImage(const std::string& data) {
|
||||
}
|
||||
}
|
||||
|
||||
void Raster::setTexturepool(Texturepool* new_texturepool) {
|
||||
texturepool = new_texturepool;
|
||||
}
|
||||
|
||||
void Raster::bind(bool linear) {
|
||||
if (!width || !height) {
|
||||
@@ -164,8 +162,8 @@ void Raster::bind(bool linear) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (img && !textured && texturepool) {
|
||||
texture = texturepool->getTextureID();
|
||||
if (img && !textured) {
|
||||
texture = texturepool.getTextureID();
|
||||
glBindTexture(GL_TEXTURE_2D, texture);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
|
||||
@@ -25,7 +25,7 @@ GLuint Texturepool::getTextureID() {
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
|
||||
void Texturepool::removeTextureID(GLuint texture_id) {
|
||||
bool needs_clear = false;
|
||||
|
||||
@@ -36,7 +36,8 @@ void Texturepool::removeTextureID(GLuint texture_id) {
|
||||
}
|
||||
|
||||
if (needs_clear) {
|
||||
clearTextureIDs();
|
||||
// TODO: We need to find a better way to deal with texture pool cleanup
|
||||
// clearTextureIDs();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
234
test/clip_ids.cpp
Normal file
234
test/clip_ids.cpp
Normal file
@@ -0,0 +1,234 @@
|
||||
#include <iostream>
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include <llmr/util/clip_ids.hpp>
|
||||
#include <llmr/util/std.hpp>
|
||||
|
||||
using namespace llmr;
|
||||
|
||||
TEST(ClipIDs, ClipMasks) {
|
||||
ASSERT_EQ(0, clipMask[0]);
|
||||
ASSERT_EQ(0x80, clipMask[1]);
|
||||
ASSERT_EQ(0xC0, clipMask[2]);
|
||||
ASSERT_EQ(0xE0, clipMask[3]);
|
||||
ASSERT_EQ(0xF0, clipMask[4]);
|
||||
ASSERT_EQ(0xF8, clipMask[5]);
|
||||
ASSERT_EQ(0xFC, clipMask[6]);
|
||||
ASSERT_EQ(0xFE, clipMask[7]);
|
||||
ASSERT_EQ(0xFF, clipMask[8]);
|
||||
}
|
||||
|
||||
|
||||
TEST(ClipIDs, ParentAndFourChildren) {
|
||||
std::array<std::unique_ptr<Tile>, 5> tiles {{
|
||||
std::make_unique<Tile>(Tile::ID { 1, 0, 0 }), // 1/0/0: 11000000 (3)
|
||||
std::make_unique<Tile>(Tile::ID { 1, 0, 1 }), // 1/0/1: 11100000 (3)
|
||||
std::make_unique<Tile>(Tile::ID { 1, 1, 0 }), // 1/1/0: 10000000 (3)
|
||||
std::make_unique<Tile>(Tile::ID { 1, 1, 1 }), // 1/1/1: 10100000 (3)
|
||||
std::make_unique<Tile>(Tile::ID { 0, 0, 0 }), // 0/0/0: 10000000 (1)
|
||||
}};
|
||||
|
||||
std::list<Tile *> tile_ptrs;
|
||||
std::transform(tiles.begin(), tiles.end(), std::back_inserter(tile_ptrs), [](std::unique_ptr<Tile> &tile) { return tile.get(); });
|
||||
|
||||
updateClipIDs(tile_ptrs);
|
||||
|
||||
// for (const auto &it : tiles) {
|
||||
// std::cout << std::string(it->id) << ": " << it->clip.mask << " (" << (int)it->clip.length << ")" << std::endl;
|
||||
// }
|
||||
|
||||
ASSERT_EQ(std::bitset<8>("11000000"), tiles[0]->clip.mask); ASSERT_EQ(3, tiles[0]->clip.length);
|
||||
ASSERT_EQ(std::bitset<8>("11100000"), tiles[1]->clip.mask); ASSERT_EQ(3, tiles[1]->clip.length);
|
||||
ASSERT_EQ(std::bitset<8>("10000000"), tiles[2]->clip.mask); ASSERT_EQ(3, tiles[2]->clip.length);
|
||||
ASSERT_EQ(std::bitset<8>("10100000"), tiles[3]->clip.mask); ASSERT_EQ(3, tiles[3]->clip.length);
|
||||
ASSERT_EQ(std::bitset<8>("10000000"), tiles[4]->clip.mask); ASSERT_EQ(1, tiles[4]->clip.length);
|
||||
}
|
||||
|
||||
TEST(ClipIDs, ParentAndFourChildrenNegative) {
|
||||
std::array<std::unique_ptr<Tile>, 5> tiles {{
|
||||
std::make_unique<Tile>(Tile::ID { 1, -2, 0 }), // 1/0/0: 11000000 (3)
|
||||
std::make_unique<Tile>(Tile::ID { 1, -2, 1 }), // 1/0/1: 11100000 (3)
|
||||
std::make_unique<Tile>(Tile::ID { 1, -1, 0 }), // 1/1/0: 10000000 (3)
|
||||
std::make_unique<Tile>(Tile::ID { 1, -1, 1 }), // 1/1/1: 10100000 (3)
|
||||
std::make_unique<Tile>(Tile::ID { 0, -1, 0 }), // 0/0/0: 10000000 (1)
|
||||
}};
|
||||
|
||||
std::list<Tile *> tile_ptrs;
|
||||
std::transform(tiles.begin(), tiles.end(), std::back_inserter(tile_ptrs), [](std::unique_ptr<Tile> &tile) { return tile.get(); });
|
||||
|
||||
updateClipIDs(tile_ptrs);
|
||||
|
||||
// for (const auto &it : tiles) {
|
||||
// std::cout << std::string(it->id) << ": " << it->clip.mask << " (" << (int)it->clip.length << ")" << std::endl;
|
||||
// }
|
||||
|
||||
ASSERT_EQ(std::bitset<8>("11000000"), tiles[0]->clip.mask); ASSERT_EQ(3, tiles[0]->clip.length);
|
||||
ASSERT_EQ(std::bitset<8>("11100000"), tiles[1]->clip.mask); ASSERT_EQ(3, tiles[1]->clip.length);
|
||||
ASSERT_EQ(std::bitset<8>("10000000"), tiles[2]->clip.mask); ASSERT_EQ(3, tiles[2]->clip.length);
|
||||
ASSERT_EQ(std::bitset<8>("10100000"), tiles[3]->clip.mask); ASSERT_EQ(3, tiles[3]->clip.length);
|
||||
ASSERT_EQ(std::bitset<8>("10000000"), tiles[4]->clip.mask); ASSERT_EQ(1, tiles[4]->clip.length);
|
||||
}
|
||||
|
||||
TEST(ClipIDs, NegativeParentAndMissingLevel) {
|
||||
std::array<std::unique_ptr<Tile>, 5> tiles {{
|
||||
std::make_unique<Tile>(Tile::ID { 1, -1, 0 }), // 1/-1/0: 10000000 (1)
|
||||
std::make_unique<Tile>(Tile::ID { 2, -1, 0 }), // 2/-1/0: 10000000 (3)
|
||||
std::make_unique<Tile>(Tile::ID { 2, -2, 1 }), // 2/-2/1: 11100000 (3)
|
||||
std::make_unique<Tile>(Tile::ID { 2, -1, 1 }), // 2/-1/1: 10100000 (3)
|
||||
std::make_unique<Tile>(Tile::ID { 2, -2, 0 }), // 2/-2/0: 11000000 (3)
|
||||
}};
|
||||
|
||||
std::list<Tile *> tile_ptrs;
|
||||
std::transform(tiles.begin(), tiles.end(), std::back_inserter(tile_ptrs), [](std::unique_ptr<Tile> &tile) { return tile.get(); });
|
||||
|
||||
updateClipIDs(tile_ptrs);
|
||||
|
||||
// for (const auto &it : tiles) {
|
||||
// std::cout << std::string(it->id) << ": " << it->clip.mask << " (" << (int)it->clip.length << ")" << std::endl;
|
||||
// }
|
||||
|
||||
ASSERT_EQ(std::bitset<8>("10000000"), tiles[0]->clip.mask); ASSERT_EQ(1, tiles[0]->clip.length);
|
||||
ASSERT_EQ(std::bitset<8>("10000000"), tiles[1]->clip.mask); ASSERT_EQ(3, tiles[1]->clip.length);
|
||||
ASSERT_EQ(std::bitset<8>("11100000"), tiles[2]->clip.mask); ASSERT_EQ(3, tiles[2]->clip.length);
|
||||
ASSERT_EQ(std::bitset<8>("10100000"), tiles[3]->clip.mask); ASSERT_EQ(3, tiles[3]->clip.length);
|
||||
ASSERT_EQ(std::bitset<8>("11000000"), tiles[4]->clip.mask); ASSERT_EQ(3, tiles[4]->clip.length);
|
||||
}
|
||||
|
||||
|
||||
TEST(ClipIDs, SevenOnSameLevel) {
|
||||
std::array<std::unique_ptr<Tile>, 7> tiles {{
|
||||
std::make_unique<Tile>(Tile::ID { 2, 0, 0 }), // 2/0/0: 11000000 (3)
|
||||
std::make_unique<Tile>(Tile::ID { 2, 0, 1 }), // 2/0/1: 11100000 (3)
|
||||
std::make_unique<Tile>(Tile::ID { 2, 0, 2 }), // 2/0/2: 10000000 (3)
|
||||
std::make_unique<Tile>(Tile::ID { 2, 1, 0 }), // 2/1/0: 10100000 (3)
|
||||
std::make_unique<Tile>(Tile::ID { 2, 1, 1 }), // 2/1/1: 01000000 (3)
|
||||
std::make_unique<Tile>(Tile::ID { 2, 1, 2 }), // 2/1/2: 01100000 (3)
|
||||
std::make_unique<Tile>(Tile::ID { 2, 2, 0 }), // 2/2/0: 00100000 (3)
|
||||
}};
|
||||
|
||||
std::list<Tile *> tile_ptrs;
|
||||
std::transform(tiles.begin(), tiles.end(), std::back_inserter(tile_ptrs), [](std::unique_ptr<Tile> &tile) { return tile.get(); });
|
||||
|
||||
updateClipIDs(tile_ptrs);
|
||||
|
||||
// for (const auto &it : tiles) {
|
||||
// std::cout << std::string(it->id) << ": " << it->clip.mask << " (" << (int)it->clip.length << ")" << std::endl;
|
||||
// }
|
||||
|
||||
ASSERT_EQ(std::bitset<8>("11000000"), tiles[0]->clip.mask); ASSERT_EQ(3, tiles[0]->clip.length);
|
||||
ASSERT_EQ(std::bitset<8>("11100000"), tiles[1]->clip.mask); ASSERT_EQ(3, tiles[1]->clip.length);
|
||||
ASSERT_EQ(std::bitset<8>("10000000"), tiles[2]->clip.mask); ASSERT_EQ(3, tiles[2]->clip.length);
|
||||
ASSERT_EQ(std::bitset<8>("10100000"), tiles[3]->clip.mask); ASSERT_EQ(3, tiles[3]->clip.length);
|
||||
ASSERT_EQ(std::bitset<8>("01000000"), tiles[4]->clip.mask); ASSERT_EQ(3, tiles[4]->clip.length);
|
||||
ASSERT_EQ(std::bitset<8>("01100000"), tiles[5]->clip.mask); ASSERT_EQ(3, tiles[5]->clip.length);
|
||||
ASSERT_EQ(std::bitset<8>("00100000"), tiles[6]->clip.mask); ASSERT_EQ(3, tiles[6]->clip.length);
|
||||
}
|
||||
|
||||
TEST(ClipIDs, MultipleLevels) {
|
||||
std::array<std::unique_ptr<Tile>, 12> tiles {{
|
||||
std::make_unique<Tile>(Tile::ID { 2, 0, 0 }), // 2/0/0: 10000000 (1)
|
||||
std::make_unique<Tile>(Tile::ID { 3, 0, 0 }), // 3/0/0: 10000000 (3)
|
||||
std::make_unique<Tile>(Tile::ID { 3, 0, 1 }), // 3/0/1: 11100000 (3)
|
||||
std::make_unique<Tile>(Tile::ID { 4, 0, 2 }), // 4/0/2: 11110000 (5)
|
||||
std::make_unique<Tile>(Tile::ID { 4, 1, 2 }), // 4/0/3: 11111000 (5)
|
||||
std::make_unique<Tile>(Tile::ID { 4, 0, 3 }), // 4/1/2: 11100000 (5)
|
||||
std::make_unique<Tile>(Tile::ID { 4, 1, 3 }), // 4/1/3: 11101000 (5)
|
||||
std::make_unique<Tile>(Tile::ID { 3, 1, 0 }), // 3/1/0: 10100000 (3)
|
||||
std::make_unique<Tile>(Tile::ID { 3, 1, 1 }), // 3/1/1: 11000000 (3)
|
||||
std::make_unique<Tile>(Tile::ID { 2, 1, 0 }), // 2/1/0: 01000000 (2)
|
||||
std::make_unique<Tile>(Tile::ID { 3, 2, 0 }), // 3/2/0: 01010000 (4)
|
||||
std::make_unique<Tile>(Tile::ID { 3, 2, 1 }), // 3/2/1: 01100000 (3)
|
||||
}};
|
||||
|
||||
// Use a random order to verify that the result of this algorithm is independent of the
|
||||
std::random_shuffle(tiles.begin(), tiles.end());
|
||||
|
||||
std::list<Tile *> tile_ptrs;
|
||||
std::transform(tiles.begin(), tiles.end(), std::back_inserter(tile_ptrs), [](std::unique_ptr<Tile> &tile) { return tile.get(); });
|
||||
|
||||
updateClipIDs(tile_ptrs);
|
||||
|
||||
// Sort them by tile ID so that we know what order we have to test in.
|
||||
std::sort(tiles.begin(), tiles.end(), [](const std::unique_ptr<Tile> &a, const std::unique_ptr<Tile> &b) {
|
||||
return a->id < b->id;
|
||||
});
|
||||
|
||||
// for (const auto &it : tiles) {
|
||||
// std::cout << std::string(it->id) << ": " << it->clip.mask << " (" << (int)it->clip.length << ")" << std::endl;
|
||||
// }
|
||||
|
||||
ASSERT_EQ(std::string("2/0/0"), std::string(tiles[0]->id));
|
||||
ASSERT_EQ(std::bitset<8>("10000000"), tiles[0]->clip.mask);
|
||||
ASSERT_EQ(1, tiles[0]->clip.length);
|
||||
|
||||
ASSERT_EQ(std::string("2/1/0"), std::string(tiles[1]->id));
|
||||
ASSERT_EQ(std::bitset<8>("01000000"), tiles[1]->clip.mask);
|
||||
ASSERT_EQ(2, tiles[1]->clip.length);
|
||||
|
||||
ASSERT_EQ(std::string("3/0/0"), std::string(tiles[2]->id));
|
||||
ASSERT_EQ(std::bitset<8>("10000000"), tiles[2]->clip.mask);
|
||||
ASSERT_EQ(3, tiles[2]->clip.length);
|
||||
|
||||
ASSERT_EQ(std::string("3/0/1"), std::string(tiles[3]->id));
|
||||
ASSERT_EQ(std::bitset<8>("11100000"), tiles[3]->clip.mask);
|
||||
ASSERT_EQ(3, tiles[3]->clip.length);
|
||||
|
||||
ASSERT_EQ(std::string("3/1/0"), std::string(tiles[4]->id));
|
||||
ASSERT_EQ(std::bitset<8>("10100000"), tiles[4]->clip.mask);
|
||||
ASSERT_EQ(3, tiles[4]->clip.length);
|
||||
|
||||
ASSERT_EQ(std::string("3/1/1"), std::string(tiles[5]->id));
|
||||
ASSERT_EQ(std::bitset<8>("11000000"), tiles[5]->clip.mask);
|
||||
ASSERT_EQ(3, tiles[5]->clip.length);
|
||||
|
||||
ASSERT_EQ(std::string("3/2/0"), std::string(tiles[6]->id));
|
||||
ASSERT_EQ(std::bitset<8>("01010000"), tiles[6]->clip.mask);
|
||||
ASSERT_EQ(4, tiles[6]->clip.length);
|
||||
|
||||
ASSERT_EQ(std::string("3/2/1"), std::string(tiles[7]->id));
|
||||
ASSERT_EQ(std::bitset<8>("01100000"), tiles[7]->clip.mask);
|
||||
ASSERT_EQ(3, tiles[7]->clip.length);
|
||||
|
||||
ASSERT_EQ(std::string("4/0/2"), std::string(tiles[8]->id));
|
||||
ASSERT_EQ(std::bitset<8>("11110000"), tiles[8]->clip.mask);
|
||||
ASSERT_EQ(5, tiles[8]->clip.length);
|
||||
|
||||
ASSERT_EQ(std::string("4/0/3"), std::string(tiles[9]->id));
|
||||
ASSERT_EQ(std::bitset<8>("11111000"), tiles[9]->clip.mask);
|
||||
ASSERT_EQ(5, tiles[9]->clip.length);
|
||||
|
||||
ASSERT_EQ(std::string("4/1/2"), std::string(tiles[10]->id));
|
||||
ASSERT_EQ(std::bitset<8>("11100000"), tiles[10]->clip.mask);
|
||||
ASSERT_EQ(5, tiles[10]->clip.length);
|
||||
|
||||
ASSERT_EQ(std::string("4/1/3"), std::string(tiles[11]->id));
|
||||
ASSERT_EQ(std::bitset<8>("11101000"), tiles[11]->clip.mask);
|
||||
ASSERT_EQ(5, tiles[11]->clip.length);
|
||||
}
|
||||
|
||||
TEST(ClipIDs, DuplicateIDs) {
|
||||
|
||||
std::forward_list<Tile::ID> tiles {{
|
||||
Tile::ID { 2, 0, 0 },
|
||||
Tile::ID { 2, 0, 1 },
|
||||
Tile::ID { 2, 0, 0 },
|
||||
Tile::ID { 2, 0, 1 },
|
||||
Tile::ID { 2, 0, 1 },
|
||||
}};
|
||||
|
||||
std::map<Tile::ID, ClipID> mapping = computeClipIDs(tiles);
|
||||
|
||||
ASSERT_EQ(2ull, mapping.size());
|
||||
|
||||
// for (const auto &it : mapping) {
|
||||
// std::cout << std::string(it.first) << "(" << it.first << ")" << ": " << it.second.mask << " (" << (int)it.second.length << ")" << std::endl;
|
||||
// }
|
||||
|
||||
ASSERT_EQ(std::bitset<8>("01000000"), mapping[Tile::ID(2, 0, 0)].mask);
|
||||
ASSERT_EQ(2, mapping[Tile::ID(2, 0, 0)].length);
|
||||
|
||||
ASSERT_EQ(std::bitset<8>("10000000"), mapping[Tile::ID(2, 0, 1)].mask);
|
||||
ASSERT_EQ(1, mapping[Tile::ID(2, 0, 1)].length);
|
||||
}
|
||||
@@ -64,6 +64,22 @@
|
||||
"../llmr.gyp:llmr-x86"
|
||||
]
|
||||
},
|
||||
{
|
||||
"target_name": "clip_ids",
|
||||
"product_name": "test_clip_ids",
|
||||
"type": "executable",
|
||||
"libraries": [
|
||||
"-lpthread",
|
||||
],
|
||||
"sources": [
|
||||
"./main.cpp",
|
||||
"./clip_ids.cpp",
|
||||
],
|
||||
"dependencies": [
|
||||
"../deps/gtest/gtest.gyp:gtest",
|
||||
"../llmr.gyp:llmr-x86"
|
||||
]
|
||||
},
|
||||
{
|
||||
"target_name": "variant",
|
||||
"product_name": "test_variant",
|
||||
@@ -80,6 +96,22 @@
|
||||
"../llmr.gyp:llmr-x86"
|
||||
]
|
||||
},
|
||||
{
|
||||
"target_name": "tile",
|
||||
"product_name": "test_tile",
|
||||
"type": "executable",
|
||||
"libraries": [
|
||||
"-lpthread",
|
||||
],
|
||||
"sources": [
|
||||
"./main.cpp",
|
||||
"./tile.cpp",
|
||||
],
|
||||
"dependencies": [
|
||||
"../deps/gtest/gtest.gyp:gtest",
|
||||
"../llmr.gyp:llmr-x86"
|
||||
]
|
||||
},
|
||||
{
|
||||
"target_name": "headless",
|
||||
"product_name": "test_headless",
|
||||
@@ -105,7 +137,9 @@
|
||||
"type": "none",
|
||||
"dependencies": [
|
||||
"rotation_range",
|
||||
"clip_ids",
|
||||
"variant",
|
||||
"tile",
|
||||
],
|
||||
}
|
||||
]
|
||||
|
||||
50
test/tile.cpp
Normal file
50
test/tile.cpp
Normal file
@@ -0,0 +1,50 @@
|
||||
#include <iostream>
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include <llmr/map/tile.hpp>
|
||||
|
||||
using namespace llmr;
|
||||
|
||||
|
||||
TEST(Variant, isChild) {
|
||||
ASSERT_TRUE(Tile::ID(1, 0, 0).isChildOf(Tile::ID(0, 0, 0)));
|
||||
ASSERT_TRUE(Tile::ID(1, 1, 0).isChildOf(Tile::ID(0, 0, 0)));
|
||||
ASSERT_TRUE(Tile::ID(1, 2, 0).isChildOf(Tile::ID(0, 1, 0)));
|
||||
ASSERT_TRUE(Tile::ID(1, 3, 0).isChildOf(Tile::ID(0, 1, 0)));
|
||||
ASSERT_TRUE(Tile::ID(1, 4, 0).isChildOf(Tile::ID(0, 2, 0)));
|
||||
ASSERT_TRUE(Tile::ID(1, 5, 0).isChildOf(Tile::ID(0, 2, 0)));
|
||||
ASSERT_TRUE(Tile::ID(2, 0, 0).isChildOf(Tile::ID(0, 0, 0)));
|
||||
|
||||
ASSERT_TRUE(Tile::ID(2, 8, 0).isChildOf(Tile::ID(0, 2, 0)));
|
||||
ASSERT_TRUE(Tile::ID(2, 9, 0).isChildOf(Tile::ID(0, 2, 0)));
|
||||
ASSERT_TRUE(Tile::ID(2, 10, 0).isChildOf(Tile::ID(0, 2, 0)));
|
||||
ASSERT_TRUE(Tile::ID(2, 11, 0).isChildOf(Tile::ID(0, 2, 0)));
|
||||
ASSERT_TRUE(Tile::ID(2, 12, 0).isChildOf(Tile::ID(0, 3, 0)));
|
||||
ASSERT_TRUE(Tile::ID(2, 13, 0).isChildOf(Tile::ID(0, 3, 0)));
|
||||
|
||||
ASSERT_TRUE(Tile::ID(1, -1, 0).isChildOf(Tile::ID(0, -1, 0)));
|
||||
ASSERT_TRUE(Tile::ID(1, -2, 0).isChildOf(Tile::ID(0, -1, 0)));
|
||||
ASSERT_TRUE(Tile::ID(1, -3, 0).isChildOf(Tile::ID(0, -2, 0)));
|
||||
ASSERT_TRUE(Tile::ID(1, -4, 0).isChildOf(Tile::ID(0, -2, 0)));
|
||||
ASSERT_TRUE(Tile::ID(2, -1, 0).isChildOf(Tile::ID(0, -1, 0)));
|
||||
ASSERT_TRUE(Tile::ID(2, -2, 0).isChildOf(Tile::ID(0, -1, 0)));
|
||||
ASSERT_TRUE(Tile::ID(2, -3, 0).isChildOf(Tile::ID(0, -1, 0)));
|
||||
ASSERT_TRUE(Tile::ID(2, -4, 0).isChildOf(Tile::ID(0, -1, 0)));
|
||||
ASSERT_TRUE(Tile::ID(2, -5, 0).isChildOf(Tile::ID(0, -2, 0)));
|
||||
ASSERT_TRUE(Tile::ID(2, -6, 0).isChildOf(Tile::ID(0, -2, 0)));
|
||||
ASSERT_TRUE(Tile::ID(2, -7, 0).isChildOf(Tile::ID(0, -2, 0)));
|
||||
ASSERT_TRUE(Tile::ID(2, -8, 0).isChildOf(Tile::ID(0, -2, 0)));
|
||||
|
||||
ASSERT_FALSE(Tile::ID(4, -16, 0).isChildOf(Tile::ID(0, -2, 0)));
|
||||
ASSERT_TRUE(Tile::ID(4, -17, 0).isChildOf(Tile::ID(0, -2, 0)));
|
||||
|
||||
ASSERT_TRUE(Tile::ID(2, -1, 0).isChildOf(Tile::ID(1, -1, 0)));
|
||||
ASSERT_TRUE(Tile::ID(2, -2, 0).isChildOf(Tile::ID(1, -1, 0)));
|
||||
ASSERT_TRUE(Tile::ID(2, -3, 0).isChildOf(Tile::ID(1, -2, 0)));
|
||||
ASSERT_TRUE(Tile::ID(2, -4, 0).isChildOf(Tile::ID(1, -2, 0)));
|
||||
ASSERT_TRUE(Tile::ID(3, -1, 0).isChildOf(Tile::ID(1, -1, 0)));
|
||||
ASSERT_TRUE(Tile::ID(3, -2, 0).isChildOf(Tile::ID(1, -1, 0)));
|
||||
ASSERT_TRUE(Tile::ID(3, -3, 0).isChildOf(Tile::ID(1, -1, 0)));
|
||||
ASSERT_TRUE(Tile::ID(3, -4, 0).isChildOf(Tile::ID(1, -1, 0)));
|
||||
ASSERT_TRUE(Tile::ID(3, -5, 0).isChildOf(Tile::ID(1, -2, 0)));
|
||||
}
|
||||
Reference in New Issue
Block a user