├── .gitignore ├── .gitmodules ├── CMakeLists.txt ├── README.md ├── imgs ├── back.jpg ├── front.jpg ├── hardware.jpg ├── mount.png └── ui.gif ├── landgrab.py ├── parts ├── rpi-screen-mount.skp └── rpi-screen-mount.stl └── src ├── context.cpp ├── context.h ├── gps.cpp ├── gps.h ├── hud ├── button.cpp ├── button.h ├── hud.cpp ├── hud.h ├── shapes.h ├── slideRot.cpp ├── slideRot.h ├── slideZoom.cpp └── slideZoom.h ├── main.cpp ├── platform_rpi.cpp └── string-conversions.h /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "tangram-es"] 2 | path = tangram-es 3 | url = https://github.com/tangrams/tangram-es.git 4 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8) 2 | project(tangram) 3 | 4 | # helper functions 5 | include(${PROJECT_SOURCE_DIR}/tangram-es/toolchains/utils.cmake) 6 | 7 | # cmake output configuration 8 | set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) 9 | set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) 10 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) 11 | 12 | # compiler options 13 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -fpermissive -g") 14 | set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_CXX_FLAGS} -L/opt/vc/lib/ -lGLESv2 -lEGL -lbcm_host -lvchiq_arm -lvcos -lrt -lpthread") 15 | set(CXX_FLAGS_DEBUG "-g -O0") 16 | set(EXECUTABLE_NAME "pi-gps") 17 | 18 | add_definitions(-DPLATFORM_RPI) 19 | 20 | # check for c++11 compiler 21 | execute_process(COMMAND ${CMAKE_CXX_COMPILER} -dumpversion OUTPUT_VARIABLE GCC_VERSION) 22 | 23 | if("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") 24 | if(NOT (GCC_VERSION VERSION_GREATER 4.8 OR GCC_VERSION VERSION_EQUAL 4.8)) 25 | message(FATAL_ERROR "Please install g++ version 4.8 or greater") 26 | else() 27 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") 28 | endif() 29 | else() 30 | message(FATAL_ERROR "Please install a C++11 compatible compiler") 31 | endif() 32 | 33 | # add sources and include headers 34 | find_sources_and_include_directories( 35 | ${PROJECT_SOURCE_DIR}/src/*.h 36 | ${PROJECT_SOURCE_DIR}/src/*.cpp) 37 | 38 | # add sources and include headers 39 | find_sources_and_include_directories( 40 | ${PROJECT_SOURCE_DIR}/tangram-es/linux/src/urlWorker.* 41 | ${PROJECT_SOURCE_DIR}/tangram-es/linux/src/urlWorker.*) 42 | 43 | # include headers for rpi-installed libraries 44 | include_directories(/opt/vc/include/) 45 | include_directories(/opt/vc/include/interface/vcos/pthreads) 46 | include_directories(/opt/vc/include/interface/vmcs_host/linux) 47 | 48 | # load core library 49 | add_subdirectory(${PROJECT_SOURCE_DIR}/tangram-es/core) 50 | include_directories(${CORE_INCLUDE_DIRS}) 51 | include_directories(${CORE_LIBRARIES_INCLUDE_DIRS}) 52 | 53 | # add executable 54 | add_executable(${EXECUTABLE_NAME} ${SOURCES}) 55 | 56 | # copy resources 57 | file(GLOB_RECURSE RESOURCES ${PROJECT_SOURCE_DIR}/tangram-es/core/resources/*) 58 | foreach(_resource ${RESOURCES}) 59 | file(COPY ${_resource} DESTINATION ${PROJECT_SOURCE_DIR}/build/bin) 60 | endforeach() 61 | 62 | # link libraries 63 | target_link_libraries(${EXECUTABLE_NAME} -lcurl) 64 | target_link_libraries(${EXECUTABLE_NAME} core) 65 | 66 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | ![](imgs/ui.gif) 3 | 4 | # Make your own GPS device with Tangram-es and RaspberryPi 5 | 6 | A couple of months ago the nice folks at RaspberryPi published a [blog post](https://www.raspberrypi.org/tangram-an-open-source-map-rendering-library/) about [Tangram-ES](https://github.com/tangrams/tangram-es), Mapzen's native 2D/3D map rendering engine running on their hardware. The feedback was great and people seemed to be very excited to start using it for their own projects. 7 | 8 | Tangram-ES is a work-in-progress map engine written in C++ and openGL ES 2.0, so it can be a little intimidating to start from scratch. That's why we thought this small weekend project could get the ball rolling and ignite some ideas in the community. 9 | 10 | In [this github repository](https://github.com/tangrams/PI-GPS) you will find: 11 | 12 | - A 3D-printable model to mount together a RaspberryPi A+/B+, [Adafruit’s touch HDMI 5’’ 800x480 ](https://www.adafruit.com/product/2260), [Ultimate GPS Raspberry PI HAT](https://www.adafruit.com/products/2324), [Lithium Ion Battery](https://www.adafruit.com/products/353) and [PowerBoost 1000 Charger](https://www.adafruit.com/products/2465) 13 | 14 | - Source code to run Tangram-es with a nice little graphical interface to move, rotate and zoom a map with a touch-only screen 15 | 16 | - Instructions for configuring Tangram-ES to load tiles locally using data for your own city or region 17 | 18 | ## Hardware 19 | 20 | Besides your RaspberryPi I’m using the following components to make a stand alone device: 21 | 22 | - [HDMI 5’’800x480 Touch Screen](https://www.adafruit.com/product/2260) 23 | 24 | - [Ultimate GPS Raspberry PI HAT](https://www.adafruit.com/products/2324) 25 | 26 | - [Lithium Ion Batter](https://www.adafruit.com/products/353) 27 | 28 | - [PowerBoost 1000 Charger](https://www.adafruit.com/products/2465) 29 | 30 | ![](imgs/hardware.jpg) 31 | 32 | These are held together with the 3D-printed mounting station defined in the file: ```parts/rpi-screen-mount.stl```. 33 | 34 | ![](imgs/mount.png) 35 | 36 | Once you put everything together, it should look something like this: 37 | 38 | ![](imgs/front.jpg) 39 | ![](imgs/back.jpg) 40 | 41 | ## Compile and install Tangram-ES with a nice UI for RaspberryPi 42 | 43 | Now let’s jump into the code of this project. 44 | 45 | First we need to clone this repository in your raspberryPi and install some dependences to compile everything. 46 | 47 | ```bash 48 | sudo apt-get update 49 | sudo apt-get install cmake libcurl4-openssl-dev g++-4.8 50 | cd ~ 51 | git clone https://github.com/tangrams/PI-GPS.git 52 | cd PI-GPS 53 | git submodule update --init --recursive 54 | mkdir build 55 | ``` 56 | 57 | Then, just to make sure it is working, compile tangram and then run it. 58 | 59 | ```bash 60 | export CXX=/usr/bin/g++-4.8 61 | cd build 62 | cmake .. 63 | make 64 | cd bin 65 | ./pi-gps -m 66 | ``` 67 | 68 | *Note:* we are running tangram with the ```-m``` flag to display the mouse. 69 | 70 | ## Configure Tangram-ES to fetch tiles locally 71 | 72 | Getting fast internet access to your RaspberryPi could be a problem, especially if you are planning to use this GPS device on your bicycle. Let’s see what you need to do to download the map tiles locally making your map network-independent. 73 | 74 | We installed Tangram-ES and tested it in the previous section. Now is time for us to make some changes so Tangram-ES will search for local files instead of fetching them from a server. Lucky us, we just need to change that from the configuration YAML file. As [the web version of Tangram](https://github.com/tangrams/tangram), this engine is super flexible and you can customize most of it from this file. 75 | 76 | Go and open the ```~/PI-GPS/tangram-es/core/resources/config.yaml``` file. And edit the following line: 77 | 78 | ```yaml 79 | sources: 80 | osm: 81 | type: MVT 82 | url: http://vector.mapzen.com/osm/all/{z}/{x}/{y}.mvt 83 | ``` 84 | 85 | So it looks like: 86 | 87 | ```yaml 88 | sources: 89 | osm: 90 | type: GeoJSONTiles 91 | url: file:///home/pi/PI-GPS/build/bin/tiles/{z}-{x}-{y}.json 92 | ``` 93 | 94 | With these changes, tangram will search for tiles inside the ```tiles/``` directory. 95 | 96 | 97 | ## Get the tiles of a town/city/region from OpenStreetMap 98 | 99 | My colleague and friend [Peter Richardson](https://twitter.com/meetar) made this useful set of python scripts for downloading tiles locally at the repo called [Landgrab](https://github.com/tangrams/landgrab). 100 | 101 | Let’s start by downloading some dependencies for this script. 102 | 103 | ``` 104 | sudo apt-get update 105 | sudo apt-get install wget python-pip 106 | sudo pip install requests 107 | ``` 108 | 109 | Then download Peter’s script in the directory where tangram was compiled: 110 | 111 | ```bash 112 | cd ~/PI-GPS/build/bin 113 | wget https://raw.githubusercontent.com/tangrams/landgrab/master/landgrab.py 114 | ``` 115 | 116 | Now is the time to download some vector tiles. For that we need the [OpenStreetMap](http://www.openstreetmap.org/) ID of the place we want to download. Go to [OpenStreetMap Website](http://www.openstreetmap.org/) and search for a location and check its ID (the number between brackets). 117 | 118 | For example: 119 | 120 | * Buenos Aires (1224652) 121 | * London (65606) 122 | * Manhattan (3954665) 123 | * New York City (175905) 124 | * Paris (7444) 125 | * Rio de Janeiro (2697338) 126 | * Rome (41485) 127 | * Sydney (13766899) 128 | * Tokyo (4479121) 129 | * Tucson (253824) 130 | 131 | **Note**: To change the initial coordinates of the map, open ```~/PI-GPS/tangram-es/core/resources/config.yaml``` again and add a line to the camera block so that it looks like this: 132 | 133 | ```yaml 134 | cameras: 135 | camera1: 136 | position: [-74.00976, 40.705327] 137 | type: perspective 138 | ``` 139 | 140 | Substitute your own longitude and latitude in the position values. Once we choose a place, tangram will start fetching the vector tiles that we will need. For that we will use the python script we downloaded with the given OSM ID and the zoom level we are interested (in our case all of them are from 1 to 18). 141 | 142 | ```bash 143 | python landgrab.py 3954665 1-18 144 | ``` 145 | 146 | This may take a while. Go get a coffee. 147 | 148 | ## Finally! Run it and enjoy your 3D map! 149 | 150 | Well done! Everything is ready, unplug your internet connection and run tangram on your RaspberryPi! 151 | 152 | ```bash 153 | cd ~/PI-GPS/build/bin 154 | ./pi-gps -m 155 | ``` 156 | 157 | I hope you are excited about all the possibilities of having cool 3D maps on your projects. [Send us](http://twitter.com/mapzen) your opinions, feedback or photos of your work! 158 | -------------------------------------------------------------------------------- /imgs/back.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tangrams/PI-GPS/585f2fa07314f2334c0e3889ea2e9c39b90d485b/imgs/back.jpg -------------------------------------------------------------------------------- /imgs/front.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tangrams/PI-GPS/585f2fa07314f2334c0e3889ea2e9c39b90d485b/imgs/front.jpg -------------------------------------------------------------------------------- /imgs/hardware.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tangrams/PI-GPS/585f2fa07314f2334c0e3889ea2e9c39b90d485b/imgs/hardware.jpg -------------------------------------------------------------------------------- /imgs/mount.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tangrams/PI-GPS/585f2fa07314f2334c0e3889ea2e9c39b90d485b/imgs/mount.png -------------------------------------------------------------------------------- /imgs/ui.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tangrams/PI-GPS/585f2fa07314f2334c0e3889ea2e9c39b90d485b/imgs/ui.gif -------------------------------------------------------------------------------- /landgrab.py: -------------------------------------------------------------------------------- 1 | # todo: handle cases where the boundary crosses the dateline 2 | 3 | import requests, json, math, sys, os 4 | import xml.etree.ElementTree as ET 5 | import pprint 6 | import urllib 7 | 8 | OSMID=sys.argv[1] 9 | 10 | if isinstance(sys.argv[2], basestring): 11 | zoom=[] 12 | for part in sys.argv[2].split(','): 13 | if '-' in part: 14 | a, b = part.split('-') 15 | a, b = int(a), int(b) 16 | zoom.extend(range(a, b + 1)) 17 | else: 18 | a = int(part) 19 | zoom.append(a) 20 | else: 21 | zoom=[int(sys.argv[2])] 22 | 23 | coordsonly=0 24 | if len(sys.argv) > 3: 25 | coordsonly=int(sys.argv[3]) 26 | print coordsonly 27 | 28 | success = False 29 | try: 30 | INFILE = 'http://www.openstreetmap.org/api/0.6/relation/'+OSMID+'/full' 31 | print "Downloading", INFILE 32 | r = requests.get(INFILE) 33 | r.raise_for_status() 34 | success = True 35 | except Exception, e: 36 | print e 37 | 38 | if not success: 39 | try: 40 | INFILE = 'http://www.openstreetmap.org/api/0.6/way/'+OSMID+'/full' 41 | print "Downloading", INFILE 42 | r = requests.get(INFILE) 43 | r.raise_for_status() 44 | success = True 45 | except Exception, e: 46 | print e 47 | 48 | if not success: 49 | try: 50 | INFILE = 'http://www.openstreetmap.org/api/0.6/node/'+OSMID 51 | print "Downloading", INFILE 52 | r = requests.get(INFILE) 53 | r.raise_for_status() 54 | success = True 55 | except Exception, e: 56 | print e 57 | print "Element not found, exiting" 58 | sys.exit() 59 | 60 | # print r.encoding 61 | open('outfile.xml', 'w').close() # clear existing OUTFILE 62 | 63 | with open('outfile.xml', 'w') as fd: 64 | fd.write(r.text.encode("UTF-8")) 65 | fd.close() 66 | 67 | try: 68 | tree = ET.parse('outfile.xml') 69 | except Exception, e: 70 | print e 71 | print "XML parse failed, please check outfile.xml" 72 | sys.exit() 73 | 74 | root = tree.getroot() 75 | 76 | ## 77 | ## HELPER FUNCTIONS 78 | ## 79 | 80 | tile_size = 256 81 | half_circumference_meters = 20037508.342789244; 82 | 83 | # Convert lat-lng to mercator meters 84 | def latLngToMeters( coords ): 85 | y = float(coords['y']) 86 | x = float(coords['x']) 87 | # Latitude 88 | y = math.log(math.tan(y*math.pi/360 + math.pi/4)) / math.pi 89 | y *= half_circumference_meters 90 | 91 | # Longitude 92 | x *= half_circumference_meters / 180; 93 | 94 | return {"x": x, "y": y} 95 | 96 | # convert from tile-space coords to meters, depending on zoom 97 | def tile_to_meters(zoom): 98 | return 40075016.68557849 / pow(2, zoom) 99 | 100 | # Given a point in mercator meters and a zoom level, return the tile X/Y/Z that the point lies in 101 | def tileForMeters(coords, zoom): 102 | y = float(coords['y']) 103 | x = float(coords['x']) 104 | return { 105 | "x": math.floor((x + half_circumference_meters) / (half_circumference_meters * 2 / pow(2, zoom))), 106 | "y": math.floor((-y + half_circumference_meters) / (half_circumference_meters * 2 / pow(2, zoom))), 107 | "z": zoom 108 | } 109 | 110 | # Convert tile location to mercator meters - multiply by pixels per tile, then by meters per pixel, adjust for map origin 111 | def metersForTile(tile): 112 | return { 113 | "x": tile['x'] * half_circumference_meters * 2 / pow(2, tile.z) - half_circumference_meters, 114 | "y": -(tile['y'] * half_circumference_meters * 2 / pow(2, tile.z) - half_circumference_meters) 115 | } 116 | 117 | def getTiles(_points,_zoom): 118 | tiles = [] 119 | 120 | ## find tile 121 | for point in _points: 122 | tiles.append(tileForMeters(latLngToMeters({'x':point['x'],'y':point['y']}), _zoom)) 123 | 124 | ## de-dupe 125 | tiles = [dict(tupleized) for tupleized in set(tuple(item.items()) for item in tiles)] 126 | 127 | ## patch holes in tileset 128 | 129 | ## get min and max tiles for lat and long 130 | 131 | minx = 1048577 132 | maxx = -1 133 | miny = 1048577 134 | maxy = -1 135 | 136 | for tile in tiles: 137 | minx = min(minx, tile['x']) 138 | maxx = max(maxx, tile['x']) 139 | miny = min(miny, tile['y']) 140 | maxy = max(maxy, tile['y']) 141 | # print miny, minx, maxy, maxx 142 | 143 | newtiles = [] 144 | 145 | for tile in tiles: 146 | # find furthest tiles from this tile on x and y axes 147 | x = tile['x'] 148 | lessx = 1048577 149 | morex = -1 150 | y = tile['y'] 151 | lessy = 1048577 152 | morey = -1 153 | for t in tiles: 154 | if int(t['x']) == int(tile['x']): 155 | # check on y axis 156 | lessy = min(lessy, t['y']) 157 | morey = max(morey, t['y']) 158 | if int(t['y']) == int(tile['y']): 159 | # check on x axis 160 | lessx = min(lessx, t['x']) 161 | morex = max(morex, t['x']) 162 | 163 | # if a tile is found which is not directly adjacent, add all the tiles between the two 164 | if (lessy + 2) < tile['y']: 165 | for i in range(int(lessy+1), int(tile['y'])): 166 | newtiles.append({'x':tile['x'],'y':i, 'z':_zoom}) 167 | if (morey - 2) > tile['y']: 168 | for i in range(int(morey-1), int(tile['y'])): 169 | newtiles.append({'x':tile['x'],'y':i, 'z':_zoom}) 170 | if (lessx + 2) < tile['x']: 171 | for i in range(int(lessx+1), int(tile['x'])): 172 | newtiles.append({'x':i,'y':tile['y'], 'z':_zoom}) 173 | if (morex - 2) > tile['x']: 174 | for i in range(int(morex-1), int(tile['x'])): 175 | newtiles.append({'x':i,'y':tile['y'], 'z':_zoom}) 176 | 177 | ## de-dupe 178 | newtiles = [dict(tupleized) for tupleized in set(tuple(item.items()) for item in newtiles)] 179 | ## add fill tiles to boundary tiles 180 | tiles = tiles + newtiles 181 | ## de-dupe 182 | tiles = [dict(tupleized) for tupleized in set(tuple(item.items()) for item in tiles)] 183 | 184 | 185 | if coordsonly == 1: 186 | ## output coords 187 | pprint.pprint(tiles) 188 | print "\nFinished: %i tiles at zoom level %i" % (len(tiles), _zoom) 189 | else: 190 | ## download tiles 191 | print "\nDownloading %i tiles at zoom level %i" % (len(tiles), _zoom) 192 | 193 | ## make/empty the tiles folder 194 | folder = "build/bin/tiles" 195 | if not os.path.exists(folder): 196 | os.makedirs(folder) 197 | 198 | total = len(tiles) 199 | if total == 0: 200 | print("Error: no tiles") 201 | exit() 202 | count = 0 203 | sys.stdout.write("\r%d%%" % (float(count)/float(total)*100.)) 204 | sys.stdout.flush() 205 | for tile in tiles: 206 | 207 | dstFile = "%i-%i-%i.json" % (_zoom,tile['x'],tile['y']) 208 | urllib.urlretrieve ("http://vector.mapzen.com/osm/all/%i/%i/%i.mvt?api_key=vector-tiles-HqUVidw" % (_zoom, tile['x'],tile['y']), 209 | "build/bin/tiles/%i-%i-%i.mvt" % (_zoom,tile['x'],tile['y'])) 210 | 211 | count += 1 212 | sys.stdout.write("\r%d%%" % (float(count)/float(total)*100.)) 213 | sys.stdout.flush() 214 | 215 | ## 216 | ## PROCESSING points 217 | ## 218 | 219 | print "Processing:" 220 | 221 | points = [] 222 | for node in root: 223 | if node.tag == "node": 224 | lat = float(node.attrib["lat"]) 225 | lon = float(node.attrib["lon"]) 226 | points.append({'y':lat, 'x':lon}) 227 | 228 | ## 229 | ## GET TILES for all zoom levels 230 | ## 231 | for z in zoom: 232 | getTiles(points,z) 233 | 234 | -------------------------------------------------------------------------------- /parts/rpi-screen-mount.skp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tangrams/PI-GPS/585f2fa07314f2334c0e3889ea2e9c39b90d485b/parts/rpi-screen-mount.skp -------------------------------------------------------------------------------- /src/context.cpp: -------------------------------------------------------------------------------- 1 | #include "context.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "glm/gtc/matrix_transform.hpp" 12 | 13 | #include "util/geom.h" 14 | #include "string-conversions.h" 15 | 16 | // Main global variables 17 | //------------------------------- 18 | #define check() assert(glGetError() == 0) 19 | EGLDisplay display; 20 | EGLSurface surface; 21 | EGLContext context; 22 | 23 | static glm::ivec4 viewport; 24 | static glm::mat4 orthoMatrix; 25 | 26 | #define MOUSE_ID "mouse0" 27 | static int mouse_fd = -1; 28 | typedef struct { 29 | float x,y; 30 | float velX,velY; 31 | int button; 32 | } Mouse; 33 | static Mouse mouse; 34 | static unsigned char keyPressed; 35 | 36 | static bool bRender = true; 37 | 38 | // Mouse stuff 39 | //-------------------------------- 40 | std::string searchForDevice(const std::string& _device) { 41 | std::ifstream file; 42 | std::string buffer; 43 | std::string address = "NONE"; 44 | 45 | file.open("/proc/bus/input/devices"); 46 | 47 | if (!file.is_open()) 48 | return "NOT FOUND"; 49 | 50 | while (!file.eof()) { 51 | getline(file, buffer); 52 | std::size_t found = buffer.find(_device); 53 | if(found!=std::string::npos){ 54 | std::string tmp = buffer.substr(found+_device.size()+1); 55 | std::size_t foundBegining = tmp.find("event"); 56 | if(foundBegining!=std::string::npos){ 57 | address = "/dev/input/"+tmp.substr(foundBegining); 58 | address.erase(address.size()-1,1); 59 | } 60 | break; 61 | } 62 | } 63 | 64 | file.close(); 65 | return address; 66 | } 67 | 68 | void closeMouse(){ 69 | if (mouse_fd > 0) 70 | close(mouse_fd); 71 | mouse_fd = -1; 72 | } 73 | 74 | int initMouse(){ 75 | closeMouse(); 76 | 77 | mouse.x = viewport.z*0.5; 78 | mouse.y = viewport.w*0.5; 79 | std::string mouseAddress = searchForDevice(MOUSE_ID); 80 | std::cout << "Mouse [" << mouseAddress << "]"<< std::endl; 81 | mouse_fd = open(mouseAddress.c_str(), O_RDONLY | O_NONBLOCK); 82 | 83 | return mouse_fd; 84 | } 85 | 86 | 87 | bool readMouseEvent(struct input_event *mousee){ 88 | int bytes; 89 | if (mouse_fd > 0) { 90 | bytes = read(mouse_fd, mousee, sizeof(struct input_event)); 91 | if (bytes == -1) 92 | return false; 93 | else if (bytes == sizeof(struct input_event)) 94 | return true; 95 | } 96 | return false; 97 | } 98 | 99 | bool updateMouse() { 100 | if (mouse_fd < 0) { 101 | return false; 102 | } 103 | 104 | struct input_event mousee; 105 | while ( readMouseEvent(&mousee) ) { 106 | 107 | mouse.velX=0; 108 | mouse.velY=0; 109 | 110 | float x,y = 0.0f; 111 | int button = 0; 112 | 113 | switch(mousee.type) { 114 | // Update Mouse Event 115 | case EV_KEY: 116 | switch (mousee.code) { 117 | case BTN_LEFT: 118 | if (mousee.value == 1){ 119 | button = 1; 120 | } 121 | break; 122 | case BTN_RIGHT: 123 | if(mousee.value == 1){ 124 | button = 2; 125 | } 126 | break; 127 | case BTN_MIDDLE: 128 | if(mousee.value == 1){ 129 | button = 3; 130 | } 131 | break; 132 | default: 133 | button = 0; 134 | break; 135 | } 136 | if (button != mouse.button) { 137 | mouse.button = button; 138 | if(mouse.button == 0){ 139 | onMouseRelease(mouse.x,mouse.y); 140 | } else { 141 | onMouseClick(mouse.x,mouse.y,mouse.button); 142 | } 143 | } 144 | break; 145 | case EV_REL: 146 | switch (mousee.code) { 147 | case REL_X: 148 | mouse.velX = mousee.value; 149 | break; 150 | case REL_Y: 151 | mousee.value = mousee.value * -1; 152 | mouse.velY = mousee.value; 153 | break; 154 | // case REL_WHEEL: 155 | // if (mousee.value > 0) 156 | // std::cout << "Mouse wheel Forward" << std::endl; 157 | // else if(mousee.value < 0) 158 | // std::cout << "Mouse wheel Backward" << std::endl; 159 | // break; 160 | default: 161 | break; 162 | } 163 | mouse.x+=mouse.velX; 164 | mouse.y+=mouse.velY; 165 | 166 | // Clamp values 167 | if (mouse.x < 0) mouse.x=0; 168 | if (mouse.y < 0) mouse.y=0; 169 | if (mouse.x > viewport.z) mouse.x = viewport.z; 170 | if (mouse.y > viewport.w) mouse.y = viewport.w; 171 | 172 | if (mouse.button != 0) { 173 | onMouseDrag(mouse.x,mouse.y,mouse.button); 174 | } else { 175 | onMouseMove(mouse.x,mouse.y); 176 | } 177 | break; 178 | case EV_ABS: 179 | switch (mousee.code) { 180 | case ABS_X: 181 | x = ((float)mousee.value/4095.0f)*viewport.z; 182 | mouse.velX = x - mouse.x; 183 | mouse.x = x; 184 | break; 185 | case ABS_Y: 186 | y = (1.0-((float)mousee.value/4095.0f))*viewport.w; 187 | mouse.velY = y - mouse.y; 188 | mouse.y = y; 189 | break; 190 | default: 191 | break; 192 | } 193 | if (mouse.button != 0) { 194 | onMouseDrag(mouse.x,mouse.y,mouse.button); 195 | } else { 196 | onMouseMove(mouse.x,mouse.y); 197 | } 198 | break; 199 | default: 200 | break; 201 | } 202 | } 203 | return true; 204 | } 205 | 206 | // OpenGL ES 207 | //-------------------------------- 208 | 209 | //============================================================================== 210 | // Creation of EGL context (lines 242-373) from: 211 | // 212 | // https://github.com/raspberrypi/firmware/blob/master/opt/vc/src/hello_pi/hello_triangle2/triangle2.c#L100 213 | // 214 | /* 215 | Copyright (c) 2012, Broadcom Europe Ltd 216 | All rights reserved. 217 | Redistribution and use in source and binary forms, with or without 218 | modification, are permitted provided that the following conditions are met: 219 | * Redistributions of source code must retain the above copyright 220 | notice, this list of conditions and the following disclaimer. 221 | * Redistributions in binary form must reproduce the above copyright 222 | notice, this list of conditions and the following disclaimer in the 223 | documentation and/or other materials provided with the distribution. 224 | * Neither the name of the copyright holder nor the 225 | names of its contributors may be used to endorse or promote products 226 | derived from this software without specific prior written permission. 227 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 228 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 229 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 230 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY 231 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 232 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 233 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 234 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 235 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 236 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 237 | */ 238 | //============================================================================== 239 | void initGL(int argc, char **argv){ 240 | 241 | // Start OpenGL ES 242 | bcm_host_init(); 243 | 244 | // Clear application state 245 | int32_t success = 0; 246 | EGLBoolean result; 247 | EGLint num_config; 248 | 249 | static EGL_DISPMANX_WINDOW_T nativeviewport; 250 | 251 | DISPMANX_ELEMENT_HANDLE_T dispman_element; 252 | DISPMANX_DISPLAY_HANDLE_T dispman_display; 253 | DISPMANX_UPDATE_HANDLE_T dispman_update; 254 | VC_RECT_T dst_rect; 255 | VC_RECT_T src_rect; 256 | 257 | uint32_t screen_width; 258 | uint32_t screen_height; 259 | 260 | static const EGLint attribute_list[] = { 261 | EGL_RED_SIZE, 8, 262 | EGL_GREEN_SIZE, 8, 263 | EGL_BLUE_SIZE, 8, 264 | EGL_ALPHA_SIZE, 8, 265 | EGL_SURFACE_TYPE, EGL_WINDOW_BIT, 266 | EGL_DEPTH_SIZE, 16, 267 | EGL_NONE 268 | }; 269 | 270 | static const EGLint context_attributes[] = { 271 | EGL_CONTEXT_CLIENT_VERSION, 2, 272 | EGL_NONE 273 | }; 274 | 275 | EGLConfig config; 276 | 277 | // get an EGL display connection 278 | display = eglGetDisplay(EGL_DEFAULT_DISPLAY); 279 | assert(display!=EGL_NO_DISPLAY); 280 | check(); 281 | 282 | // initialize the EGL display connection 283 | result = eglInitialize(display, NULL, NULL); 284 | assert(EGL_FALSE != result); 285 | check(); 286 | 287 | // get an appropriate EGL frame buffer configuration 288 | result = eglChooseConfig(display, attribute_list, &config, 1, &num_config); 289 | assert(EGL_FALSE != result); 290 | check(); 291 | 292 | // get an appropriate EGL frame buffer configuration 293 | result = eglBindAPI(EGL_OPENGL_ES_API); 294 | assert(EGL_FALSE != result); 295 | check(); 296 | 297 | // create an EGL rendering context 298 | context = eglCreateContext(display, config, EGL_NO_CONTEXT, context_attributes); 299 | assert(context!=EGL_NO_CONTEXT); 300 | check(); 301 | 302 | // create an EGL viewport surface 303 | success = graphics_get_display_size(0 /* LCD */, &screen_width, &screen_height); 304 | assert( success >= 0 ); 305 | 306 | // Initially the viewport is for all the screen 307 | viewport.x = 0; 308 | viewport.y = 0; 309 | viewport.z = screen_width; 310 | viewport.w = screen_height; 311 | 312 | // Adjust the viewport acording to the passed argument 313 | for (int i = 1; i < argc ; i++){ 314 | if ( std::string(argv[i]) == "-x" ) { 315 | i++; 316 | viewport.x = getInt(std::string(argv[i])); 317 | } else if ( std::string(argv[i]) == "-y" ) { 318 | i++; 319 | viewport.y = getInt(std::string(argv[i])); 320 | } else if ( std::string(argv[i]) == "-w" || 321 | std::string(argv[i]) == "--width" ) { 322 | i++; 323 | viewport.z = getInt(std::string(argv[i])); 324 | } else if ( std::string(argv[i]) == "-h" || 325 | std::string(argv[i]) == "--height") { 326 | i++; 327 | viewport.w = getInt(std::string(argv[i])); 328 | } else if ( std::string(argv[i]) == "--square") { 329 | if (screen_width > screen_height) { 330 | viewport.x = screen_width/2-screen_height/2; 331 | } else { 332 | viewport.y = screen_height/2-screen_width/2; 333 | } 334 | viewport.z = viewport.w = MIN(screen_width,screen_height); 335 | } else if ( std::string(argv[i]) == "-l" || 336 | std::string(argv[i]) == "--life-coding" ){ 337 | viewport.x = viewport.z-500; 338 | viewport.z = viewport.w = 500; 339 | } 340 | } 341 | 342 | dst_rect.x = viewport.x; 343 | dst_rect.y = viewport.y; 344 | dst_rect.width = viewport.z; 345 | dst_rect.height = viewport.w; 346 | 347 | src_rect.x = 0; 348 | src_rect.y = 0; 349 | src_rect.width = viewport.z << 16; 350 | src_rect.height = viewport.w << 16; 351 | 352 | dispman_display = vc_dispmanx_display_open( 0 /* LCD */); 353 | dispman_update = vc_dispmanx_update_start( 0 ); 354 | 355 | dispman_element = vc_dispmanx_element_add( dispman_update, dispman_display, 356 | 0/*layer*/, &dst_rect, 0/*src*/, 357 | &src_rect, DISPMANX_PROTECTION_NONE, 0 /*alpha*/, 0/*clamp*/, (DISPMANX_TRANSFORM_T)0/*transform*/); 358 | 359 | nativeviewport.element = dispman_element; 360 | nativeviewport.width = viewport.z; 361 | nativeviewport.height = viewport.w; 362 | vc_dispmanx_update_submit_sync( dispman_update ); 363 | 364 | check(); 365 | 366 | surface = eglCreateWindowSurface( display, config, &nativeviewport, NULL ); 367 | assert(surface != EGL_NO_SURFACE); 368 | check(); 369 | 370 | // connect the context to the surface 371 | result = eglMakeCurrent(display, surface, surface, context); 372 | assert(EGL_FALSE != result); 373 | check(); 374 | 375 | // Set background color and clear buffers 376 | // glClearColor(0.15f, 0.25f, 0.35f, 1.0f); 377 | // glClear( GL_COLOR_BUFFER_BIT ); 378 | 379 | setWindowSize(viewport.z,viewport.w); 380 | mouse.x = viewport.z*0.5; 381 | mouse.y = viewport.w*0.5; 382 | check(); 383 | 384 | initMouse(); 385 | ///printf("OpenGL Initialize at %i,%i,%i,%i\n",viewport.x,viewport.y,viewport.z,viewport.w); 386 | } 387 | 388 | void renderGL(){ 389 | eglSwapBuffers(display, surface); 390 | } 391 | 392 | void closeGL(){ 393 | closeMouse(); 394 | eglSwapBuffers(display, surface); 395 | 396 | // Release OpenGL resources 397 | eglMakeCurrent( display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT ); 398 | eglDestroySurface( display, surface ); 399 | eglDestroyContext( display, context ); 400 | eglTerminate( display ); 401 | } 402 | 403 | 404 | int getKey() { 405 | int character; 406 | struct termios orig_term_attr; 407 | struct termios new_term_attr; 408 | 409 | /* set the terminal to raw mode */ 410 | tcgetattr(fileno(stdin), &orig_term_attr); 411 | memcpy(&new_term_attr, &orig_term_attr, sizeof(struct termios)); 412 | new_term_attr.c_lflag &= ~(ECHO|ICANON); 413 | new_term_attr.c_cc[VTIME] = 0; 414 | new_term_attr.c_cc[VMIN] = 0; 415 | tcsetattr(fileno(stdin), TCSANOW, &new_term_attr); 416 | 417 | /* read a character from the stdin stream without blocking */ 418 | /* returns EOF (-1) if no character is available */ 419 | character = fgetc(stdin); 420 | 421 | /* restore the original terminal attributes */ 422 | tcsetattr(fileno(stdin), TCSANOW, &orig_term_attr); 423 | 424 | return character; 425 | } 426 | 427 | void updateGL() { 428 | updateMouse(); 429 | 430 | int key = getKey(); 431 | if ( key != -1 && key != keyPressed ){ 432 | keyPressed = key; 433 | onKeyPress(key); 434 | } 435 | } 436 | 437 | void setWindowSize(int _width, int _height) { 438 | viewport.z = _width; 439 | viewport.w = _height; 440 | glViewport((float)viewport.x, (float)viewport.y, (float)viewport.z, (float)viewport.w); 441 | orthoMatrix = glm::ortho((float)viewport.x, (float)viewport.z, (float)viewport.y, (float)viewport.w); 442 | 443 | onViewportResize(viewport.z, viewport.w); 444 | } 445 | 446 | int getWindowWidth(){ 447 | return viewport.z; 448 | } 449 | 450 | int getWindowHeight(){ 451 | return viewport.w; 452 | } 453 | 454 | glm::vec2 getWindowSize() { 455 | return glm::vec2(viewport.w,viewport.w); 456 | } 457 | 458 | glm::mat4 getOrthoMatrix(){ 459 | return orthoMatrix; 460 | } 461 | 462 | float getMouseX(){ 463 | return mouse.x; 464 | } 465 | 466 | float getMouseY(){ 467 | return mouse.y; 468 | } 469 | 470 | glm::vec2 getMousePosition() { 471 | return glm::vec2(mouse.x,mouse.y); 472 | } 473 | 474 | float getMouseVelX(){ 475 | return mouse.velX; 476 | } 477 | 478 | float getMouseVelY(){ 479 | return mouse.velY; 480 | } 481 | 482 | glm::vec2 getMouseVelocity() { 483 | return glm::vec2(mouse.velX,mouse.velY); 484 | } 485 | 486 | int getMouseButton(){ 487 | return mouse.button; 488 | } 489 | 490 | unsigned char getKeyPressed(){ 491 | return keyPressed; 492 | } 493 | 494 | void setRenderRequest(bool _request) { 495 | bRender = _request; 496 | } 497 | 498 | bool getRenderRequest() { 499 | return bRender; 500 | } 501 | -------------------------------------------------------------------------------- /src/context.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "gl.h" 4 | #include "glm/glm.hpp" 5 | 6 | // GL Context 7 | void initGL(int argc, char **argv); 8 | void updateGL(); 9 | void renderGL(); 10 | void closeGL(); 11 | 12 | // SET 13 | void setWindowSize(int _width, int _height); 14 | void setRenderRequest(bool _request); 15 | 16 | // GET 17 | int getWindowWidth(); 18 | int getWindowHeight(); 19 | glm::vec2 getWindowSize(); 20 | glm::mat4 getOrthoMatrix(); 21 | 22 | float getMouseX(); 23 | float getMouseY(); 24 | glm::vec2 getMousePosition(); 25 | 26 | float getMouseVelX(); 27 | float getMouseVelY(); 28 | glm::vec2 getMouseVelocity(); 29 | 30 | int getMouseButton(); 31 | 32 | unsigned char getKeyPressed(); 33 | 34 | bool getRenderRequest(); 35 | 36 | // EVENTS 37 | void onKeyPress(int _key); 38 | void onMouseMove(float _x, float _y); 39 | void onMouseClick(float _x, float _y, int _button); 40 | void onMouseDrag(float _x, float _y, int _button); 41 | void onMouseRelease(float _x, float _y); 42 | void onViewportResize(int _width, int _height); 43 | -------------------------------------------------------------------------------- /src/gps.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "gps.h" 3 | 4 | #include // standard input / output functions 5 | #include 6 | #include 7 | #include 8 | #include // string function definitions 9 | #include // UNIX standard function definitions 10 | #include // File control definitions 11 | #include // Error number definitions 12 | #include // POSIX terminal control definitions 13 | #include // std::vector 14 | #include // std::search 15 | 16 | int openPort(const std::string& _portAddress){ 17 | int port_fd = open(_portAddress.c_str(), O_RDONLY | O_NOCTTY); 18 | 19 | if (port_fd == -1) { 20 | std::cout << "Error opening serial" << std::endl; 21 | return -1; 22 | } 23 | 24 | struct termios tty; 25 | struct termios tty_old; 26 | memset(&tty, 0, sizeof tty); 27 | 28 | /* Error Handling */ 29 | if ( tcgetattr ( port_fd, &tty ) != 0 ){ 30 | std::cout << "Error " << errno << " from tcgetattr: " << strerror(errno) << std::endl; 31 | return -1; 32 | } 33 | 34 | /* Save old tty parameters */ 35 | tty_old = tty; 36 | 37 | /* Set Baud Rate */ 38 | cfsetospeed (&tty, (speed_t)B9600); 39 | cfsetispeed (&tty, (speed_t)B9600); 40 | 41 | /* Setting other Port Stuff */ 42 | tty.c_cflag &= ~PARENB; // Make 8n1 43 | tty.c_cflag &= ~CSTOPB; 44 | tty.c_cflag &= ~CSIZE; 45 | tty.c_cflag |= CS8; 46 | 47 | tty.c_cflag &= ~CSTOPB; // 1 stop bit 48 | tty.c_cflag &= ~CRTSCTS; // no flow control 49 | tty.c_cc[VMIN] = 1; // read doesn't block 50 | tty.c_cc[VTIME] = 5; // 0.5 seconds read timeout 51 | // tty.c_cflag |= CREAD | CLOCAL; // turn on READ & ignore ctrl lines 52 | tty.c_cflag |= CLOCAL; // Enable receiver 53 | 54 | /* Make raw */ 55 | cfmakeraw(&tty); 56 | 57 | /* Flush Port, then applies attributes */ 58 | tcflush( port_fd, TCIFLUSH ); 59 | if ( tcsetattr ( port_fd, TCSANOW, &tty ) != 0) { 60 | std::cout << "Error " << errno << " from tcsetattr" << std::endl; 61 | return -1; 62 | } 63 | 64 | return port_fd; 65 | } 66 | 67 | std::string readPort(int _port_fd){ 68 | int n = 0; 69 | char buf = '\0'; 70 | std::string response = ""; 71 | do { 72 | n = read( _port_fd, &buf, 1 ); 73 | response += buf; 74 | } while( buf != '\r' && buf != '\n' && n > 0); 75 | 76 | if (n < 0) { 77 | return "ERROR"; 78 | } else if (n == 0) { 79 | return "EMPTY"; 80 | } else { 81 | return response; 82 | } 83 | } 84 | 85 | std::vector splitString(const std::string &_source, const std::string &_delimiter = "", bool _ignoreEmpty = false) { 86 | std::vector result; 87 | if (_delimiter.empty()) { 88 | result.push_back(_source); 89 | return result; 90 | } 91 | std::string::const_iterator substart = _source.begin(), subend; 92 | while (true) { 93 | subend = search(substart, _source.end(), _delimiter.begin(), _delimiter.end()); 94 | std::string sub(substart, subend); 95 | 96 | if (!_ignoreEmpty || !sub.empty()) { 97 | result.push_back(sub); 98 | } 99 | if (subend == _source.end()) { 100 | break; 101 | } 102 | substart = subend + _delimiter.size(); 103 | } 104 | return result; 105 | } 106 | 107 | int toInt(const std::string &_intString) { 108 | int x = 0; 109 | std::istringstream cur(_intString); 110 | cur >> x; 111 | return x; 112 | } 113 | 114 | float toFloat(const std::string &_floatString) { 115 | float x = 0; 116 | std::istringstream cur(_floatString); 117 | cur >> x; 118 | return x; 119 | } 120 | 121 | void getLocation(int _port_fd, float *lat, float *lon){ 122 | std::string line = readPort(_port_fd); 123 | if (line.find("GPRMC") != std::string::npos){ 124 | // std::cout << line << std::endl; 125 | std::vector data = splitString(line,","); 126 | 127 | // If got a FIX 128 | if (data[2] == "A"){ 129 | // parse the data 130 | *lat = 0.0f; 131 | *lon = 0.0f; 132 | 133 | // LATITUD 134 | *lat = toInt(data[3].substr(0,2)); 135 | *lat += toFloat(data[3].substr(2))/60.0; 136 | if(data[4]=="S") 137 | *lat *= -1.0; 138 | 139 | // LONGITUD 140 | *lon = toInt(data[5].substr(0,3)); 141 | *lon += toFloat(data[5].substr(3))/60.0; 142 | if(data[6]=="W") 143 | *lon *= -1.0; 144 | } 145 | } 146 | } 147 | 148 | bool getLocation(float *_lat, float *_lon){ 149 | static int fd = -1; 150 | static float lat = 0.0; 151 | static float lon = 0.0; 152 | 153 | if (fd<0) { 154 | fd = openPort("/dev/ttyAMA0"); 155 | } 156 | 157 | if (fd>=0) { 158 | float tmpLat = 0.0; 159 | float tmpLon = 0.0; 160 | 161 | getLocation(fd,&tmpLat,&tmpLon); 162 | 163 | if (tmpLat != 0.0 && tmpLon != 0.0){ 164 | lat = tmpLat; 165 | lon = tmpLon; 166 | } 167 | 168 | if (lat != 0.0 && lon != 0.0) { 169 | *_lat = lat; 170 | *_lon = lon; 171 | return true; 172 | } else { 173 | return false; 174 | } 175 | } else { 176 | return false; 177 | } 178 | } -------------------------------------------------------------------------------- /src/gps.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | bool getLocation(float *lat, float *lon); 4 | -------------------------------------------------------------------------------- /src/hud/button.cpp: -------------------------------------------------------------------------------- 1 | #include "button.h" 2 | 3 | #include "context.h" 4 | 5 | #include "glm/gtc/matrix_transform.hpp" 6 | #include "glm/gtc/type_ptr.hpp" 7 | 8 | #include "util/vertexLayout.h" 9 | #include "util/geom.h" 10 | 11 | void Button::init(){ 12 | 13 | std::string frag = 14 | "#ifdef GL_ES\n" 15 | "precision mediump float;\n" 16 | "#endif\n"; 17 | 18 | // Fixed Vertex Shader 19 | std::string fixVertShader = frag; 20 | fixVertShader += STRINGIFY( 21 | uniform mat4 u_modelViewProjectionMatrix; 22 | attribute vec4 a_position; 23 | varying vec4 v_position; 24 | varying float v_alpha; 25 | void main(void) { 26 | v_position = a_position; 27 | v_position.z = 0.1; 28 | v_alpha = 1.0; 29 | gl_Position = u_modelViewProjectionMatrix * v_position; 30 | }); 31 | 32 | // Universal Fragment Shader 33 | frag += STRINGIFY( 34 | uniform vec2 u_resolution; 35 | uniform vec4 u_mask; 36 | uniform vec4 u_color; 37 | varying float v_alpha; 38 | void main(void) { 39 | vec2 st = gl_FragCoord.xy; 40 | vec2 mask = step(u_mask.xy,st)*(1.0-step(u_mask.zw,st)); 41 | gl_FragColor = vec4(vec3(1.0),v_alpha)*(mask.x*mask.y); 42 | }); 43 | 44 | m_fixShader = std::shared_ptr(new ShaderProgram()); 45 | m_fixShader->setSourceStrings(frag, fixVertShader); 46 | 47 | { 48 | float cornersWidth = 10.; 49 | float crossWidth = 5.; 50 | 51 | std::shared_ptr vertexLayout = std::shared_ptr(new VertexLayout({ 52 | {"a_position", 2, GL_FLOAT, false, 0} 53 | })); 54 | std::vector vertices; 55 | std::vector indices; 56 | 57 | // Cross 58 | glm::vec3 center = getCenter(); 59 | vertices.push_back({ center.x-crossWidth, center.y}); 60 | vertices.push_back({ center.x+crossWidth, center.y}); 61 | vertices.push_back({ center.x, center.y-crossWidth}); 62 | vertices.push_back({ center.x, center.y+crossWidth}); 63 | 64 | indices.push_back(0); indices.push_back(1); 65 | indices.push_back(2); indices.push_back(3); 66 | 67 | // Coorners 68 | glm::vec3 A = getTopLeft(); 69 | glm::vec3 B = getTopRight(); 70 | glm::vec3 C = getBottomRight(); 71 | glm::vec3 D = getBottomLeft(); 72 | 73 | vertices.push_back({ A.x, A.y + cornersWidth}); 74 | vertices.push_back({ A.x, A.y}); 75 | vertices.push_back({ A.x + cornersWidth, A.y}); 76 | 77 | vertices.push_back({ B.x - cornersWidth, B.y}); 78 | vertices.push_back({ B.x, B.y}); 79 | vertices.push_back({ B.x, B.y + cornersWidth}); 80 | 81 | vertices.push_back({ C.x, C.y - cornersWidth}); 82 | vertices.push_back({ C.x, C.y}); 83 | vertices.push_back({ C.x - cornersWidth, C.y}); 84 | 85 | vertices.push_back({ D.x + cornersWidth, D.y}); 86 | vertices.push_back({ D.x, D.y}); 87 | vertices.push_back({ D.x, D.y - cornersWidth}); 88 | 89 | indices.push_back(4); indices.push_back(5); 90 | indices.push_back(5); indices.push_back(6); 91 | indices.push_back(7); indices.push_back(8); 92 | indices.push_back(8); indices.push_back(9); 93 | indices.push_back(10); indices.push_back(11); 94 | indices.push_back(11); indices.push_back(12); 95 | indices.push_back(13); indices.push_back(14); 96 | indices.push_back(14); indices.push_back(15); 97 | 98 | std::shared_ptr mesh(new HudMesh(vertexLayout, GL_LINES)); 99 | mesh->addVertices(std::move(vertices), std::move(indices)); 100 | mesh->compileVertexBuffer(); 101 | 102 | m_fixMesh = mesh; 103 | } 104 | } 105 | 106 | void Button::draw(){ 107 | m_fixShader->use(); 108 | m_fixShader->setUniformf("u_mask", 0, 0, getWindowWidth(), getWindowHeight()); 109 | m_fixShader->setUniformMatrix4f("u_modelViewProjectionMatrix", glm::value_ptr(getOrthoMatrix())); 110 | m_fixMesh->draw(m_fixShader); 111 | } -------------------------------------------------------------------------------- /src/hud/button.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "util/shaderProgram.h" 4 | 5 | #include "shapes.h" 6 | #include "glm/glm.hpp" 7 | #include "rectangle.h" 8 | 9 | class Button : public Rectangle { 10 | public: 11 | 12 | void init(); 13 | void draw(); 14 | 15 | private: 16 | std::shared_ptr m_fixShader; 17 | std::shared_ptr m_fixMesh; 18 | }; 19 | -------------------------------------------------------------------------------- /src/hud/hud.cpp: -------------------------------------------------------------------------------- 1 | #include "hud.h" 2 | 3 | #include 4 | #include "context.h" 5 | 6 | #include "glm/gtc/matrix_transform.hpp" 7 | #include "glm/gtc/type_ptr.hpp" 8 | 9 | #include "util/vertexLayout.h" 10 | #include "util/geom.h" 11 | 12 | #include "tangram.h" 13 | 14 | #include "../gps.h" 15 | 16 | #define STRINGIFY(A) #A 17 | 18 | Hud::Hud(): m_selected(0), m_bCursor(false){ 19 | } 20 | 21 | Hud::~Hud(){ 22 | } 23 | 24 | void Hud::setDrawCursor(bool _true){ 25 | m_bCursor = _true; 26 | } 27 | 28 | void Hud::init() { 29 | 30 | m_zoom.set(getWindowWidth()*0.9575,getWindowHeight()*0.30625,getWindowWidth()*0.02625,getWindowHeight()*0.3875); 31 | m_zoom.init(); 32 | 33 | m_rot.set(getWindowWidth()*0.34625,getWindowHeight()*0.866667,getWindowWidth()*0.3125,getWindowHeight()*0.1); 34 | m_rot.init(); 35 | 36 | m_center.set(getWindowWidth()*0.93625,getWindowHeight()*0.8958,getWindowHeight()*0.0708,getWindowHeight()*0.0708); 37 | m_center.init(); 38 | 39 | if (m_bCursor){ 40 | std::string frag = 41 | "#ifdef GL_ES\n" 42 | "precision mediump float;\n" 43 | "#endif\n"; 44 | 45 | // Translatation Vertex Shader 46 | std::string trnVertShader = frag; 47 | trnVertShader += STRINGIFY( 48 | uniform mat4 u_modelViewProjectionMatrix; 49 | uniform vec2 u_offset; 50 | attribute vec4 a_position; 51 | varying vec4 v_position; 52 | void main(void) { 53 | v_position = vec4(u_offset.x,u_offset.y,0.1,0.0) + a_position; 54 | gl_Position = u_modelViewProjectionMatrix * v_position; 55 | }); 56 | 57 | // Universal Fragment Shader 58 | frag += STRINGIFY( 59 | uniform vec2 u_resolution; 60 | uniform vec4 u_color; 61 | void main(void) { 62 | gl_FragColor = vec4(1.0); 63 | }); 64 | 65 | m_trnShader = std::shared_ptr(new ShaderProgram()); 66 | m_trnShader->setSourceStrings(frag, trnVertShader); 67 | 68 | m_cursorMesh = getCrossMesh(10.0f); 69 | } 70 | } 71 | 72 | void Hud::cursorClick(float _x, float _y, int _button){ 73 | 74 | if (m_center.inside(_x,_y)){ 75 | float lat = 0.0; 76 | float lon = 0.0; 77 | if (getLocation(&lat,&lon)){ 78 | // GO TO CENTER 79 | std::cout << "GO TO " << lat << " lat, " << lon << " lon"<< std::endl; 80 | Tangram::setViewPosition(lon,lat); 81 | } else { 82 | std::cout << "NO FIX GPS" << std::endl; 83 | } 84 | } else if (m_rot.inside(_x,_y)){ 85 | m_selected = 1; 86 | } else if (m_zoom.inside(_x,_y)){ 87 | m_selected = 2; 88 | } 89 | } 90 | 91 | void Hud::cursorDrag(float _x, float _y, int _button){ 92 | if (m_selected == 1) { 93 | 94 | float scale = -1.0; 95 | 96 | glm::vec2 screen = glm::vec2(getWindowWidth(),getWindowHeight()); 97 | glm::vec2 mouse = glm::vec2(_x,_y)-screen*glm::vec2(0.5); 98 | glm::vec2 prevMouse = mouse - glm::vec2(getMouseVelX(),getMouseVelY()); 99 | 100 | double rot1 = atan2(mouse.y, mouse.x); 101 | double rot2 = atan2(prevMouse.y, prevMouse.x); 102 | double rot = rot1-rot2; 103 | wrapRad(rot); 104 | Tangram::handleRotateGesture(getWindowWidth()/2.0, getWindowHeight()/2.0, rot*scale); 105 | 106 | } else if (m_selected == 2) { 107 | 108 | Tangram::handlePinchGesture(getWindowWidth()/2.0, getWindowHeight()/2.0, 1.0 + getMouseVelY()*0.01); 109 | m_zoom.slider += getMouseVelY(); 110 | 111 | } 112 | } 113 | 114 | void Hud::cursorRelease(float _x, float _y){ 115 | m_selected = 0; 116 | } 117 | 118 | bool Hud::isInUse(){ 119 | return m_selected != 0; 120 | } 121 | 122 | void Hud::draw(){ 123 | 124 | glDisable(GL_DEPTH_TEST); 125 | glEnable(GL_BLEND); 126 | glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 127 | 128 | // Cursor 129 | if (m_bCursor){ 130 | m_trnShader->use(); 131 | m_trnShader->setUniformf("u_offset", getMouseX(), getMouseY()); 132 | m_trnShader->setUniformMatrix4f("u_modelViewProjectionMatrix", glm::value_ptr(getOrthoMatrix())); 133 | m_cursorMesh->draw(m_trnShader); 134 | } 135 | 136 | // Zoom 137 | m_zoom.zoom = Tangram::getViewZoom(); 138 | m_zoom.draw(); 139 | 140 | // Rotation 141 | m_rot.angle = Tangram::getViewRotation(); 142 | m_rot.draw(); 143 | 144 | // Center button 145 | m_center.draw(); 146 | 147 | glDisable(GL_BLEND); 148 | glEnable(GL_DEPTH_TEST); 149 | } -------------------------------------------------------------------------------- /src/hud/hud.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "util/shaderProgram.h" 4 | 5 | #include "shapes.h" 6 | #include "glm/glm.hpp" 7 | 8 | #include "slideZoom.h" 9 | #include "slideRot.h" 10 | #include "button.h" 11 | 12 | class Hud { 13 | public: 14 | 15 | Hud(); 16 | virtual ~Hud(); 17 | 18 | void setDrawCursor(bool _true); 19 | 20 | void init(); 21 | void draw(); 22 | 23 | void cursorClick(float _x, float _y, int _button); 24 | void cursorDrag(float _x, float _y, int _button); 25 | void cursorRelease(float _x, float _y); 26 | 27 | bool isInUse(); 28 | 29 | SlideRot m_rot; 30 | SlideZoom m_zoom; 31 | Button m_center; 32 | 33 | private: 34 | 35 | std::shared_ptr m_trnShader; 36 | std::shared_ptr m_cursorMesh; 37 | 38 | int m_selected; 39 | bool m_bCursor; 40 | }; -------------------------------------------------------------------------------- /src/hud/shapes.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "glm/glm.hpp" 4 | #include "util/typedMesh.h" 5 | #include "util/geom.h" 6 | 7 | struct LineVertex { 8 | GLfloat x; 9 | GLfloat y; 10 | }; 11 | typedef TypedMesh HudMesh; 12 | 13 | inline std::shared_ptr getCrossMesh(float width){ 14 | 15 | std::shared_ptr vertexLayout = std::shared_ptr(new VertexLayout({ 16 | {"a_position", 2, GL_FLOAT, false, 0} 17 | })); 18 | std::vector vertices; 19 | std::vector indices; 20 | 21 | vertices.push_back({ -width, 0.0f}); 22 | vertices.push_back({ width, 0.0f}); 23 | vertices.push_back({ 0.0f, -width}); 24 | vertices.push_back({ 0.0f, width}); 25 | 26 | indices.push_back(0); indices.push_back(1); 27 | indices.push_back(2); indices.push_back(3); 28 | 29 | std::shared_ptr mesh(new HudMesh(vertexLayout, GL_LINES)); 30 | mesh->addVertices(std::move(vertices), std::move(indices)); 31 | mesh->compileVertexBuffer(); 32 | 33 | return mesh; 34 | } 35 | 36 | inline std::shared_ptr getVerticalRulerMesh(float min, float max, float step, float width){ 37 | 38 | std::shared_ptr vertexLayout = std::shared_ptr(new VertexLayout({ 39 | {"a_position", 2, GL_FLOAT, false, 0} 40 | })); 41 | std::vector vertices; 42 | std::vector indices; 43 | 44 | float lenght = max-min; 45 | int nLines = lenght/step; 46 | 47 | for(int i = 0; i < nLines; i++){ 48 | float y = min + (step*(float)i); 49 | vertices.push_back({0.0f, y}); 50 | vertices.push_back({width, y}); 51 | 52 | indices.push_back(i*2); indices.push_back(i*2+1); 53 | } 54 | 55 | std::shared_ptr mesh(new HudMesh(vertexLayout, GL_LINES)); 56 | mesh->addVertices(std::move(vertices), std::move(indices)); 57 | mesh->compileVertexBuffer(); 58 | 59 | return mesh; 60 | } 61 | 62 | inline std::shared_ptr getCircularRulerMesh(float radius, int nLines, float width){ 63 | 64 | std::shared_ptr vertexLayout = std::shared_ptr(new VertexLayout({ 65 | {"a_position", 2, GL_FLOAT, false, 0} 66 | })); 67 | std::vector vertices; 68 | std::vector indices; 69 | 70 | float step = TWO_PI/(float)nLines; 71 | float a = -PI; 72 | for(int i = 0; i < nLines; i++){ 73 | vertices.push_back({radius*(float)cos(a), 74 | radius*(float)sin(a)}); 75 | vertices.push_back({(radius+width)*(float)cos(a), 76 | (radius+width)*(float)sin(a)}); 77 | indices.push_back(i*2); indices.push_back(i*2+1); 78 | a += step; 79 | } 80 | 81 | std::shared_ptr mesh(new HudMesh(vertexLayout, GL_LINES)); 82 | mesh->addVertices(std::move(vertices), std::move(indices)); 83 | mesh->compileVertexBuffer(); 84 | 85 | return mesh; 86 | } 87 | 88 | inline std::shared_ptr getTriangle(const glm::vec2& pos, float width, float angle = 0){ 89 | 90 | std::shared_ptr vertexLayout = std::shared_ptr(new VertexLayout({ 91 | {"a_position", 2, GL_FLOAT, false, 0} 92 | })); 93 | std::vector vertices; 94 | std::vector indices; 95 | 96 | float step = TWO_PI/3.0f; 97 | for(int i = 0; i < 3; i++){ 98 | vertices.push_back({pos.x+width*(float)cos(angle), 99 | pos.y+width*(float)sin(angle)}); 100 | indices.push_back(i); 101 | angle += step; 102 | } 103 | 104 | std::shared_ptr mesh(new HudMesh(vertexLayout, GL_TRIANGLES)); 105 | mesh->addVertices(std::move(vertices), std::move(indices)); 106 | mesh->compileVertexBuffer(); 107 | 108 | return mesh; 109 | } 110 | 111 | inline std::shared_ptr getTriangle(float width, float angle = 0){ 112 | return getTriangle(glm::vec2(0.0,0.0),width,angle); 113 | } -------------------------------------------------------------------------------- /src/hud/slideRot.cpp: -------------------------------------------------------------------------------- 1 | #include "slideRot.h" 2 | 3 | #include "context.h" 4 | 5 | #include "glm/gtc/matrix_transform.hpp" 6 | #include "glm/gtc/type_ptr.hpp" 7 | 8 | #include "util/vertexLayout.h" 9 | #include "util/geom.h" 10 | 11 | void SlideRot::init(){ 12 | 13 | angle = 0; 14 | 15 | std::string frag = 16 | "#ifdef GL_ES\n" 17 | "precision mediump float;\n" 18 | "#endif\n"; 19 | 20 | // Fixed Vertex Shader 21 | std::string fixVertShader = frag; 22 | fixVertShader += STRINGIFY( 23 | uniform mat4 u_modelViewProjectionMatrix; 24 | attribute vec4 a_position; 25 | varying vec4 v_position; 26 | varying float v_alpha; 27 | void main(void) { 28 | v_position = a_position; 29 | v_position.z = 0.1; 30 | v_alpha = 1.0; 31 | gl_Position = u_modelViewProjectionMatrix * v_position; 32 | }); 33 | 34 | // Rotation Vertex Shader 35 | std::string rotVertShader = frag; 36 | rotVertShader += STRINGIFY( 37 | uniform mat4 u_modelViewProjectionMatrix; 38 | uniform float u_angle; 39 | uniform vec2 u_resolution; 40 | attribute vec4 a_position; 41 | varying vec4 v_position; 42 | varying float v_alpha; 43 | mat2 rotate2d(float _angle){ 44 | return mat2(cos(_angle),-sin(_angle), 45 | sin(_angle),cos(_angle)); 46 | } 47 | void main(void) { 48 | v_position = a_position; 49 | v_position.xy = rotate2d(u_angle) * v_position.xy; 50 | v_position.xy += u_resolution.xy*0.5; 51 | v_position.z = 0.1; 52 | vec2 fromCenter = v_position.xy-u_resolution.xy*0.5; 53 | v_alpha = 1.0-clamp( (abs(atan(fromCenter.y,fromCenter.x)-1.57079632679)/1.57079632679)*2.0,0.0,1.0); 54 | gl_Position = u_modelViewProjectionMatrix * v_position; 55 | }); 56 | 57 | // Universal Fragment Shader 58 | frag += STRINGIFY( 59 | uniform vec2 u_resolution; 60 | uniform vec4 u_mask; 61 | uniform vec4 u_color; 62 | varying float v_alpha; 63 | void main(void) { 64 | vec2 st = gl_FragCoord.xy; 65 | vec2 mask = step(u_mask.xy,st)*(1.0-step(u_mask.zw,st)); 66 | gl_FragColor = vec4(vec3(1.0),v_alpha)*(mask.x*mask.y); 67 | }); 68 | 69 | m_fixShader = std::shared_ptr(new ShaderProgram()); 70 | m_fixShader->setSourceStrings(frag, fixVertShader); 71 | 72 | m_rotShader = std::shared_ptr(new ShaderProgram()); 73 | m_rotShader->setSourceStrings(frag, rotVertShader); 74 | 75 | m_circularRulerMeshA = getCircularRulerMesh(((float)getWindowHeight())*0.42125,180,getWindowWidth()*0.0151125); 76 | m_circularRulerMeshB = getCircularRulerMesh(((float)getWindowHeight())*0.42125,36,getWindowWidth()*0.0204525); 77 | 78 | m_fixed = getTriangle(glm::vec2(getWindowWidth()*0.5,y+height*0.3),getWindowHeight()*0.01,PI/2.0); 79 | } 80 | 81 | void SlideRot::draw(){ 82 | m_rotShader->use(); 83 | m_rotShader->setUniformf("u_angle", angle); 84 | m_rotShader->setUniformf("u_mask", x, y, x+width, y+height); 85 | m_rotShader->setUniformf("u_resolution", (float)getWindowWidth(), (float)getWindowHeight()); 86 | m_rotShader->setUniformMatrix4f("u_modelViewProjectionMatrix", glm::value_ptr(getOrthoMatrix())); 87 | m_circularRulerMeshA->draw(m_rotShader); 88 | glLineWidth(2.0f); 89 | m_circularRulerMeshB->draw(m_rotShader); 90 | glLineWidth(1.0f); 91 | 92 | m_fixShader->use(); 93 | m_fixShader->setUniformf("u_mask", 0, 0, getWindowWidth(), getWindowHeight()); 94 | m_fixShader->setUniformMatrix4f("u_modelViewProjectionMatrix", glm::value_ptr(getOrthoMatrix())); 95 | m_fixed->draw(m_fixShader); 96 | } -------------------------------------------------------------------------------- /src/hud/slideRot.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "util/shaderProgram.h" 4 | 5 | #include "shapes.h" 6 | #include "glm/glm.hpp" 7 | #include "rectangle.h" 8 | 9 | class SlideRot : public Rectangle { 10 | public: 11 | 12 | void init(); 13 | void draw(); 14 | 15 | float angle; 16 | 17 | private: 18 | std::shared_ptr m_fixShader; 19 | std::shared_ptr m_rotShader; 20 | std::shared_ptr m_circularRulerMeshA; 21 | std::shared_ptr m_circularRulerMeshB; 22 | std::shared_ptr m_fixed; 23 | }; 24 | -------------------------------------------------------------------------------- /src/hud/slideZoom.cpp: -------------------------------------------------------------------------------- 1 | #include "slideZoom.h" 2 | 3 | #include "context.h" 4 | 5 | #include "glm/gtc/matrix_transform.hpp" 6 | #include "glm/gtc/type_ptr.hpp" 7 | 8 | #include "util/vertexLayout.h" 9 | #include "util/geom.h" 10 | 11 | void SlideZoom::init(){ 12 | 13 | zoom = 0; 14 | slider = 0; 15 | 16 | std::string frag = 17 | "#ifdef GL_ES\n" 18 | "precision mediump float;\n" 19 | "#endif\n"; 20 | 21 | // Fixed Vertex Shader 22 | std::string fixVertShader = frag; 23 | fixVertShader += STRINGIFY( 24 | uniform mat4 u_modelViewProjectionMatrix; 25 | attribute vec4 a_position; 26 | varying vec4 v_position; 27 | varying float v_alpha; 28 | void main(void) { 29 | v_position = a_position; 30 | v_position.z = 0.1; 31 | v_alpha = 1.0; 32 | gl_Position = u_modelViewProjectionMatrix * v_position; 33 | }); 34 | 35 | // Translatation Vertex Shader 36 | std::string trnVertShader = frag; 37 | trnVertShader += STRINGIFY( 38 | uniform mat4 u_modelViewProjectionMatrix; 39 | uniform vec2 u_offset; 40 | uniform vec2 u_resolution; 41 | attribute vec4 a_position; 42 | varying vec4 v_position; 43 | varying float v_alpha; 44 | void main(void) { 45 | v_position = vec4(u_offset.x,u_offset.y,0.1,0.0) + a_position; 46 | v_alpha = 1.0; 47 | 48 | float toCenter = v_position.y-u_resolution.y*0.5; 49 | v_alpha = 1.0-clamp( abs(toCenter/(u_resolution.y*2.)) ,0.0,1.0); 50 | 51 | gl_Position = u_modelViewProjectionMatrix * v_position; 52 | }); 53 | 54 | // Universal Fragment Shader 55 | frag += STRINGIFY( 56 | uniform vec2 u_resolution; 57 | uniform vec4 u_mask; 58 | uniform vec4 u_color; 59 | varying float v_alpha; 60 | void main(void) { 61 | vec2 st = gl_FragCoord.xy; 62 | vec2 mask = step(u_mask.xy,st)*(1.0-step(u_mask.zw,st)); 63 | gl_FragColor = vec4(vec3(1.0),v_alpha)*(mask.x*mask.y); 64 | }); 65 | 66 | m_fixShader = std::shared_ptr(new ShaderProgram()); 67 | m_fixShader->setSourceStrings(frag, fixVertShader); 68 | 69 | m_trnShader = std::shared_ptr(new ShaderProgram()); 70 | m_trnShader->setSourceStrings(frag, trnVertShader); 71 | 72 | m_verticalRulerMeshA = getVerticalRulerMesh(-getWindowHeight(),getWindowHeight(),5.0f,getWindowWidth()*0.0131125); 73 | m_verticalRulerMeshB = getVerticalRulerMesh(-getWindowHeight(),getWindowHeight(),50.0f,getWindowWidth()*0.0194525); 74 | 75 | m_triangle = getTriangle(getWindowHeight()*0.01); 76 | 77 | // Make fixed lines 78 | { 79 | std::shared_ptr vertexLayout = std::shared_ptr(new VertexLayout({ 80 | {"a_position", 2, GL_FLOAT, false, 0} 81 | })); 82 | std::vector vertices; 83 | std::vector indices; 84 | 85 | vertices.push_back({ x - 10.0, getWindowHeight()*0.5-20.0 }); 86 | vertices.push_back({ x - 5.0, getWindowHeight()*0.5-20.0 }); 87 | indices.push_back(0); indices.push_back(1); 88 | 89 | vertices.push_back({ x - 7.5, getWindowHeight()*0.5 }); 90 | vertices.push_back({ x - 5.0, getWindowHeight()*0.5 }); 91 | indices.push_back(2); indices.push_back(3); 92 | 93 | vertices.push_back({ x - 10.0, getWindowHeight()*0.5+20.0 }); 94 | vertices.push_back({ x - 5.0, getWindowHeight()*0.5+20.0 }); 95 | indices.push_back(4); indices.push_back(5); 96 | 97 | indices.push_back(1); indices.push_back(5); 98 | 99 | std::shared_ptr mesh(new HudMesh(vertexLayout, GL_LINES)); 100 | mesh->addVertices(std::move(vertices), std::move(indices)); 101 | mesh->compileVertexBuffer(); 102 | 103 | m_fixed = mesh; 104 | } 105 | } 106 | 107 | void SlideZoom::draw(){ 108 | 109 | if ( slider > height ) { 110 | slider -= height; 111 | } else if ( slider < -height ) { 112 | slider += height; 113 | } 114 | 115 | m_trnShader->use(); 116 | m_trnShader->setUniformf("u_offset", x, slider); 117 | m_trnShader->setUniformf("u_mask", x, y, x+width, y+height); 118 | m_trnShader->setUniformf("u_resolution", getWindowWidth(),getWindowHeight()); 119 | m_trnShader->setUniformMatrix4f("u_modelViewProjectionMatrix", glm::value_ptr(getOrthoMatrix())); 120 | m_verticalRulerMeshA->draw(m_trnShader); 121 | glLineWidth(2.0f); 122 | m_verticalRulerMeshB->draw(m_trnShader); 123 | glLineWidth(1.0f); 124 | 125 | m_trnShader->use(); 126 | m_trnShader->setUniformf("u_offset", x-15., getWindowHeight()*0.5-(zoom-9)*2.1f) ; 127 | m_trnShader->setUniformf("u_mask", 0, 0, getWindowWidth(), getWindowHeight()); 128 | m_trnShader->setUniformMatrix4f("u_modelViewProjectionMatrix", glm::value_ptr(getOrthoMatrix())); 129 | m_triangle->draw(m_trnShader); 130 | 131 | m_fixShader->use(); 132 | m_fixShader->setUniformf("u_mask", 0, 0, getWindowWidth(), getWindowHeight()); 133 | m_fixShader->setUniformMatrix4f("u_modelViewProjectionMatrix", glm::value_ptr(getOrthoMatrix())); 134 | // glLineWidth(2.0f); 135 | m_fixed->draw(m_fixShader); 136 | // glLineWidth(1.0f); 137 | } -------------------------------------------------------------------------------- /src/hud/slideZoom.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "util/shaderProgram.h" 4 | 5 | #include "shapes.h" 6 | #include "glm/glm.hpp" 7 | #include "rectangle.h" 8 | 9 | class SlideZoom : public Rectangle { 10 | public: 11 | 12 | void init(); 13 | void draw(); 14 | 15 | float zoom; 16 | float slider; 17 | 18 | private: 19 | std::shared_ptr m_fixShader; 20 | std::shared_ptr m_trnShader; 21 | 22 | std::shared_ptr m_verticalRulerMeshA; 23 | std::shared_ptr m_verticalRulerMeshB; 24 | std::shared_ptr m_triangle; 25 | std::shared_ptr m_fixed; 26 | }; 27 | -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | 14 | #include "context.h" 15 | #include "tangram.h" 16 | #include "platform.h" 17 | 18 | #include 19 | #include "hud/hud.h" 20 | #include "gps.h" 21 | #include 22 | Hud m_hud; 23 | 24 | #define KEY_ESC 113 // q 25 | #define KEY_ZOOM_IN 45 // - 26 | #define KEY_ZOOM_OUT 61 // = 27 | #define KEY_UP 119 // w 28 | #define KEY_LEFT 97 // a 29 | #define KEY_RIGHT 115 // s 30 | #define KEY_DOWN 122 // z 31 | 32 | struct timeval tv; 33 | unsigned long long timePrev, timeStart; 34 | static double delta; 35 | 36 | static bool bUpdate = true; 37 | 38 | //============================================================================== 39 | void setup(); 40 | void newFrame(); 41 | 42 | int main(int argc, char **argv){ 43 | 44 | for (int i = 1; i < argc ; i++){ 45 | if ( std::string(argv[i]) == "-m" ){ 46 | // bMouse = true; 47 | m_hud.setDrawCursor(true); 48 | } 49 | } 50 | 51 | // Start OpenGL context 52 | initGL(argc, argv); 53 | 54 | /* Do Curl Init */ 55 | curl_global_init(CURL_GLOBAL_DEFAULT); 56 | 57 | // Set background color and clear buffers 58 | Tangram::initialize(); 59 | Tangram::resize(getWindowWidth(), getWindowHeight()); 60 | 61 | setup(); 62 | 63 | // Start clock 64 | gettimeofday(&tv, NULL); 65 | timeStart = timePrev = (unsigned long long)(tv.tv_sec) * 1000 + (unsigned long long)(tv.tv_usec) / 1000; 66 | 67 | bool bGPS = false; 68 | double minDelta = 1.0; 69 | while (bUpdate) { 70 | updateGL(); 71 | 72 | processNetworkQueue(); 73 | 74 | if (getRenderRequest()) { 75 | setRenderRequest(false); 76 | gettimeofday( &tv, NULL); 77 | timePrev = (unsigned long long)(tv.tv_sec) * 1000 + (unsigned long long)(tv.tv_usec) / 1000; 78 | newFrame(); 79 | gettimeofday( &tv, NULL); 80 | unsigned long long timeNow = (unsigned long long)(tv.tv_sec) * 1000 + (unsigned long long)(tv.tv_usec) / 1000; 81 | delta = ((double)(timeNow - timePrev))*0.001; 82 | 83 | if (delta < minDelta){ 84 | minDelta = delta; 85 | } 86 | } else { 87 | 88 | if (!bGPS) { 89 | float lat = 0.0; 90 | float lon = 0.0; 91 | bGPS = getLocation(&lat,&lon); 92 | } else { 93 | usleep(1000000.0*minDelta); 94 | } 95 | 96 | } 97 | } 98 | 99 | Tangram::teardown(); 100 | curl_global_cleanup(); 101 | closeGL(); 102 | return 0; 103 | } 104 | 105 | void setup() { 106 | m_hud.init(); 107 | } 108 | 109 | void newFrame() { 110 | // logMsg("New frame (delta %f msec)\n",delta); 111 | 112 | Tangram::update(delta); 113 | 114 | // Render 115 | Tangram::render(); 116 | 117 | m_hud.draw(); 118 | 119 | renderGL(); 120 | } 121 | 122 | //======================================================================= EVENTS 123 | 124 | void onKeyPress(int _key) { 125 | switch (_key) { 126 | case KEY_ZOOM_IN: 127 | Tangram::handlePinchGesture(0.0,0.0,0.5); 128 | break; 129 | case KEY_ZOOM_OUT: 130 | Tangram::handlePinchGesture(0.0,0.0,2.0); 131 | break; 132 | case KEY_UP: 133 | Tangram::handlePanGesture(0.0,0.0,0.0,100.0); 134 | break; 135 | case KEY_DOWN: 136 | Tangram::handlePanGesture(0.0,0.0,0.0,-100.0); 137 | break; 138 | case KEY_LEFT: 139 | Tangram::handlePanGesture(0.0,0.0,100.0,0.0); 140 | break; 141 | case KEY_RIGHT: 142 | Tangram::handlePanGesture(0.0,0.0,-100.0,0.0); 143 | break; 144 | case KEY_ESC: 145 | bUpdate = false; 146 | break; 147 | // default: 148 | // logMsg(" -> %i\n",_key); 149 | } 150 | requestRender(); 151 | } 152 | 153 | void onMouseMove(float _x, float _y) { 154 | requestRender(); 155 | } 156 | 157 | void onMouseClick(float _x, float _y, int _button) { 158 | m_hud.cursorClick(_x,_y,_button); 159 | requestRender(); 160 | } 161 | 162 | void onMouseDrag(float _x, float _y, int _button) { 163 | 164 | if( _button == 1 ){ 165 | 166 | if (m_hud.isInUse()){ 167 | m_hud.cursorDrag(_x,_y,_button); 168 | } else { 169 | Tangram::handlePanGesture( _x-getMouseVelX()*1.0, 170 | _y+getMouseVelY()*1.0, 171 | _x, 172 | _y); 173 | } 174 | 175 | } else if( _button == 2 ){ 176 | if ( getKeyPressed() == 'r') { 177 | float scale = -0.05; 178 | float rot = atan2(getMouseVelY(),getMouseVelX()); 179 | if( _x < getWindowWidth()/2.0 ) { 180 | scale *= -1.0; 181 | } 182 | Tangram::handleRotateGesture(getWindowWidth()/2.0, getWindowHeight()/2.0, rot*scale); 183 | } else if ( getKeyPressed() == 't') { 184 | Tangram::handleShoveGesture(getMouseVelY()*0.005); 185 | } else { 186 | Tangram::handlePinchGesture(getWindowWidth()/2.0, getWindowHeight()/2.0, 1.0 + getMouseVelY()*0.001); 187 | } 188 | 189 | } 190 | requestRender(); 191 | } 192 | 193 | void onMouseRelease(float _x, float _y) { 194 | m_hud.cursorRelease(_x,_y); 195 | requestRender(); 196 | } 197 | 198 | void onViewportResize(int _newWidth, int _newHeight) { 199 | Tangram::resize(_newWidth,_newHeight); 200 | requestRender(); 201 | } 202 | -------------------------------------------------------------------------------- /src/platform_rpi.cpp: -------------------------------------------------------------------------------- 1 | #ifdef PLATFORM_RPI 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "urlWorker.h" 11 | #include "platform.h" 12 | #include "gl.h" 13 | #include "context.h" 14 | 15 | #define NUM_WORKERS 3 16 | 17 | static bool s_isContinuousRendering = false; 18 | 19 | static UrlWorker s_Workers[NUM_WORKERS]; 20 | static std::list> s_urlTaskQueue; 21 | 22 | void logMsg(const char* fmt, ...) { 23 | va_list args; 24 | va_start(args, fmt); 25 | vfprintf(stderr, fmt, args); 26 | va_end(args); 27 | } 28 | 29 | void processNetworkQueue() { 30 | 31 | // check if any of the workers is done 32 | { 33 | for(auto& worker : s_Workers) { 34 | if(worker.isFinished() && !worker.isAvailable()) { 35 | auto result = worker.getResult(); 36 | worker.reset(); 37 | if(result->content.size() != 0) { 38 | result->callback(std::move(result->content)); 39 | } 40 | } 41 | } 42 | } 43 | 44 | // attach workers to NetWorkerData 45 | { 46 | auto taskItr = s_urlTaskQueue.begin(); 47 | for(auto& worker : s_Workers) { 48 | if(taskItr == s_urlTaskQueue.end()) { 49 | break; 50 | } 51 | if(worker.isAvailable()) { 52 | worker.perform(std::move(*taskItr)); 53 | taskItr = s_urlTaskQueue.erase(taskItr); 54 | } 55 | } 56 | } 57 | } 58 | 59 | void requestRender() { 60 | setRenderRequest(true); 61 | } 62 | 63 | void setContinuousRendering(bool _isContinuous) { 64 | s_isContinuousRendering = _isContinuous; 65 | } 66 | 67 | bool isContinuousRendering() { 68 | return s_isContinuousRendering; 69 | } 70 | 71 | std::string stringFromResource(const char* _path) { 72 | std::string into; 73 | 74 | std::ifstream file; 75 | std::string buffer; 76 | 77 | file.open(_path); 78 | if(!file.is_open()) { 79 | return std::string(); 80 | } 81 | 82 | while(!file.eof()) { 83 | getline(file, buffer); 84 | into += buffer + "\n"; 85 | } 86 | 87 | file.close(); 88 | return into; 89 | } 90 | 91 | unsigned char* bytesFromResource(const char* _path, unsigned int* _size) { 92 | std::ifstream resource(_path, std::ifstream::ate | std::ifstream::binary); 93 | 94 | if(!resource.is_open()) { 95 | logMsg("Failed to read file at path: %s\n", _path); 96 | *_size = 0; 97 | return nullptr; 98 | } 99 | 100 | *_size = resource.tellg(); 101 | 102 | resource.seekg(std::ifstream::beg); 103 | 104 | char* cdata = (char*) malloc(sizeof(char) * (*_size)); 105 | 106 | resource.read(cdata, *_size); 107 | resource.close(); 108 | 109 | return reinterpret_cast(cdata); 110 | } 111 | 112 | bool startUrlRequest(const std::string& _url, UrlCallback _callback) { 113 | 114 | std::unique_ptr task(new UrlTask(_url, _callback)); 115 | for(auto& worker : s_Workers) { 116 | if(worker.isAvailable()) { 117 | worker.perform(std::move(task)); 118 | return true; 119 | } 120 | } 121 | s_urlTaskQueue.push_back(std::move(task)); 122 | return true; 123 | 124 | } 125 | 126 | void cancelUrlRequest(const std::string& _url) { 127 | 128 | // Only clear this request if a worker has not started operating on it!! otherwise it gets too convoluted with curl! 129 | auto itr = s_urlTaskQueue.begin(); 130 | while(itr != s_urlTaskQueue.end()) { 131 | if((*itr)->url == _url) { 132 | itr = s_urlTaskQueue.erase(itr); 133 | } else { 134 | itr++; 135 | } 136 | } 137 | } 138 | 139 | #endif 140 | -------------------------------------------------------------------------------- /src/string-conversions.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include "glm/glm.hpp" 16 | 17 | //---------------------------------------- Conversions 18 | 19 | /* Transform the string into lower letters */ 20 | inline void toLower( std::string &_str ){ 21 | for (int i = 0; _str[i]; i++) { 22 | _str[i] = tolower(_str[i]); 23 | } 24 | } 25 | 26 | /* Return new string with all into lower letters */ 27 | inline std::string getLower(const std::string& _str ){ 28 | std::string std = _str; 29 | toLower(std); 30 | return std; 31 | } 32 | 33 | /* Extract extrange characters from a string */ 34 | inline void purifyString( std::string& _s ){ 35 | for ( std::string::iterator it = _s.begin(), itEnd = _s.end(); it!=itEnd; ++it){ 36 | if ( static_cast(*it) < 32 || static_cast(*it) > 127 ){ 37 | (*it) = ' '; 38 | } 39 | } 40 | } 41 | 42 | inline int getInt(const std::string &_intString) { 43 | int x = 0; 44 | std::istringstream cur(_intString); 45 | cur >> x; 46 | return x; 47 | } 48 | 49 | inline float getFloat(const std::string &_floatString) { 50 | float x = 0; 51 | std::istringstream cur(_floatString); 52 | cur >> x; 53 | return x; 54 | } 55 | 56 | inline double getDouble(const std::string &_doubleString) { 57 | double x = 0; 58 | std::istringstream cur(_doubleString); 59 | cur >> x; 60 | return x; 61 | } 62 | 63 | inline bool getBool(const std::string &_boolString) { 64 | static const std::string trueString = "true"; 65 | static const std::string falseString = "false"; 66 | 67 | std::string lower = getLower(_boolString); 68 | 69 | if(lower == trueString) { 70 | return true; 71 | } 72 | if(lower == falseString) { 73 | return false; 74 | } 75 | 76 | bool x = false; 77 | std::istringstream cur(lower); 78 | cur >> x; 79 | return x; 80 | } 81 | 82 | inline char getChar(const std::string &_charString) { 83 | char x = '\0'; 84 | std::istringstream cur(_charString); 85 | cur >> x; 86 | return x; 87 | } 88 | 89 | inline std::string getString(bool _bool){ 90 | std::ostringstream strStream; 91 | strStream << (_bool?"true":"false") ; 92 | return strStream.str(); 93 | } 94 | 95 | // Translations 96 | 97 | template 98 | std::string getString(const T& _value){ 99 | std::ostringstream out; 100 | out << _value; 101 | return out.str(); 102 | } 103 | 104 | /// like sprintf "%4f" format, in this example precision=4 105 | template 106 | std::string getString(const T& _value, int _precision){ 107 | std::ostringstream out; 108 | out << std::fixed << std::setprecision(_precision) << _value; 109 | return out.str(); 110 | } 111 | 112 | /// like sprintf "% 4d" or "% 4f" format, in this example width=4, fill=' ' 113 | template 114 | std::string getString(const T& _value, int _width, char _fill ){ 115 | std::ostringstream out; 116 | out << std::fixed << std::setfill(_fill) << std::setw(_width) << _value; 117 | return out.str(); 118 | } 119 | 120 | /// like sprintf "%04.2d" or "%04.2f" format, in this example precision=2, width=4, fill='0' 121 | template 122 | std::string getString(const T& _value, int _precision, int _width, char _fill ){ 123 | std::ostringstream out; 124 | out << std::fixed << std::setfill(_fill) << std::setw(_width) << std::setprecision(_precision) << _value; 125 | return out.str(); 126 | } 127 | 128 | //---------------------------------------- Conversions 129 | 130 | inline int toInt(const std::string &_intString) { 131 | int x = 0; 132 | std::istringstream cur(_intString); 133 | cur >> x; 134 | return x; 135 | } 136 | 137 | inline float toFloat(const std::string &_floatString) { 138 | float x = 0; 139 | std::istringstream cur(_floatString); 140 | cur >> x; 141 | return x; 142 | } 143 | 144 | inline double toDouble(const std::string &_doubleString) { 145 | double x = 0; 146 | std::istringstream cur(_doubleString); 147 | cur >> x; 148 | return x; 149 | } 150 | 151 | inline bool toBool(const std::string &_boolString) { 152 | static const std::string trueString = "true"; 153 | static const std::string falseString = "false"; 154 | 155 | std::string lower = getLower(_boolString); 156 | 157 | if(lower == trueString) { 158 | return true; 159 | } 160 | if(lower == falseString) { 161 | return false; 162 | } 163 | 164 | bool x = false; 165 | std::istringstream cur(lower); 166 | cur >> x; 167 | return x; 168 | } 169 | 170 | inline char ofToChar(const std::string &_charString) { 171 | char x = '\0'; 172 | std::istringstream cur(_charString); 173 | cur >> x; 174 | return x; 175 | } 176 | 177 | inline std::string toString(bool _bool){ 178 | std::ostringstream strStream; 179 | strStream << (_bool?"true":"false") ; 180 | return strStream.str(); 181 | } 182 | 183 | template 184 | std::string toString(const T& _value){ 185 | std::ostringstream out; 186 | out << _value; 187 | return out.str(); 188 | } 189 | 190 | /// like sprintf "%4f" format, in this example precision=4 191 | template 192 | std::string toString(const T& _value, int _precision){ 193 | std::ostringstream out; 194 | out << std::fixed << std::setprecision(_precision) << _value; 195 | return out.str(); 196 | } 197 | 198 | /// like sprintf "% 4d" or "% 4f" format, in this example width=4, fill=' ' 199 | template 200 | std::string toString(const T& _value, int _width, char _fill ){ 201 | std::ostringstream out; 202 | out << std::fixed << std::setfill(_fill) << std::setw(_width) << _value; 203 | return out.str(); 204 | } 205 | 206 | /// like sprintf "%04.2d" or "%04.2f" format, in this example precision=2, width=4, fill='0' 207 | template 208 | std::string toString(const T& _value, int _precision, int _width, char _fill ){ 209 | std::ostringstream out; 210 | out << std::fixed << std::setfill(_fill) << std::setw(_width) << std::setprecision(_precision) << _value; 211 | return out.str(); 212 | } 213 | 214 | inline std::string toString(const glm::vec2 &_vec, char _sep = ','){ 215 | std::ostringstream strStream; 216 | strStream<< _vec.x << _sep << _vec.y << _sep; 217 | return strStream.str(); 218 | } 219 | 220 | inline std::string toString(const glm::vec3 &_vec, char _sep = ','){ 221 | std::ostringstream strStream; 222 | strStream<< _vec.x << _sep << _vec.y << _sep << _vec.z; 223 | return strStream.str(); 224 | } 225 | 226 | inline std::string toString(const glm::vec4 &_vec, char _sep = ','){ 227 | std::ostringstream strStream; 228 | strStream<< _vec.x << _sep << _vec.y << _sep << _vec.z << _sep << _vec.w; 229 | return strStream.str(); 230 | } 231 | 232 | //-------------------------------------------------- << and >> 233 | 234 | inline std::ostream& operator<<(std::ostream& os, const glm::vec3& vec) { 235 | os << vec.x << ", " << vec.y << ", " << vec.z; 236 | return os; 237 | } 238 | 239 | inline std::istream& operator>>(std::istream& is, glm::vec3& vec) { 240 | is >> vec.x; 241 | is.ignore(2); 242 | is >> vec.y; 243 | is.ignore(2); 244 | is >> vec.z; 245 | return is; 246 | } 247 | --------------------------------------------------------------------------------