Merge branch 'master' into outdoors-style

Conflicts:
	bin/style.js
	src/map/map.cpp
This commit is contained in:
Konstantin Käfer
2014-05-13 16:18:52 +02:00
64 changed files with 2047 additions and 934 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -2,6 +2,10 @@
module.exports = {
"buckets": {
"satellite": {
"source": "satellite",
"type": "raster"
},
"admin_maritime": {
"source": "outdoors",
"layer": "admin",

View File

@@ -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;

View 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

View File

@@ -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;
};
}

View File

@@ -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;
};
}

View File

@@ -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 &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;
};
}

View File

@@ -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;

View 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

View File

@@ -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() {}
};

View 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

View File

@@ -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);

View File

@@ -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);

View File

@@ -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;

View File

@@ -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);

View File

@@ -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;
};
}

View File

@@ -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:

View File

@@ -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);

View File

@@ -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 {

View File

@@ -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 &map;
mutable std::mutex mtx;
std::string body;
bool loaded = false;
std::string image;
std::atomic<bool> loaded;
std::map<std::string, SpritePosition> pos;
};

View File

@@ -28,6 +28,7 @@ public:
void loadJSON(const uint8_t *const data, size_t bytes);
size_t layerCount() const;
void cascade(float z);
public:

View 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

View File

@@ -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;

View File

@@ -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

View File

@@ -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

View File

@@ -2,4 +2,7 @@
@interface MBXViewController : UIViewController
- (void)saveState;
- (void)restoreState;
@end

View File

@@ -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;
}

View File

@@ -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",

View File

@@ -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
View 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

View File

@@ -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

View File

@@ -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);
}
}
}
}

View 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();
}

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -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;
}
}

View File

@@ -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);
}

View 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;
}
}

View 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()));
}

View File

@@ -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) {

View File

@@ -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 {

View File

@@ -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);

View 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();
}

View 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);
}

View 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);
}

View 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);
}

View 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);
}

View 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);
}

View 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);
}

View File

@@ -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;
}

View File

@@ -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();
}

View File

@@ -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);

View File

@@ -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

View File

@@ -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

View File

@@ -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;

View File

@@ -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
}
};
}

View File

@@ -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
View 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;
}
}

View File

@@ -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);

View File

@@ -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
View 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);
}

View File

@@ -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
View 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)));
}