use sprite atlas

This commit is contained in:
Konstantin Käfer
2014-05-19 10:48:11 +02:00
parent 24bd0bb667
commit 13575330f7
20 changed files with 439 additions and 86 deletions

View File

@@ -231,5 +231,34 @@ void show_debug_image(std::string name, const char *data, size_t width, size_t h
glfwMakeContextCurrent(current_window);
}
void show_color_debug_image(std::string name, const char *data, size_t width, size_t height) {
static GLFWwindow *debug_window = nullptr;
if (!debug_window) {
debug_window = glfwCreateWindow(width, height, name.c_str(), nullptr, nullptr);
if (!debug_window) {
glfwTerminate();
fprintf(stderr, "Failed to initialize window\n");
exit(1);
}
}
GLFWwindow *current_window = glfwGetCurrentContext();
glfwSetWindowSize(debug_window, width, height);
glfwMakeContextCurrent(debug_window);
int fb_width, fb_height;
glfwGetFramebufferSize(debug_window, &fb_width, &fb_height);
float scale = (float)fb_width / (float)width;
glPixelZoom(scale, -scale);
glRasterPos2f(-1.0f, 1.0f);
glDrawPixels(width, height, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, data);
glfwSwapBuffers(debug_window);
glfwMakeContextCurrent(current_window);
}
}
}

View File

@@ -0,0 +1,68 @@
#ifndef LLMR_GEOMETRY_SPRITE_ATLAS
#define LLMR_GEOMETRY_SPRITE_ATLAS
#include <llmr/geometry/binpack.hpp>
#include <string>
#include <map>
#include <mutex>
#include <atomic>
#include <set>
namespace llmr {
class Sprite;
class SpriteAtlas {
public:
typedef uint16_t dimension;
public:
// Add way to construct this from another SpriteAtlas (e.g. with another pixelRatio)
SpriteAtlas(dimension width, dimension height);
~SpriteAtlas();
// Changes the pixel ratio.
bool resize(float newRatio);
// Update uninitialized sprites in this atlas from the given sprite.
void update(const Sprite &sprite);
// Returns the coordinates of the icon. The getter also *creates* new icons in the atlas
// if they don't exist, but they'll be default-initialized with a a black circle.
Rect<dimension> getIcon(int size, const std::string &name);
// Updates or creates an icon with a given size and name. The data must be a
// a (pixelRatio * size)^2 * 4 byte long RGBA image buffer.
Rect<dimension> setIcon(int size, const std::string &name, const std::string &icon);
// Binds the image buffer of this sprite atlas to the GPU, and uploads data if it is out
// of date.
void bind(bool linear = false);
inline float getTextureWidth() const { return width * pixelRatio; }
inline float getTextureHeight() const { return height * pixelRatio; }
private:
void allocate();
public:
const dimension width = 0;
const dimension height = 0;
private:
std::mutex mtx;
float pixelRatio = 1.0f;
BinPack<dimension> bin;
std::map<int, std::map<std::string, Rect<dimension>>> index;
std::set<std::pair<int, std::string>> uninitialized;
char *data = nullptr;
std::atomic<bool> dirty;
uint32_t texture = 0;
uint32_t filter = 0;
static const int buffer = 1;
};
};
#endif

View File

@@ -92,6 +92,7 @@ public:
inline const TransformState &getState() const { return state; }
inline const Style &getStyle() const { return style; }
inline GlyphAtlas &getGlyphAtlas() { return glyphAtlas; }
inline SpriteAtlas &getSpriteAtlas() { return spriteAtlas; }
inline uv_loop_t *getLoop() { return loop; }
inline time getAnimationTime() const { return animationTime; }
inline Texturepool &getTexturepool() { return texturepool; }
@@ -143,6 +144,7 @@ private:
Texturepool texturepool;
Style style;
GlyphAtlas glyphAtlas;
SpriteAtlas spriteAtlas;
Painter painter;
std::map<std::string, const std::unique_ptr<Source>> sources;

View File

@@ -9,13 +9,14 @@ namespace llmr {
class Style;
class GlyphAtlas;
class SpriteAtlas;
class LayerDescription;
class Bucket;
class TileParser {
public:
TileParser(const std::string& data, VectorTileData& tile, const Style& style, GlyphAtlas& glyphAtlas);
TileParser(const std::string& data, VectorTileData& tile, const Style& style, GlyphAtlas& glyphAtlas, SpriteAtlas &spriteAtlas);
private:
bool obsolete() const;
@@ -27,13 +28,14 @@ private:
std::unique_ptr<Bucket> createIconBucket(const VectorTileLayer& layer, const BucketDescription& bucket_desc);
std::unique_ptr<Bucket> createTextBucket(const VectorTileLayer& layer, const BucketDescription& bucket_desc);
template <class Bucket> void addBucketFeatures(Bucket& bucket, const VectorTileLayer& layer, const BucketDescription& bucket_desc);
template <class Bucket, typename... Args> void addBucketFeatures(Bucket& bucket, const VectorTileLayer& layer, const BucketDescription& bucket_desc, Args... args);
template <class Bucket, typename ...Args> void addBucketFeatures(Bucket& bucket, const VectorTileLayer& layer, const BucketDescription& bucket_desc, Args&& ...args);
private:
const VectorTile vector_data;
VectorTileData& tile;
const Style& style;
GlyphAtlas& glyphAtlas;
SpriteAtlas &spriteAtlas;
Faces faces;
Placement placement;
};

View File

@@ -32,8 +32,11 @@ std::shared_ptr<Request> request_http(const std::string &url,
// Cancels an HTTP request.
void cancel_request_http(const std::shared_ptr<Request> &req);
// Shows an RGBA image with the specified dimensions in a named window.
// Shows an alpha image with the specified dimensions in a named window.
void show_debug_image(std::string name, const char *data, size_t width, size_t height);
// Shows an alpha image with the specified dimensions in a named window.
void show_color_debug_image(std::string name, const char *data, size_t width, size_t height);
}
}

View File

@@ -20,7 +20,7 @@ class IconVertexBuffer;
class BucketDescription;
class IconShader;
class DotShader;
class Sprite;
class SpriteAtlas;
class VectorTileFeature;
class IconBucket : public Bucket {
@@ -31,7 +31,7 @@ public:
virtual void render(Painter& painter, const std::string& layer_name, const Tile::ID& id);
virtual bool hasData() const;
void addFeature(const VectorTileFeature &feature, const std::shared_ptr<Sprite> &sprite);
void addFeature(const VectorTileFeature &feature, SpriteAtlas &sprite_atlas);
void drawIcons(IconShader& shader);
void drawIcons(DotShader& shader);

View File

@@ -7,6 +7,7 @@
#include <llmr/style/bucket_description.hpp>
#include <llmr/style/layer_description.hpp>
#include <llmr/style/class_description.hpp>
#include <llmr/geometry/sprite_atlas.hpp>
#include <map>
#include <vector>

View File

@@ -87,12 +87,28 @@ inline T dist(const S1& a, const S2& b) {
return c;
}
template <typename T>
inline T length(T a, T b) {
return std::sqrt(a * a + b * b);
}
// Take the magnitude of vector a.
template <typename T = double, typename S>
inline T mag(const S& a) {
return std::sqrt(a.x * a.x + a.y * a.y);
}
template <typename T>
T clamp(T value, T min, T max) {
return value < min ? min : (value > max ? max : value);
}
template <typename T>
T smoothstep(T edge0, T edge1, T x) {
T t = clamp((x - edge0) / (edge1 - edge0), T(0), T(1));
return t * t * (T(3) - T(2) * t);
}
}
}

View File

@@ -32,6 +32,8 @@ public:
bool needsTransition() const;
void updateTransitions(time now);
inline const char *getData() const { return img; }
public:
// loaded image dimensions
uint32_t width = 0, height = 0;

View File

@@ -9,6 +9,11 @@ struct Rect {
T x = 0, y = 0;
T w = 0, h = 0;
template <typename Number>
Rect operator *(Number value) const {
return Rect(x * value, y * value, w * value, h * value);
}
operator bool() const { return w == 0 || h == 0; }
};
}

View File

@@ -0,0 +1,235 @@
#include <llmr/geometry/sprite_atlas.hpp>
#include <llmr/platform/gl.hpp>
#include <llmr/platform/platform.hpp>
#include <llmr/util/math.hpp>
#include <llmr/util/std.hpp>
#include <llmr/style/sprite.hpp>
#include <cassert>
#include <cmath>
#include <algorithm>
using namespace llmr;
SpriteAtlas::SpriteAtlas(dimension width, dimension height)
: width(width),
height(height),
bin(width, height),
dirty(true) {
}
bool SpriteAtlas::resize(const float newRatio) {
if (pixelRatio == newRatio) return false;
std::lock_guard<std::mutex> lock(mtx);
const float oldRatio = pixelRatio;
pixelRatio = newRatio;
if (data) {
char *old_data = data;
data = nullptr;
allocate();
dimension w = static_cast<dimension>(width * newRatio);
dimension h = static_cast<dimension>(height * newRatio);
float s = std::pow(oldRatio / newRatio, 2);
// Basic image scaling. TODO: Replace this with better image scaling.
uint32_t *img_new = reinterpret_cast<uint32_t *>(data);
uint32_t *img_old = reinterpret_cast<uint32_t *>(old_data);
for (size_t i = 0, length = w * h; i < length; i++) {
img_new[i] = img_old[static_cast<size_t>(s * i)];
}
free(old_data);
dirty = true;
}
return dirty;
}
Rect<SpriteAtlas::dimension> SpriteAtlas::getIcon(const int size, const std::string &name) {
std::lock_guard<std::mutex> lock(mtx);
std::map<std::string, Rect<dimension>> &size_index = index[size];
auto rect_it = size_index.find(name);
if (rect_it != size_index.end()) {
return rect_it->second;
}
// We have to allocate a new area in the bin, and store an empty image in it.
// Add a 1px border around every image.
const dimension pack_size = size + 2 * buffer;
Rect<dimension> rect = bin.allocate(pack_size, pack_size);
if (rect.w == 0) {
fprintf(stderr, "sprite atlas bitmap overflow");
return rect;
}
size_index.emplace(name, rect);
allocate();
// Draw an antialiased circle.
const int img_size = size * pixelRatio;
const int img_offset_x = (rect.x + buffer) * pixelRatio;
const int img_offset_y = (rect.y + buffer) * pixelRatio;
uint32_t *sprite_img = reinterpret_cast<uint32_t *>(data);
const float blur = 1.5f / size;
const uint8_t r = 0x7F;
const uint8_t g = 0x7F;
const uint8_t b = 0x7F;
const int sprite_stride = width * pixelRatio;
for (int y = 0; y < img_size; y++) {
const int img_y = (img_offset_y + y) * sprite_stride + img_offset_x;
for (int x = 0; x < img_size; x++) {
const float dist = util::length(float(x) / img_size - 0.5f, float(y) / img_size - 0.5f);
const float t = util::smoothstep(0.5f, 0.5f - blur, dist);
const uint8_t alpha = t * 255;
uint32_t color = (uint32_t(r * t) << 0) |
(uint32_t(g * t) << 8) |
(uint32_t(b * t) << 16) |
(uint32_t(alpha) << 24);
sprite_img[img_y + x] = color;
}
}
uninitialized.emplace(size, name);
dirty = true;
return rect;
}
void SpriteAtlas::allocate() {
if (!data) {
dimension w = static_cast<dimension>(width * pixelRatio);
dimension h = static_cast<dimension>(height * pixelRatio);
data = (char *)calloc(w * h, sizeof(uint32_t));
}
}
Rect<SpriteAtlas::dimension> SpriteAtlas::setIcon(const int size, const std::string &name, const std::string &icon) {
Rect<dimension> rect = getIcon(size, name);
// Copy the bitmap
const int img_size = size * pixelRatio;
const int img_offset_x = (rect.x + buffer) * pixelRatio;
const int img_offset_y = (rect.y + buffer) * pixelRatio;
if (std::pow(size * pixelRatio, 2) * 4 /* rgba */ != icon.size()) { fprintf(stderr, "mismatched icon buffer size!"); }
const uint32_t *icon_img = reinterpret_cast<const uint32_t *>(icon.data());
uint32_t *sprite_img = reinterpret_cast<uint32_t *>(data);
const int sprite_stride = width * pixelRatio;
const int icon_stride = size * pixelRatio;
for (size_t y = 0; y < img_size; y++) {
const int img_y = (img_offset_y + y) * sprite_stride + img_offset_x;
const int icon_y = y * icon_stride;
for (size_t x = 0; x < img_size; x++) {
sprite_img[img_y + x] = icon_img[icon_y + x];
}
}
dirty = true;
return rect;
}
void SpriteAtlas::update(const Sprite &sprite) {
if (!sprite.isLoaded()) return;
SpriteAtlas &atlas = *this;
std::erase_if(uninitialized, [&sprite, &atlas](const std::pair<int, std::string> &pair) {
const int &size = pair.first;
const std::string &name = pair.second;
const SpritePosition &src = sprite.getSpritePosition(name + "-" + std::to_string(size));
if (src.width == size && src.height == size && src.pixelRatio == atlas.pixelRatio) {
const uint32_t *src_img = reinterpret_cast<const uint32_t *>(sprite.raster.getData());
uint32_t *dst_img = reinterpret_cast<uint32_t *>(atlas.data);
Rect<dimension> dst = atlas.getIcon(size, name);
dst.x = (dst.x + buffer) * atlas.pixelRatio;
dst.y = (dst.y + buffer) * atlas.pixelRatio;
dst.w = (dst.w - 2 * buffer) * atlas.pixelRatio;
dst.h = (dst.h - 2 * buffer) * atlas.pixelRatio;
const int src_image_stride = sprite.raster.width;
const int dst_image_stride = atlas.width * atlas.pixelRatio;
const int src_stride = src.width * src.pixelRatio;
for (int y = 0; y < dst.h; y++) {
const int src_pos = (src.y * src.pixelRatio + y) * src_image_stride + src.x * src.pixelRatio;
const int dst_pos = (dst.y + y) * dst_image_stride + dst.x;
// TODO: this is crashing
// memcpy(dst_img + dst_pos, src_img + src_pos, src_stride * sizeof(uint32_t));
}
return true;
} else {
return false;
}
});
}
void SpriteAtlas::bind(bool linear) {
if (!texture) {
glGenTextures(1, &texture);
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);
} else {
glBindTexture(GL_TEXTURE_2D, texture);
}
GLuint filter_val = linear ? GL_LINEAR : GL_NEAREST;
if (filter_val != filter) {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter_val);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter_val);
filter = filter_val;
}
if (dirty) {
std::lock_guard<std::mutex> lock(mtx);
allocate();
glTexImage2D(
GL_TEXTURE_2D, // GLenum target
0, // GLint level
GL_RGBA, // GLint internalformat
width * pixelRatio, // GLsizei width
height * pixelRatio, // GLsizei height
0, // GLint border
GL_RGBA, // GLenum format
GL_UNSIGNED_BYTE, // GLenum type
data // const GLvoid * data
);
platform::show_color_debug_image("Sprite Atlas", data, width * pixelRatio, height * pixelRatio);
dirty = false;
}
};
SpriteAtlas::~SpriteAtlas() {
std::lock_guard<std::mutex> lock(mtx);
if (data) {
free(data);
data = nullptr;
}
}

View File

@@ -20,6 +20,7 @@ Map::Map(View& view)
texturepool(),
style(),
glyphAtlas(1024, 1024),
spriteAtlas(512, 512),
painter(*this),
loop(uv_loop_new()) {
@@ -175,7 +176,16 @@ void Map::resize(uint16_t width, uint16_t height, float ratio) {
}
void Map::resize(uint16_t width, uint16_t height, float ratio, uint16_t fb_width, uint16_t fb_height) {
bool changed = false;
if (transform.resize(width, height, ratio, fb_width, fb_height)) {
changed = true;
}
if (spriteAtlas.resize(ratio)) {
changed = true;
}
if (changed) {
update();
}
}
@@ -393,6 +403,11 @@ void Map::prepare() {
style.sprite->load(kSpriteURL);
}
// Allow the sprite atlas to potentially pull new sprite images if needed.
if (style.sprite && style.sprite->isLoaded()) {
spriteAtlas.update(*style.sprite);
}
updateTiles();
updateClippingIDs();

View File

@@ -14,11 +14,12 @@
using namespace llmr;
TileParser::TileParser(const std::string& data, VectorTileData& tile, const Style& style, GlyphAtlas& glyphAtlas)
TileParser::TileParser(const std::string& data, VectorTileData& tile, const Style& style, GlyphAtlas& glyphAtlas, SpriteAtlas &spriteAtlas)
: vector_data(pbf((const uint8_t *)data.data(), data.size())),
tile(tile),
style(style),
glyphAtlas(glyphAtlas),
spriteAtlas(spriteAtlas),
placement(tile.id.z) {
parseGlyphs();
parseStyleLayers(style.layers);
@@ -120,12 +121,12 @@ void TileParser::addBucketFeatures(Bucket& bucket, const VectorTileLayer& layer,
}
}
template <class Bucket, typename... Args>
void TileParser::addBucketFeatures(Bucket& bucket, const VectorTileLayer& layer, const BucketDescription& bucket_desc, Args... args) {
template <class Bucket, typename ...Args>
void TileParser::addBucketFeatures(Bucket& bucket, const VectorTileLayer& layer, const BucketDescription& bucket_desc, Args&& ...args) {
FilteredVectorTileLayer filtered_layer(layer, bucket_desc);
for (const pbf& feature_pbf : filtered_layer) {
if (obsolete()) return;
bucket->addFeature({ feature_pbf, layer }, args...);
bucket->addFeature({ feature_pbf, layer }, std::forward<Args>(args)...);
}
}
@@ -146,7 +147,7 @@ std::unique_ptr<Bucket> TileParser::createLineBucket(const VectorTileLayer& laye
std::unique_ptr<Bucket> TileParser::createIconBucket(const VectorTileLayer& layer, const BucketDescription& bucket_desc) {
std::unique_ptr<IconBucket> bucket = std::make_unique<IconBucket>(
tile.iconVertexBuffer, bucket_desc);
addBucketFeatures(bucket, layer, bucket_desc, style.sprite);
addBucketFeatures(bucket, layer, bucket_desc, spriteAtlas);
return obsolete() ? nullptr : std::move(bucket);
}

View File

@@ -22,7 +22,7 @@ void VectorTileData::parse() {
// 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());
TileParser parser(data, *this, map.getStyle(), map.getGlyphAtlas(), map.getSpriteAtlas());
} 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();

View File

@@ -23,27 +23,18 @@ IconBucket::IconBucket(IconVertexBuffer& vertexBuffer,
vertex_start(vertexBuffer.index()) {
}
void IconBucket::addFeature(const VectorTileFeature &feature, const std::shared_ptr<Sprite> &sprite) {
// TODO: We somehow need to reparse the stylesheet, or maintain a mapping of id => sprite name
// For now, we're just not showing points if the sprite was not yet loaded at *parse time* of
// the tile.
if (!sprite || !sprite->isLoaded()) return;
void IconBucket::addFeature(const VectorTileFeature &feature, SpriteAtlas &sprite_atlas) {
auto field_it = feature.properties.find(geometry.field);
if (field_it == feature.properties.end()) {
fprintf(stderr, "feature doesn't contain field '%s'\n", geometry.field.c_str());
// fprintf(stderr, "feature doesn't contain field '%s'\n", geometry.field.c_str());
return;
}
std::string field = toString(field_it->second);
if (geometry.size) {
field.append("-");
field.append(std::to_string(static_cast<int>(std::round(geometry.size))));
}
const SpritePosition &pos = sprite->getSpritePosition(field);
const uint16_t tx = pos.x + pos.width / 2;
const uint16_t ty = pos.y + pos.height / 2;
const Rect<uint16_t> rect = sprite_atlas.getIcon(geometry.size, field);
const uint16_t tx = rect.x + rect.w / 2;
const uint16_t ty = rect.y + rect.h / 2;
Geometry::command cmd;
pbf geom = feature.geometry;

View File

@@ -26,44 +26,27 @@ void Painter::renderIcon(IconBucket& bucket, const std::string& layer_name, cons
const mat4 &vtxMatrix = translatedMatrix(properties.translate, id, properties.translateAnchor);
const std::shared_ptr<Sprite> &sprite = map.getStyle().sprite;
SpriteAtlas &spriteAtlas = map.getSpriteAtlas();
if (!sprite || !sprite->isLoaded()) {
useProgram(dotShader->program);
dotShader->setMatrix(vtxMatrix);
dotShader->setColor(color);
useProgram(iconShader->program);
iconShader->setMatrix(vtxMatrix);
iconShader->setColor(color);
iconShader->setImage(0);
const float iconSize = (properties.radius ? properties.radius * 2 : 8) * map.getState().getPixelRatio();
dotShader->setSize(iconSize);
iconShader->setDimension({{
spriteAtlas.getTextureWidth(),
spriteAtlas.getTextureHeight(),
}});
spriteAtlas.bind(map.getState().isChanging());
const float iconSize = bucket.geometry.size * map.getState().getPixelRatio();
iconShader->setSize(iconSize);
#ifndef GL_ES_VERSION_2_0
glPointSize(iconSize);
glEnable(GL_POINT_SPRITE);
#endif
dotShader->setBlur((properties.blur ? properties.blur : 1.5) / iconSize);
glDepthRange(strata, 1.0f);
bucket.drawIcons(*dotShader);
} else {
useProgram(iconShader->program);
iconShader->setMatrix(vtxMatrix);
iconShader->setColor(color);
iconShader->setImage(0);
iconShader->setDimension({{
static_cast<float>(sprite->raster.width),
static_cast<float>(sprite->raster.height)
}});
sprite->raster.bind(map.getState().isChanging());
const float iconSize = bucket.geometry.size * map.getState().getPixelRatio();
iconShader->setSize(iconSize);
#ifndef GL_ES_VERSION_2_0
glPointSize(iconSize);
glEnable(GL_POINT_SPRITE);
glPointSize(iconSize);
glEnable(GL_POINT_SPRITE);
#endif
glDepthRange(strata, 1.0f);
bucket.drawIcons(*iconShader);
}
glDepthRange(strata, 1.0f);
bucket.drawIcons(*iconShader);
}

View File

@@ -7,5 +7,5 @@ varying vec2 v_tex;
void main() {
vec2 pos = (v_tex + (gl_PointCoord - 0.5) * u_size) / u_dimension;
gl_FragColor = texture2D(u_image, pos) * u_color;
gl_FragColor = texture2D(u_image, pos);
}

View File

@@ -12,7 +12,7 @@ const shader_source llmr::shaders[SHADER_COUNT] = {
},
{
"#version 120\nattribute vec2 a_pos;\nattribute vec2 a_tex;\nuniform mat4 u_matrix;\nuniform float u_size;\nvarying vec2 v_tex;\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 v_tex = a_tex;\n}\n\n",
"#version 120\nuniform sampler2D u_image;\nuniform vec2 u_dimension;\nuniform vec4 u_color;\nuniform float u_size;\nvarying vec2 v_tex;\nvoid main ()\n{\n gl_FragColor = (texture2D (u_image, ((v_tex + \n ((gl_PointCoord - 0.5) * u_size)\n ) / u_dimension)) * u_color);\n}\n\n",
"#version 120\nuniform sampler2D u_image;\nuniform vec2 u_dimension;\nuniform float u_size;\nvarying vec2 v_tex;\nvoid main ()\n{\n gl_FragColor = texture2D (u_image, ((v_tex + (\n (gl_PointCoord - 0.5)\n * u_size)) / u_dimension));\n}\n\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",

View File

@@ -12,7 +12,7 @@ const shader_source llmr::shaders[SHADER_COUNT] = {
},
{
"precision highp float;\nattribute vec2 a_pos;\nattribute vec2 a_tex;\nuniform mat4 u_matrix;\nuniform float u_size;\nvarying vec2 v_tex;\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 v_tex = a_tex;\n}\n\n",
"precision highp float;\nuniform sampler2D u_image;\nuniform vec2 u_dimension;\nuniform vec4 u_color;\nuniform float u_size;\nvarying vec2 v_tex;\nvoid main ()\n{\n mediump vec2 tmpvar_1;\n tmpvar_1 = ((v_tex + (\n (gl_PointCoord - 0.5)\n * u_size)) / u_dimension);\n lowp vec4 tmpvar_2;\n tmpvar_2 = (texture2D (u_image, tmpvar_1) * u_color);\n gl_FragColor = tmpvar_2;\n}\n\n",
"precision highp float;\nuniform sampler2D u_image;\nuniform vec2 u_dimension;\nuniform float u_size;\nvarying vec2 v_tex;\nvoid main ()\n{\n mediump vec2 tmpvar_1;\n tmpvar_1 = ((v_tex + (\n (gl_PointCoord - 0.5)\n * u_size)) / u_dimension);\n lowp vec4 tmpvar_2;\n tmpvar_2 = texture2D (u_image, tmpvar_1);\n gl_FragColor = tmpvar_2;\n}\n\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",

View File

@@ -31,35 +31,35 @@ Sprite::Sprite(Map &map, float pixelRatio)
}
void Sprite::load(const std::string& base_url) {
std::shared_ptr<Sprite> sprite = shared_from_this();
std::shared_ptr<Sprite> sprite = shared_from_this();
std::string suffix = (pixelRatio > 1 ? "@2x" : "");
std::string suffix = (pixelRatio > 1 ? "@2x" : "");
platform::request_http(base_url + suffix + ".json", [sprite](platform::Response *res) {
if (res->code == 200) {
sprite->body.swap(res->body);
sprite->asyncParseJSON();
} else {
fprintf(stderr, "failed to load sprite\n");
}
}, map.getLoop());
platform::request_http(base_url + suffix + ".json", [sprite](platform::Response *res) {
if (res->code == 200) {
sprite->body.swap(res->body);
sprite->asyncParseJSON();
} else {
fprintf(stderr, "failed to load sprite\n");
}
}, map.getLoop());
platform::request_http(base_url + suffix + ".png", [sprite](platform::Response *res) {
if (res->code == 200) {
sprite->image.swap(res->body);
sprite->asyncParseImage();
} else {
fprintf(stderr, "failed to load sprite image\n");
}
}, map.getLoop());
platform::request_http(base_url + suffix + ".png", [sprite](platform::Response *res) {
if (res->code == 200) {
sprite->image.swap(res->body);
sprite->asyncParseImage();
} else {
fprintf(stderr, "failed to load sprite image\n");
}
}, map.getLoop());
}
void Sprite::complete(std::shared_ptr<Sprite> &sprite) {
if (sprite->raster.isLoaded() && sprite->pos.size()) {
sprite->loaded = true;
sprite->map.update();
fprintf(stderr, "sprite loaded\n");
}
if (sprite->raster.isLoaded() && sprite->pos.size()) {
sprite->loaded = true;
sprite->map.update();
fprintf(stderr, "sprite loaded\n");
}
}
bool Sprite::isLoaded() const {