├── LICENSE ├── README.md ├── program ├── Makefile ├── README.md ├── program.h ├── source │ ├── fidget │ │ ├── fidget.cpp │ │ ├── fidget.fs │ │ └── fidget.vs │ ├── gif │ │ ├── gif.cpp │ │ ├── gif.fs │ │ ├── gif.vs │ │ ├── gifwrap.c │ │ └── gifwrap.h │ ├── hairy │ │ ├── fidget.fs │ │ ├── fidget.gs │ │ ├── fidget.vs │ │ ├── hairy.cpp │ │ ├── solid.fs │ │ └── solid.vs │ ├── img │ │ ├── img.cpp │ │ ├── img.fs │ │ ├── img.h │ │ └── img.vs │ ├── spiky │ │ ├── fidget.fs │ │ ├── fidget.gs │ │ ├── fidget.vs │ │ ├── solid.fs │ │ ├── solid.vs │ │ └── spiky.cpp │ └── text │ │ ├── text.cpp │ │ ├── text.fs │ │ └── text.vs └── utility │ ├── instance.cpp │ ├── model.cpp │ ├── shader.cpp │ ├── target.cpp │ └── texture.cpp ├── screenshots ├── fidget.gif ├── smbsplash.gif └── ttf.gif ├── setup.sh ├── splash ├── Makefile ├── helper │ ├── logger.h │ ├── parse.h │ └── pipe.h ├── include │ ├── event.cpp │ ├── program.cpp │ └── window.cpp ├── resource │ ├── help.txt │ └── info.txt ├── splash.cpp └── splash.h └── test ├── README.md ├── canyon.png ├── font.ttf ├── smb.gif └── test.sh /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Nick McDonald 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # splash 2 | Unix Splash Screen Tool - turn your desktop into a 2D / 3D canvas 3 | 4 | Made in C/C++ + XLIB 5 | 6 | See below for distro / DE compatbility 7 | 8 | ## Description 9 | splash is a generic tool that provides a command-line interface for rendering non-windowed raw data directly onto your desktop environment. 10 | 11 | splash transforms your entire desktop into a 2D / 3D canvas, for dynamic data or simple static overlays that can interact with your mouse and keyboard and trigger commands. 12 | 13 | ![smbsplash.gif](https://github.com/weigert/splash/blob/master/screenshots/smbsplash.gif) 14 | 15 | Example of splash displaying an animated GIF on the desktop background. 16 | 17 | ![fidget.gif](https://github.com/weigert/splash/blob/master/screenshots/fidget.gif) 18 | 19 | Example of splash displaying a full 3D OpenGL application (albeit a very basic one) with user interaction. 20 | 21 | ![ttf.gif](https://github.com/weigert/splash/blob/master/screenshots/ttf.gif) 22 | 23 | Example of splash rendering truetype font text, with a bunch of options, in the foreground on the desktop. 24 | 25 | This was originally written because I wanted to have a custom "splash screen" like those fancy, super expensive software packages have when starting up, with transparency and everything. Now it supports a full OpenGL context. 26 | 27 | ### Features 28 | 29 | splash rovides an elegant interface for creating a light-weight, "windowless" OpenGL context on your desktop. It also provides a simply system for accessing command line options and access to a pipe for streaming data to the visualization. Programs are dynamically linked so splash is very easy to extend (see below for details). The structure thereby allows for the development of light-weight, highly modular shader based desktop visuals. 30 | 31 | - dynamically linked, shader-based execution modes 32 | - super light code structure and execution cost 33 | - streaming data to exeuction modes via pipes 34 | - easy to develpo new execution modes 35 | 36 | ## Usage 37 | 38 | splash requires specification of an execution mode, which can then use piped data as well as options: 39 | 40 | Reading: 41 | [] Required 42 | <> Optional 43 | 44 | Running: 45 | splash [mode] 46 | 47 | Options: 48 | 49 | -p [x y w h] Position Splash Quad 50 | -t [s] Splash timeout in seconds (integer) 51 | 52 | Flags: 53 | 54 | --t Test configuration file 55 | --v Verbose mode 56 | 57 | --bg Place splash in background 58 | --fg Place splash in foreground (default) 59 | --ni Splash no-interact 60 | --ns Disable splash shadows (compton) 61 | --a Display splash on all desktops 62 | 63 | Modes (included by default): 64 | 65 | help Display help message (this message) 66 | info Display info message 67 | 68 | fidget Display a 3D fidget spinner (lol) 69 | hairy A different fidget spinner 70 | spiky Yet another different spinner 71 | 72 | img Display .png image 73 | [data]: Image file name 74 | 75 | gif Display .gif image (note: only full replacing gifs) 76 | [data]: GIF file name 77 | 78 | text Render truetype text (static) 79 | [data]: Text to render 80 | -ff [font] Font face (searches in ~/.fonts/, default "arial.ttf") 81 | -fc [hex] Text color (hex code, e.g. 0xFFFFFF) 82 | -fs [size] Font size, positive integer 83 | -v [0,1,2] Vertical Align [center, up, down] 84 | -h [0,1,2] Horizontal Align [center, left, right] 85 | 86 | ### Data Piping 87 | 88 | Data can be piped into a splash directly at the start, using e.g.: 89 | 90 | echo "splash" | splash text -p 100 100 800 400 --bg --ns -ff "arial.ttf" -fs 100 91 | 92 | Every splash program will also additionally open a numbered named pipe in `~/.config/splash/pipe/`, on which it (can) listen for data stream to affect the visualization. The name of the pipe is returned by splash: 93 | 94 | > splash text -p 100 100 800 400 --bg --ns -ff "arial.ttf" -fs 100 95 | $ /home/user/.config/splash/pipe/pipe0 96 | 97 | which can then be used to stream data from another source, e.g.: 98 | 99 | > watch -n1 'date +%H:%M:%S > /home/user/.config/splash/pipe/pipe0' 100 | 101 | resulting in a clock visualized in the splash opened previously with the correct settings. 102 | 103 | Note that what happens with the data depends on the programming of the execution mode. 104 | 105 | ### Examples 106 | 107 | To test your installation of splash and see some application examples, check the `test` folder. It comes with some data provided. 108 | 109 | ## Installation 110 | 111 | ### Dependencies 112 | 113 | Compiler: g++ 114 | Rendering: OpenGL, glX, GLEW 115 | X-Server: X11, Xfixes, Xrender 116 | Boost: boost, boost_system, boost_filesystem 117 | 118 | All of these should be available as packages for your favorite distro. 119 | 120 | ### Setup 121 | 122 | Run the script `setup.sh` **with privileges**. 123 | 124 | The script will make sure that the required directories exist and will setup splash at `~/.config/splash`. 125 | 126 | The script will also ask if you wish to compile splash and the execution modes. 127 | 128 | The execution modes are placed in `~/.config/splash/exec` and splash is placed in `/usr/local/bin`. 129 | 130 | #### Manual Compiling 131 | 132 | If you wish to compile manually, use the makefiles in `splash/Makefile` and `program/Makefile`: 133 | 134 | # ./splash: 135 | make build 136 | sudo make install 137 | # or 138 | sudo make splash 139 | 140 | # ./program: 141 | make build 142 | make install 143 | # or 144 | make all 145 | 146 | Note that splash is separate from the actual execution modes. Execution modes are compiled separately (linked at runtime by splash). Execution mode installation does not require privilege. 147 | 148 | #### Compilation Issues 149 | 150 | If you have problems with compiling search the closed issues to see if there is a solution and otherwise feel free to open a ticket. 151 | 152 | Common problems might include: Incorrect linking in the make files, because your distro places libraries in a different location, and slightly different names of the libraries in `#include` directives. 153 | 154 | ## Compabitibility / Requirements / Configuration 155 | 156 | This is just from some basic tests I can run on my computer. If you can compile / test on other distros and DEs, please open an issue so I can add it here. 157 | 158 | Distros: 159 | 160 | Ubuntu 18 Compiles successfully 161 | Arch / Manjaro Compiles successfully (see issues) 162 | ... feel free to open an issue for your distro! 163 | 164 | Desktop Environments: 165 | 166 | Gnome / Ubuntu Works fully 167 | Openbox Works fully 168 | XFCE Works fully 169 | bspwm Works fully (requires config, see below) 170 | 171 | i3 *Restricted (see below)* 172 | i3-gaps Not tested 173 | 174 | ... Feel free to open an issue for your DE! 175 | 176 | **Note**: Some WMs require additions to their config. 177 | 178 | **splash wiki**: [window manager compatibility and system configuration](https://github.com/weigert/splash/wiki/Window-Manager-Configuration) 179 | 180 | ## Customization & How it Works 181 | 182 | ### config 183 | 184 | I am currently working on a configuration system to make the display behavior more easily customizable. 185 | 186 | Possible configuration options include color-schemes, transparency options and event triggers. 187 | 188 | ### Custom Mode 189 | 190 | Currently, building custom execution modes requires knowledge of C++ and OpenGL. If you don't have the experience but you have an idea for a type of visualization you would like, let me know. 191 | 192 | splash provides a "windowless"* OpenGL context and a system for modularized data visualization using small shader programs ("execution modes"), which have access to the piped-in data and user-input events. 193 | 194 | Note: An X11 window technically exists, it is just made "transparent" to the desktop. 195 | 196 | A generic "program" base class is exposed to user input (piped data, commandline, X11 events). It contains an event handling callback and a rendering callback. 197 | 198 | The program class is exposed to an OpenGL rendering context for visualization, as well as a number of utility classes that intuitively wrap boilerplate OpenGL (taken from [TinyEngine](https://github.com/weigert/TinyEngine) ). 199 | 200 | By defining a derived class with specialized callbacks (and any additional desired members), arbitrary behavior can be achieved using the data piped in, on the "windowless" OpenGL window. 201 | 202 | The derived classes are precompiled and placed into `~/.config/splash/exec` where they are found and linked by splash at run time. 203 | 204 | You can build your own visualization programs by defining a custom derived class, compiling it and placing it in the `exec` folder. Check out the `program` folder for examples (as well as the makefile). 205 | 206 | Interesting Reads: 207 | 208 | https://www.linuxjournal.com/article/3687 209 | 210 | https://www.tldp.org/HOWTO/html_single/C++-dlopen/ 211 | 212 | https://stackoverflow.com/questions/20443560/how-to-practically-ship-glsl-shaders-with-your-c-software 213 | 214 | https://stackoverflow.com/questions/4052940/how-to-make-an-opengl-rendering-context-with-transparent-background 215 | 216 | If there is a desire for more detailed information on how to build a custom visualization, I can update the Wiki (if I get requests). 217 | 218 | ## Notes 219 | 220 | ### To-Do 221 | - Proper configuration file system, to expose the programs to static user settings 222 | - More well tested execution modes 223 | - Plotting methods 224 | - .obj file load and display 225 | - Particle system 226 | 227 | ### Why? 228 | 229 | ¯\_(ツ)_/¯ 230 | 231 | *check out my dope desktop fidget spinner, man* 232 | 233 | splash fidget 234 | 235 | ### A terrible Idea 236 | 237 | One could theoretically add a whole DearImGUI menu to your desktop using splash. Why? I don't know. But it's possible. Everyday I stray further from the light. 238 | 239 | ### Contributing 240 | If you find this interesting and would like to contribute that would be awesome. A number of places where contributing would be nice: 241 | 242 | - Designing use cases for different execution modes 243 | - Testing portability for other window managers / desktop environments 244 | 245 | ## License 246 | 247 | MIT License 248 | -------------------------------------------------------------------------------- /program/Makefile: -------------------------------------------------------------------------------- 1 | #================================ 2 | # Splash Execution Mode Makefile 3 | #================================ 4 | 5 | #Compiler Settings 6 | CC = g++ -std=c++17 7 | CFLAGS = -O -w 8 | LFLAGS = -I/usr/local/include -L/usr/local/lib -shared -fPIC 9 | LINK = -lX11 -ldl -lXfixes -lXrender -lSDL2 -lSDL2_image -lSDL2_ttf -lgif -lGL -lGLEW -lboost_system -lboost_filesystem 10 | 11 | #Programs that are installable... 12 | #Note: Remove programs you do not wish to install! 13 | #PROGRAMS = fidget img gif text hairy spiky 14 | PROGRAMS = text 15 | 16 | build: 17 | # Make build directory 18 | @if [ ! -d "./tmp" ]; then mkdir tmp; fi; 19 | @$(foreach i,$(PROGRAMS), echo Building $(i); $(CC) source/$(i)/$(i).cpp $(CFLAGS) $(LFLAGS) $(LINK) -o tmp/$(i);) 20 | 21 | install: 22 | @$(foreach i,$(PROGRAMS), mv ./tmp/$(i) ~/.config/splash/exec/$(i);) 23 | @rmdir tmp 24 | 25 | all: build install 26 | -------------------------------------------------------------------------------- /program/README.md: -------------------------------------------------------------------------------- 1 | # splash program 2 | splash itself provides the constructing of the raw "splash screen" OpenGL context. 3 | 4 | It also provides an interface for using the splash screen with your own custom shader programs via the "program" base class. 5 | 6 | You can write your own program by writing a derived class and pre-compiling it. splash can find the program and link it dynamically at run time if it is placed in `~/.config/splash/exec`. 7 | 8 | The OpenGL boilerplate is wrapped with the utility classes in `utility`, taken from [TinyEngine](https://github.com/weigert/TinyEngine). 9 | 10 | To see how to correctly write a derived class, check out these example programs in `source`. 11 | 12 | Note that the shader programs are included directly into the derived classes so that they are included in the precompilation. 13 | -------------------------------------------------------------------------------- /program/program.h: -------------------------------------------------------------------------------- 1 | #include "../splash/splash.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | using Handle = std::function; 9 | using slist = std::initializer_list; 10 | using svec = std::vector; 11 | 12 | #include 13 | 14 | #include "utility/texture.cpp" 15 | #include "utility/model.cpp" 16 | #include "utility/instance.cpp" 17 | #include "utility/shader.cpp" 18 | #include "utility/target.cpp" 19 | -------------------------------------------------------------------------------- /program/source/fidget/fidget.cpp: -------------------------------------------------------------------------------- 1 | #include "../../program.h" 2 | 3 | class Fidget : public Program { 4 | private: 5 | 6 | //Shader Source 7 | const std::string vs_source = 8 | #include "fidget.vs" 9 | ; 10 | const std::string fs_source = 11 | #include "fidget.fs" 12 | ; 13 | 14 | //Utility Classes 15 | Model mesh; 16 | Shader* shader; 17 | 18 | //Dynamics Properties 19 | float acc = 0.1; //Acceleration 20 | float frc = 0.01; //Friction 21 | 22 | public: 23 | 24 | int WIDTH, HEIGHT; 25 | float zoom = 0.05; 26 | glm::mat4 camera = glm::lookAt(glm::vec3(0, 0, 10), glm::vec3(0, 0, 0), glm::vec3(0,1,0)); 27 | glm::mat4 projection; 28 | 29 | string test = "Worked!"; 30 | 31 | float rot[3] = {0.0f}; 32 | float vrot[3] = {0.0f}; //Initial Velocity 33 | float trot[3] = {0.0f}; //Target Velocity () 34 | 35 | Fidget(svec* s, parse::data* d):Program(s){ 36 | WIDTH = d->w; 37 | HEIGHT = d->h; 38 | 39 | projection = glm::ortho(-(float)WIDTH*zoom, (float)WIDTH*zoom, -(float)HEIGHT*zoom, (float)HEIGHT*zoom, -50.0f, 50.0f); 40 | 41 | //Setup Stuff 42 | shader = new Shader({vs_source, fs_source}, {"in_Position", "in_Normal"}, true); 43 | mesh.construct(_build); 44 | } 45 | 46 | virtual void pipeline(){ 47 | glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 48 | 49 | shader->use(); //Prepare Shader 50 | shader->uniform("model", mesh.model); //Set Model Matrix 51 | shader->uniform("vp", projection*camera); //View Projection Matrix 52 | 53 | mesh.render(GL_LINES); //Render Model with Lines 54 | } 55 | 56 | virtual void event(event::eventdata& e){ 57 | glm::mat4 R1 = glm::rotate(glm::mat4(1), glm::radians(vrot[0]), glm::vec3(1, 0, 0)); 58 | glm::mat4 R2 = glm::rotate(glm::mat4(1), glm::radians(vrot[1]), glm::vec3(0, 1, 0)); 59 | mesh.model = R1*R2*mesh.model; 60 | 61 | if(e.clicked){ 62 | vrot[1] += acc*e.rx; 63 | vrot[0] += acc*e.ry; 64 | } 65 | 66 | vrot[0] += frc*(trot[0]-vrot[0]); 67 | vrot[1] += frc*(trot[1]-vrot[1]); 68 | vrot[2] += frc*(trot[2]-vrot[2]); 69 | 70 | rot[0] += vrot[0]; 71 | rot[1] += vrot[1]; 72 | rot[2] += vrot[2]; 73 | } 74 | 75 | virtual void onpipe(std::string s){ 76 | /* ... */ 77 | } 78 | 79 | ~Fidget(){ 80 | delete shader; 81 | } 82 | 83 | std::function _build = [&](Model* h){ 84 | 85 | //Radius r 86 | float r = 15.0f; 87 | 88 | //Icosahedron Vertices 89 | 90 | //Top Cap 91 | h->positions.push_back(0); 92 | h->positions.push_back(r); 93 | h->positions.push_back(0); 94 | 95 | float a = 72.0f/360.0f*2.0f*3.14159265f; 96 | 97 | for(int i = 0; i < 5; i++){ //Top 98 | float x1 = r*cos(atan(0.5))*cos(a*i); 99 | float y1 = r*cos(atan(0.5))*sin(a*i); 100 | float z1 = r*sin(atan(0.5)); 101 | h->positions.push_back(x1); 102 | h->positions.push_back(z1); 103 | h->positions.push_back(y1); 104 | } 105 | 106 | for(int i = 0; i < 5; i++){ //Bottom 107 | float x2 = r*cos(atan(0.5))*cos(a*i+a/2.0f); 108 | float y2 = r*cos(atan(0.5))*sin(a*i+a/2.0f); 109 | float z2 = -r*sin(atan(0.5)); 110 | h->positions.push_back(x2); 111 | h->positions.push_back(z2); 112 | h->positions.push_back(y2); 113 | } 114 | 115 | //Bottom Cap 116 | h->positions.push_back(0); 117 | h->positions.push_back(-r); 118 | h->positions.push_back(0); 119 | 120 | for(int i = 0; i < 12; i++){ 121 | h->normals.push_back(1.0); 122 | h->normals.push_back(1.0); 123 | h->normals.push_back(1.0); 124 | } 125 | 126 | h->indices.push_back(1); 127 | h->indices.push_back(2); 128 | 129 | h->indices.push_back(2); 130 | h->indices.push_back(3); 131 | 132 | h->indices.push_back(3); 133 | h->indices.push_back(4); 134 | 135 | h->indices.push_back(4); 136 | h->indices.push_back(5); 137 | 138 | h->indices.push_back(5); 139 | h->indices.push_back(1); 140 | 141 | h->indices.push_back(6); 142 | h->indices.push_back(7); 143 | 144 | h->indices.push_back(7); 145 | h->indices.push_back(8); 146 | 147 | h->indices.push_back(8); 148 | h->indices.push_back(9); 149 | 150 | h->indices.push_back(9); 151 | h->indices.push_back(10); 152 | 153 | h->indices.push_back(10); 154 | h->indices.push_back(6); 155 | 156 | for(int i = 1; i < 6; i++){ 157 | h->indices.push_back(i); 158 | h->indices.push_back(i+5); 159 | } 160 | 161 | h->indices.push_back(1); 162 | h->indices.push_back(10); 163 | 164 | h->indices.push_back(2); 165 | h->indices.push_back(6); 166 | 167 | h->indices.push_back(3); 168 | h->indices.push_back(7); 169 | 170 | h->indices.push_back(4); 171 | h->indices.push_back(8); 172 | 173 | h->indices.push_back(5); 174 | h->indices.push_back(9); 175 | 176 | //Caps 177 | for(int i = 1; i < 6; i++){ 178 | h->indices.push_back(0); 179 | h->indices.push_back(i); 180 | 181 | h->indices.push_back(11); 182 | h->indices.push_back(11-i); 183 | } 184 | }; 185 | }; 186 | 187 | //Exporter 188 | extern "C" { 189 | Program* create(svec* s, parse::data* d) { 190 | return new Fidget(s, d); 191 | } 192 | 193 | void destroy(Program* p) { 194 | delete p; 195 | } 196 | } 197 | -------------------------------------------------------------------------------- /program/source/fidget/fidget.fs: -------------------------------------------------------------------------------- 1 | R""( 2 | 3 | #version 130 4 | 5 | in vec4 ex_Color; 6 | in vec3 ex_Normal; 7 | in vec3 ex_FragPos; 8 | 9 | out vec4 fragColor; 10 | 11 | void main(void) { 12 | fragColor = ex_Color; 13 | } 14 | 15 | )"" 16 | -------------------------------------------------------------------------------- /program/source/fidget/fidget.vs: -------------------------------------------------------------------------------- 1 | R""( 2 | 3 | #version 130 4 | 5 | in vec3 in_Position; 6 | in vec3 in_Normal; 7 | 8 | uniform mat4 model; 9 | uniform mat4 vp; 10 | 11 | out vec4 ex_Color; 12 | out vec3 ex_Normal; 13 | out vec3 ex_FragPos; 14 | 15 | void main(void) { 16 | ex_FragPos = (model * vec4(in_Position, 1.0f)).xyz; 17 | ex_Normal = in_Normal; 18 | gl_Position = vp * vec4(ex_FragPos, 1.0f); 19 | ex_Color = vec4(normalize(in_Normal), 1.0); 20 | } 21 | 22 | )"" 23 | -------------------------------------------------------------------------------- /program/source/gif/gif.cpp: -------------------------------------------------------------------------------- 1 | #include "../../program.h" 2 | #include "gifwrap.c" 3 | #include 4 | 5 | class Image : public Program { 6 | private: 7 | 8 | //Utility Classes 9 | Square2D flat; 10 | Shader* shader; 11 | GIF_Image* gif; 12 | std::vector tex; 13 | int index = 0; 14 | 15 | //Shader Sources 16 | const std::string vs_source = 17 | #include "gif.vs" 18 | ; 19 | const std::string fs_source = 20 | #include "gif.fs" 21 | ; 22 | 23 | public: 24 | 25 | //Constructor 26 | Image(svec* s):Program(s){ 27 | shader = new Shader({vs_source, fs_source}, {"in_Quad, in_Tex"}, true); 28 | 29 | if(s == NULL){ 30 | logger::err("No Image Specified"); 31 | exit(0); 32 | } 33 | 34 | gif = GIF_LoadImage(data->back().c_str()); 35 | for(int i = 0; i < gif->num_frames; i++) 36 | tex.emplace_back(new Texture(gif->frames[i]->surface)); 37 | 38 | } 39 | 40 | virtual void pipeline(){ 41 | glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 42 | 43 | shader->use(); 44 | shader->uniform("model", flat.model); 45 | shader->texture("imageTexture", *tex[index]); 46 | flat.render(); 47 | 48 | usleep(1000*gif->frames[index]->delay); 49 | index = (index + 1)%gif->num_frames; 50 | } 51 | 52 | virtual void event(event::eventdata& e){ 53 | /* ... */ 54 | } 55 | 56 | virtual void onpipe(std::string s){ 57 | /* ... */ 58 | } 59 | 60 | ~Image(){ 61 | delete shader; 62 | for(auto& t: tex) 63 | delete t; 64 | } 65 | }; 66 | 67 | extern "C" { 68 | Program* create(svec* s, parse::data* d) { 69 | return new Image(s); 70 | } 71 | 72 | void destroy(Program* p) { 73 | delete p; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /program/source/gif/gif.fs: -------------------------------------------------------------------------------- 1 | R""( 2 | 3 | #version 130 4 | in vec2 ex_Tex; 5 | out vec4 fragColor; 6 | 7 | uniform sampler2D imageTexture; 8 | 9 | void main(){ 10 | fragColor = texture(imageTexture, ex_Tex); 11 | if(fragColor.a == 0.0) discard; 12 | 13 | //Invert Color 14 | //fragColor = vec4(vec3(1.0) - fragColor.xyz, 1.0); 15 | } 16 | 17 | )"" 18 | -------------------------------------------------------------------------------- /program/source/gif/gif.vs: -------------------------------------------------------------------------------- 1 | R""( 2 | 3 | #version 130 4 | in vec2 in_Quad; 5 | in vec2 in_Tex; 6 | out vec2 ex_Tex; 7 | 8 | uniform mat4 model; 9 | 10 | void main(void) { 11 | ex_Tex = in_Tex; 12 | gl_Position = model*vec4(in_Quad, -1.0, 1.0); 13 | } 14 | 15 | )"" 16 | -------------------------------------------------------------------------------- /program/source/gif/gifwrap.c: -------------------------------------------------------------------------------- 1 | /* 2 | Note: This source code was taken from 3 | grimfang4 on github! 4 | 5 | ...because I was too lazy to write it myself. 6 | */ 7 | 8 | #include "gifwrap.h" 9 | #include "gif_lib.h" 10 | #include 11 | #include 12 | 13 | #define MIN(x,y) ((x) < (y)? (x) : (y)) 14 | 15 | // Create and initialize a blank GIF image. 16 | DECLSPEC GIF_Image* SDLCALL GIF_CreateImage(void) 17 | { 18 | GIF_Image* result = (GIF_Image*)SDL_malloc(sizeof(GIF_Image)); 19 | if(result == NULL) 20 | return NULL; 21 | 22 | memset(result, 0, sizeof(GIF_Image)); 23 | 24 | result->background_color.r = result->background_color.g = result->background_color.b = 0; 25 | result->background_color.a = 255; 26 | 27 | return result; 28 | } 29 | 30 | static SDL_Surface* gif_create_surface32(Uint32 width, Uint32 height) 31 | { 32 | #if SDL_BYTEORDER == SDL_BIG_ENDIAN 33 | return SDL_CreateRGBSurface(SDL_SWSURFACE, width, height, 32, 0xFF000000, 0x00FF0000, 0x0000FF00, 0x000000FF); 34 | #else 35 | return SDL_CreateRGBSurface(SDL_SWSURFACE, width, height, 32, 0x000000FF, 0x0000FF00, 0x00FF0000, 0xFF000000); 36 | #endif 37 | } 38 | 39 | 40 | static Uint32 get_pixel(SDL_Surface *Surface, int x, int y) 41 | { 42 | Uint8* bits; 43 | Uint32 bpp; 44 | 45 | if(x < 0 || x >= Surface->w) 46 | return 0; // Best I could do for errors 47 | 48 | bpp = Surface->format->BytesPerPixel; 49 | bits = ((Uint8*)Surface->pixels) + y*Surface->pitch + x*bpp; 50 | 51 | switch (bpp) 52 | { 53 | case 1: 54 | return *((Uint8*)Surface->pixels + y * Surface->pitch + x); 55 | break; 56 | case 2: 57 | return *((Uint16*)Surface->pixels + y * Surface->pitch/2 + x); 58 | break; 59 | case 3: 60 | { 61 | // Endian-correct, but slower 62 | Uint8 r, g, b; 63 | r = *((bits)+Surface->format->Rshift/8); 64 | g = *((bits)+Surface->format->Gshift/8); 65 | b = *((bits)+Surface->format->Bshift/8); 66 | return SDL_MapRGB(Surface->format, r, g, b); 67 | } 68 | break; 69 | case 4: 70 | return *((Uint32*)Surface->pixels + y * Surface->pitch/4 + x); 71 | break; 72 | } 73 | 74 | return 0; // FIXME: Handle errors better 75 | } 76 | 77 | 78 | static void set_pixel(SDL_Surface* surface, int x, int y, Uint32 color) 79 | { 80 | int bpp = surface->format->BytesPerPixel; 81 | Uint8* bits = ((Uint8 *)surface->pixels) + y*surface->pitch + x*bpp; 82 | 83 | /* Set the pixel */ 84 | switch(bpp) 85 | { 86 | case 1: 87 | *((Uint8 *)(bits)) = (Uint8)color; 88 | break; 89 | case 2: 90 | *((Uint16 *)(bits)) = (Uint16)color; 91 | break; 92 | case 3: { /* Format/endian independent */ 93 | Uint8 r,g,b; 94 | r = (color >> surface->format->Rshift) & 0xFF; 95 | g = (color >> surface->format->Gshift) & 0xFF; 96 | b = (color >> surface->format->Bshift) & 0xFF; 97 | *((bits)+surface->format->Rshift/8) = r; 98 | *((bits)+surface->format->Gshift/8) = g; 99 | *((bits)+surface->format->Bshift/8) = b; 100 | } 101 | break; 102 | case 4: 103 | *((Uint32 *)(bits)) = (Uint32)color; 104 | break; 105 | } 106 | } 107 | 108 | // Load a GIF image from a file. 109 | DECLSPEC GIF_Image* SDLCALL GIF_LoadImage(const char* filename) 110 | { 111 | int error; 112 | GifFileType* gif = DGifOpenFileName(filename, &error); 113 | if(gif == NULL) 114 | return NULL; 115 | 116 | if(DGifSlurp(gif) == GIF_ERROR) 117 | { 118 | DGifCloseFile(gif, &error); 119 | return NULL; 120 | } 121 | 122 | GIF_Image* result = (GIF_Image*)SDL_malloc(sizeof(GIF_Image)); 123 | memset(result, 0, sizeof(GIF_Image)); 124 | result->width = gif->SWidth; 125 | result->height = gif->SHeight; 126 | 127 | // Unused? 128 | //gif->SColorResolution // Number of bits per color channel, roughly. 129 | // Binary 111 (7) -> 8 bits per channel 130 | // Binary 001 (1) -> 2 bits per channel 131 | //gif->AspectByte 132 | //gif->SBackGroundColor 133 | 134 | SDL_Palette* global_palette = NULL; 135 | int i; 136 | int j; 137 | 138 | if(gif->SColorMap != NULL) 139 | { 140 | SDL_Color* global_colors = (SDL_Color*)SDL_malloc(sizeof(SDL_Color)*gif->SColorMap->ColorCount); 141 | for(i = 0; i < gif->SColorMap->ColorCount; ++i) 142 | { 143 | global_colors[i].r = gif->SColorMap->Colors[i].Red; 144 | global_colors[i].g = gif->SColorMap->Colors[i].Green; 145 | global_colors[i].b = gif->SColorMap->Colors[i].Blue; 146 | global_colors[i].a = 255; 147 | } 148 | 149 | global_palette = GIF_CreatePalette(gif->SColorMap->ColorCount, global_colors); 150 | } 151 | 152 | for(j = 0; j < gif->ExtensionBlockCount; ++j) 153 | { 154 | if(gif->ExtensionBlocks[j].Function == APPLICATION_EXT_FUNC_CODE) 155 | { 156 | if(gif->ExtensionBlocks[j].ByteCount >= 14) 157 | { 158 | char name[12]; 159 | memcpy(name, gif->ExtensionBlocks[j].Bytes, 11); 160 | if(strcmp(name, "NETSCAPE2.0") == 0) 161 | { 162 | result->num_loops = gif->ExtensionBlocks[j].Bytes[12] + gif->ExtensionBlocks[j].Bytes[13]*256; 163 | } 164 | } 165 | } 166 | } 167 | 168 | result->num_frames = gif->ImageCount; 169 | result->frames = (GIF_Frame**)SDL_malloc(sizeof(GIF_Frame*)*result->num_frames); 170 | memset(result->frames, 0, sizeof(GIF_Frame*)*result->num_frames); 171 | 172 | for(i = 0; i < gif->ImageCount; ++i) 173 | { 174 | SavedImage* img = &gif->SavedImages[i]; 175 | 176 | GIF_Frame* frame = (GIF_Frame*)SDL_malloc(sizeof(GIF_Frame)); 177 | memset(frame, 0, sizeof(GIF_Frame)); 178 | 179 | result->frames[i] = frame; 180 | 181 | // Load basic attributes 182 | frame->width = img->ImageDesc.Width; 183 | frame->height = img->ImageDesc.Height; 184 | frame->left_offset = img->ImageDesc.Left; 185 | frame->top_offset = img->ImageDesc.Top; 186 | 187 | 188 | // Load palette 189 | if(img->ImageDesc.ColorMap != NULL) 190 | { 191 | SDL_Color* local_colors = (SDL_Color*)SDL_malloc(sizeof(SDL_Color)*img->ImageDesc.ColorMap->ColorCount); 192 | for(j = 0; j < img->ImageDesc.ColorMap->ColorCount; ++j) 193 | { 194 | local_colors[j].r = img->ImageDesc.ColorMap->Colors[j].Red; 195 | local_colors[j].g = img->ImageDesc.ColorMap->Colors[j].Green; 196 | local_colors[j].b = img->ImageDesc.ColorMap->Colors[j].Blue; 197 | local_colors[j].a = 255; 198 | } 199 | 200 | frame->local_palette = GIF_CreatePalette(img->ImageDesc.ColorMap->ColorCount, local_colors); 201 | } 202 | 203 | SDL_Palette* pal = global_palette; 204 | if(frame->local_palette != NULL) 205 | pal = frame->local_palette; 206 | 207 | // Look for graphics extension to get delay and transparency 208 | for(j = 0; j < img->ExtensionBlockCount; ++j) 209 | { 210 | if(img->ExtensionBlocks[j].Function == GRAPHICS_EXT_FUNC_CODE) 211 | { 212 | Uint8 block[4]; 213 | memcpy(block, img->ExtensionBlocks[j].Bytes, 4); 214 | 215 | // Check for transparency 216 | if(block[0] & 0x01) 217 | frame->use_transparent_index = SDL_TRUE; 218 | frame->transparent_index = block[3]; 219 | 220 | // Disposal mode 221 | frame->overlay_previous = (SDL_bool)((block[0] & 0x08) != 0x08); 222 | 223 | // Reconstruct delay 224 | frame->delay = 10*(block[1] + block[2]*256); 225 | 226 | // Get transparent color 227 | if(pal != NULL && frame->transparent_index < pal->ncolors) 228 | frame->transparent_color = pal->colors[frame->transparent_index]; 229 | } 230 | } 231 | 232 | int count = img->ImageDesc.Width * img->ImageDesc.Height; 233 | frame->surface = gif_create_surface32(frame->width, frame->height); 234 | frame->owns_surface = SDL_TRUE; 235 | if(pal != NULL) 236 | { 237 | for(j = 0; j < count; ++j) 238 | { 239 | SDL_Color c = pal->colors[img->RasterBits[j]]; 240 | if(frame->transparent_index == img->RasterBits[j]) 241 | c.a = 0; 242 | 243 | set_pixel(frame->surface, j%frame->width, j/frame->width, SDL_MapRGBA(frame->surface->format, c.r, c.g, c.b, c.a)); 244 | } 245 | } 246 | } 247 | 248 | DGifCloseFile(gif, &error); 249 | 250 | return result; 251 | } 252 | 253 | // Load a GIF image from an arbitrary data source. 254 | /*DECLSPEC GIF_Image* SDLCALL GIF_LoadImage_RW(SDL_RWops* rwops, int freerwops) 255 | { 256 | // TODO: Implement this. 257 | return NULL; 258 | }*/ 259 | 260 | 261 | static ColorMapObject* gif_copy_palette(GifFileType* gif, SDL_Palette* palette) 262 | { 263 | if(palette == NULL || palette->ncolors <= 0) 264 | return NULL; 265 | 266 | int num_colors = MIN(palette->ncolors, 256); 267 | GifColorType* colors = (GifColorType*)SDL_malloc(sizeof(GifColorType)*num_colors); 268 | SDL_Color* source_colors = palette->colors; 269 | int i; 270 | for(i = 0; i < num_colors; ++i) 271 | { 272 | colors[i].Red = source_colors[i].r; 273 | colors[i].Green = source_colors[i].g; 274 | colors[i].Blue = source_colors[i].b; 275 | } 276 | 277 | ColorMapObject* result = GifMakeMapObject(num_colors, colors); 278 | SDL_free(colors); 279 | 280 | return result; 281 | } 282 | 283 | static Uint8 gif_find_matching_index(SDL_Palette* palette, SDL_Color background_color) 284 | { 285 | if(palette == NULL) 286 | return 0; 287 | 288 | int num_colors = MIN(palette->ncolors, 256); 289 | 290 | int i; 291 | for(i = 0; i < num_colors; ++i) 292 | { 293 | if(background_color.r == palette->colors[i].r 294 | && background_color.g == palette->colors[i].g 295 | && background_color.b == palette->colors[i].b) 296 | return i; 297 | } 298 | 299 | return 0; 300 | } 301 | 302 | 303 | static SDL_bool gif_add_loop_extension(GifFileType* gif, int num_loops) 304 | { 305 | if(EGifPutExtensionLeader(gif, APPLICATION_EXT_FUNC_CODE) == GIF_ERROR) 306 | return SDL_FALSE; 307 | 308 | if(EGifPutExtensionBlock(gif, 11, "NETSCAPE2.0") == GIF_ERROR) 309 | return SDL_FALSE; 310 | 311 | Uint8 block[3] = {1, num_loops % 256, num_loops / 256}; // Loops bytes are low, high right? 312 | if(EGifPutExtensionBlock(gif, 3, block) == GIF_ERROR) 313 | return SDL_FALSE; 314 | 315 | return (SDL_bool)(EGifPutExtensionTrailer(gif) != GIF_ERROR); 316 | } 317 | 318 | 319 | static int gif_get_closest_palette_match(Uint8 r, Uint8 g, Uint8 b, SDL_Color* palette, int num_colors) 320 | { 321 | int distance = 1000; 322 | int result = 0; 323 | 324 | int index = 0; 325 | int i; 326 | for(i = 0; i < num_colors; ++i) 327 | { 328 | int dist = abs((int)(palette[i].r) - r) + abs((int)(palette[i].g) - g) + abs((int)(palette[i].b) - b); 329 | if(dist < distance) 330 | { 331 | result = index; 332 | distance = dist; 333 | } 334 | ++index; 335 | } 336 | 337 | return result; 338 | } 339 | 340 | static void gif_copy_surface_to_indices(GIF_Frame* frame, SDL_Palette* global_palette) 341 | { 342 | SDL_Surface* surface = frame->surface; 343 | if(surface == NULL) 344 | return; 345 | 346 | SDL_Palette* pal = (global_palette != NULL? global_palette : frame->local_palette); 347 | if(pal == NULL) 348 | return; 349 | 350 | int n = 0; 351 | Uint8 r,g,b; 352 | 353 | int i,j; 354 | for(i = 0; i < surface->w; ++i) 355 | { 356 | for(j = 0; j < surface->h; ++j) 357 | { 358 | Uint32 pixel = get_pixel(surface, i, j); 359 | SDL_GetRGB(pixel, surface->format, &r, &g, &b); 360 | frame->indices[n] = gif_get_closest_palette_match(r, g, b, pal->colors, MIN(pal->ncolors, 256)); 361 | ++n; 362 | } 363 | } 364 | } 365 | 366 | 367 | // Save the given image to a file. Returns SDL_FALSE on failure. 368 | DECLSPEC SDL_bool SDLCALL GIF_SaveImage(GIF_Image* image, const char* filename) 369 | { 370 | if(image == NULL || image->num_frames == 0) 371 | return SDL_FALSE; 372 | 373 | int error; 374 | 375 | // Create the output object 376 | GifFileType* gif = EGifOpenFileName(filename, SDL_FALSE, &error); 377 | if(!gif) 378 | return SDL_FALSE; 379 | 380 | 381 | // Set up screen description 382 | ColorMapObject* palette = gif_copy_palette(gif, image->global_palette); 383 | 384 | if(image->global_palette != NULL && image->use_background_color) 385 | image->background_index = gif_find_matching_index(image->global_palette, image->background_color); 386 | 387 | if(EGifPutScreenDesc(gif, image->width, image->height, 8, image->background_index, palette) == GIF_ERROR) 388 | return SDL_FALSE; 389 | 390 | 391 | if(!gif_add_loop_extension(gif, image->num_loops)) 392 | return SDL_FALSE; 393 | 394 | int i; 395 | for(i = 0; i < image->num_frames; ++i) 396 | { 397 | GIF_Frame* frame = image->frames[i]; 398 | 399 | if(image->num_frames > 1 || frame->use_transparent_color || frame->use_transparent_index) 400 | { 401 | if(frame->use_transparent_color) 402 | frame->transparent_index = gif_find_matching_index((frame->local_palette == NULL? image->global_palette : frame->local_palette), frame->transparent_color); 403 | 404 | Uint8 block[4] = {0, (frame->delay/10)%256, (frame->delay/10)/256, frame->transparent_index}; 405 | 406 | if(frame->use_transparent_color || frame->use_transparent_index) 407 | block[0] |= 0x01; 408 | if(!frame->overlay_previous) 409 | block[0] |= 0x08; 410 | 411 | if(EGifPutExtension(gif, GRAPHICS_EXT_FUNC_CODE, 4, block) == GIF_ERROR) 412 | return SDL_FALSE; 413 | } 414 | 415 | 416 | if(frame->surface != NULL) 417 | { 418 | frame->width = frame->surface->w; 419 | frame->height = frame->surface->h; 420 | } 421 | ColorMapObject* palette = gif_copy_palette(gif, frame->local_palette); 422 | if(EGifPutImageDesc(gif, frame->left_offset, frame->top_offset, frame->width, frame->height, SDL_FALSE, palette) == GIF_ERROR) 423 | { 424 | //printf("EGifPutImageDesc error: %d\n", gif->Error); 425 | return SDL_FALSE; 426 | } 427 | 428 | if(frame->indices == NULL || frame->surface != NULL) 429 | { 430 | SDL_free(frame->indices); 431 | frame->indices = (Uint8*)SDL_malloc(sizeof(Uint8)*(frame->width*frame->height)); 432 | 433 | // Copy surface data if it's there 434 | gif_copy_surface_to_indices(frame, image->global_palette); 435 | } 436 | 437 | // Write lines of pixel indices 438 | int y; 439 | int j = 0; 440 | for(y = 0; y < frame->height; ++y) 441 | { 442 | if(EGifPutLine(gif, &(frame->indices[j]), frame->width) == GIF_ERROR) 443 | return SDL_FALSE; 444 | j += frame->width; 445 | } 446 | } 447 | 448 | if(EGifCloseFile(gif, &error) == GIF_ERROR) 449 | return SDL_FALSE; 450 | 451 | return SDL_TRUE; 452 | } 453 | 454 | // Save the given image to an arbitrary destination. Returns SDL_FALSE on failure. 455 | /*DECLSPEC SDL_bool SDLCALL GIF_SaveImage_RW(GIF_Image* image, SDL_RWops* rwops, int freerwops) 456 | { 457 | return SDL_FALSE; 458 | }*/ 459 | 460 | // Free the memory allocated for the given image. 461 | DECLSPEC void SDLCALL GIF_FreeImage(GIF_Image* image) 462 | { 463 | if(image == NULL) 464 | return; 465 | 466 | SDL_FreePalette(image->global_palette); 467 | image->global_palette = NULL; 468 | 469 | 470 | Uint16 i; 471 | for(i = 0; i < image->num_frames; ++i) 472 | GIF_FreeFrame(image->frames[i]); 473 | 474 | SDL_free(image->frames); 475 | image->frames = NULL; 476 | 477 | 478 | for(i = 0; i < image->num_comments; ++i) 479 | SDL_free(image->comments[i]); 480 | 481 | SDL_free(image->comments); 482 | image->comments = NULL; 483 | 484 | 485 | SDL_free(image); 486 | } 487 | 488 | 489 | // Sets the canvas size. 490 | DECLSPEC void SDLCALL GIF_SetCanvasSize(GIF_Image* image, Uint16 width, Uint16 height) 491 | { 492 | if(image == NULL) 493 | return; 494 | 495 | image->width = width; 496 | image->height = height; 497 | } 498 | 499 | // Sets the background color. 500 | DECLSPEC void SDLCALL GIF_SetBackgroundColor(GIF_Image* image, SDL_Color background_color) 501 | { 502 | if(image == NULL) 503 | return; 504 | 505 | image->use_background_color = SDL_TRUE; 506 | image->background_color = background_color; 507 | } 508 | 509 | // Sets the background color index. 510 | DECLSPEC void SDLCALL GIF_SetBackgroundIndex(GIF_Image* image, Uint8 background_color_index) 511 | { 512 | if(image == NULL) 513 | return; 514 | 515 | image->use_background_color = SDL_FALSE; 516 | image->background_index = background_color_index; 517 | } 518 | 519 | // Sets the number of animation loops. 0 loops means to loop forever. 520 | DECLSPEC void SDLCALL GIF_SetLooping(GIF_Image* image, Uint16 num_loops) 521 | { 522 | if(image == NULL) 523 | return; 524 | 525 | image->num_loops = num_loops; 526 | } 527 | 528 | 529 | 530 | 531 | // Sets the global palette of the given image. The given palette's ownership is transferred to the GIF image, so it will be freed by GIF_Free(). 532 | DECLSPEC void SDLCALL GIF_SetGlobalPalette(GIF_Image* image, SDL_Palette* palette) 533 | { 534 | if(image == NULL) 535 | return; 536 | 537 | SDL_FreePalette(image->global_palette); 538 | image->global_palette = palette; 539 | } 540 | 541 | 542 | 543 | // Returns a new palette with a range of colors. 544 | DECLSPEC SDL_Palette* SDLCALL GIF_CreateBasicPalette(void) 545 | { 546 | SDL_Palette* result = SDL_AllocPalette(256); 547 | int i = 0; 548 | for(i = 0; i < 256; ++i) 549 | { 550 | SDL_Color c = {i, i, i, 255}; 551 | result->colors[i] = c; 552 | } 553 | 554 | return result; 555 | } 556 | 557 | // Creates a new palette from the given colors. GIF can only use the first 256 colors. The colors are copied into a new palette object. 558 | DECLSPEC SDL_Palette* SDLCALL GIF_CreatePalette(int ncolors, SDL_Color* colors) 559 | { 560 | SDL_Palette* result = SDL_AllocPalette(ncolors); 561 | 562 | int i = 0; 563 | for(i = 0; i < ncolors; ++i) 564 | { 565 | result->colors[i] = colors[i]; 566 | } 567 | 568 | return result; 569 | } 570 | 571 | 572 | // Copies a palette. 573 | DECLSPEC SDL_Palette* SDLCALL GIF_CopyPalette(SDL_Palette* palette) 574 | { 575 | SDL_Palette* result; 576 | if(palette == NULL) 577 | return NULL; 578 | 579 | result = SDL_AllocPalette(palette->ncolors); 580 | SDL_SetPaletteColors(result, palette->colors, 0, palette->ncolors); 581 | 582 | return result; 583 | } 584 | 585 | static void gif_grow_image_storage(GIF_Image* image) 586 | { 587 | GIF_Frame** old_frames; 588 | if(image == NULL) 589 | return; 590 | 591 | old_frames = image->frames; 592 | image->frame_storage_size += 10; 593 | image->frames = (GIF_Frame**)SDL_malloc(sizeof(GIF_Frame*)*image->frame_storage_size); 594 | if(old_frames == NULL) 595 | memset(image->frames, 0, sizeof(GIF_Frame*)*image->frame_storage_size); 596 | else 597 | { 598 | memcpy(image->frames, old_frames, sizeof(GIF_Frame*)*image->num_frames); 599 | SDL_free(old_frames); 600 | } 601 | } 602 | 603 | // Adds the given frame to the end of the given image. The given frame's ownership is transferred to the image, so it will be freed by GIF_FreeImage(). 604 | DECLSPEC void SDLCALL GIF_AddFrame(GIF_Image* image, GIF_Frame* frame) 605 | { 606 | if(image == NULL || frame == NULL) 607 | return; 608 | 609 | // Do we need to grow the storage? 610 | if(image->frame_storage_size < image->num_frames + 1) 611 | gif_grow_image_storage(image); 612 | 613 | image->frames[image->num_frames] = frame; 614 | image->num_frames += 1; 615 | } 616 | 617 | // Inserts the given frame before the frame at a specified index of the given image. The given frame's ownership is transferred to the image, so it will be freed by GIF_FreeImage(). 618 | DECLSPEC void SDLCALL GIF_InsertFrame(GIF_Image* image, GIF_Frame* frame, Uint16 index) 619 | { 620 | if(image == NULL || frame == NULL || index >= image->num_frames) 621 | return; 622 | 623 | // Do we need to grow the storage? 624 | if(image->frame_storage_size < image->num_frames + 1) 625 | gif_grow_image_storage(image); 626 | 627 | // Move the rest of the frames up 628 | memmove(&(image->frames[index+1]), &(image->frames[index]), (image->num_frames - index)*sizeof(GIF_Frame*)); 629 | image->frames[image->num_frames] = frame; 630 | image->num_frames += 1; 631 | } 632 | 633 | // Removes a frame from the given image and returns the frame. 634 | DECLSPEC GIF_Frame* SDLCALL GIF_RemoveFrame(GIF_Image* image, Uint16 index) 635 | { 636 | GIF_Frame* result = NULL; 637 | if(image == NULL || image->num_frames <= index) 638 | return result; 639 | 640 | result = image->frames[index]; 641 | 642 | // Shift down the array 643 | image->num_frames--; 644 | memmove(&(image->frames[index]), &(image->frames[index+1]), (image->num_frames - index)*sizeof(GIF_Frame*)); 645 | 646 | return result; 647 | } 648 | 649 | // Removes a frame from the given image and frees the frame. 650 | DECLSPEC void SDLCALL GIF_DeleteFrame(GIF_Image* image, Uint16 index) 651 | { 652 | if(image == NULL || image->num_frames <= index) 653 | return; 654 | 655 | GIF_FreeFrame(image->frames[index]); 656 | 657 | // Shift down the array 658 | image->num_frames--; 659 | memmove(&(image->frames[index]), &(image->frames[index+1]), (image->num_frames - index)*sizeof(GIF_Frame*)); 660 | } 661 | 662 | 663 | // Creates a new frame and returns it for further modification. If give_ownership is true, the given surface's ownership is transferred to the GIF frame, so it will be freed by GIF_FreeFrame() or GIF_FreeImage(). 664 | DECLSPEC GIF_Frame* SDLCALL GIF_CreateFrame(SDL_Surface* surface, SDL_bool give_ownership) 665 | { 666 | GIF_Frame* result = (GIF_Frame*)SDL_malloc(sizeof(GIF_Frame)); 667 | if(result == NULL) 668 | return NULL; 669 | 670 | memset(result, 0, sizeof(GIF_Frame)); 671 | 672 | result->delay = 100; 673 | 674 | result->surface = surface; 675 | result->owns_surface = give_ownership; 676 | if(surface != NULL) 677 | { 678 | result->width = surface->w; 679 | result->height = surface->h; 680 | } 681 | 682 | return result; 683 | } 684 | 685 | // Creates a new frame and returns it for further modification. The given indices are copied. 686 | DECLSPEC GIF_Frame* SDLCALL GIF_CreateFrameIndexed(Uint16 width, Uint16 height, Uint8* indices) 687 | { 688 | GIF_Frame* result = (GIF_Frame*)SDL_malloc(sizeof(GIF_Frame)); 689 | if(result == NULL) 690 | return NULL; 691 | 692 | memset(result, 0, sizeof(GIF_Frame)); 693 | 694 | result->delay = 100; 695 | 696 | int num_indices = width*height; 697 | 698 | if(num_indices > 0 && indices != NULL) 699 | { 700 | result->indices = (Uint8*)SDL_malloc(sizeof(Uint8)*num_indices); 701 | 702 | memcpy(result->indices, indices, num_indices); 703 | } 704 | 705 | result->width = width; 706 | result->height = height; 707 | 708 | return result; 709 | } 710 | 711 | // Free the memory allocated for the given frame. 712 | DECLSPEC void SDLCALL GIF_FreeFrame(GIF_Frame* frame) 713 | { 714 | if(frame == NULL) 715 | return; 716 | 717 | SDL_FreePalette(frame->local_palette); 718 | frame->local_palette = NULL; 719 | 720 | if(frame->owns_surface) 721 | SDL_FreeSurface(frame->surface); 722 | frame->surface = NULL; 723 | 724 | SDL_free(frame->indices); 725 | frame->indices = NULL; 726 | 727 | SDL_free(frame); 728 | } 729 | 730 | 731 | // Sets the global palette of the given frame. This will override the global palette for this frame. The given palette's ownership is transferred to the GIF image, so it will be freed by GIF_Free(). 732 | DECLSPEC void SDLCALL GIF_SetLocalPalette(GIF_Frame* frame, SDL_Palette* palette) 733 | { 734 | if(frame == NULL) 735 | return; 736 | 737 | SDL_FreePalette(frame->local_palette); 738 | frame->local_palette = palette; 739 | } 740 | 741 | // Sets the given frame's delay time to the given delay, in milliseconds. GIF only stores delays in hundredths of a second, so the saved image will truncate the given delay. 742 | DECLSPEC void SDLCALL GIF_SetDelay(GIF_Frame* frame, Uint32 delay) 743 | { 744 | if(frame == NULL) 745 | return; 746 | 747 | frame->delay = delay; 748 | } 749 | 750 | // Enables transparency for the given frame and sets the transparent color. 751 | DECLSPEC void SDLCALL GIF_SetTransparentColor(GIF_Frame* frame, SDL_Color transparent_color) 752 | { 753 | if(frame == NULL) 754 | return; 755 | 756 | frame->use_transparent_color = SDL_TRUE; 757 | frame->use_transparent_index = SDL_FALSE; 758 | frame->transparent_color = transparent_color; 759 | } 760 | 761 | // Enables transparency for the given frame and sets the transparent color index. 762 | DECLSPEC void SDLCALL GIF_SetTransparentIndex(GIF_Frame* frame, Uint8 color_index) 763 | { 764 | if(frame == NULL) 765 | return; 766 | 767 | frame->use_transparent_color = SDL_FALSE; 768 | frame->use_transparent_index = SDL_TRUE; 769 | frame->transparent_index = color_index; 770 | } 771 | 772 | // Disables transparency for the given frame. 773 | DECLSPEC void SDLCALL GIF_UnsetTransparency(GIF_Frame* frame) 774 | { 775 | if(frame == NULL) 776 | return; 777 | 778 | frame->use_transparent_color = SDL_FALSE; 779 | frame->use_transparent_index = SDL_FALSE; 780 | } 781 | 782 | // Sets the disposal method for the colors of the previous frame. SDL_FALSE discards the previous frame colors. 783 | DECLSPEC void SDLCALL GIF_SetDisposal(GIF_Frame* frame, SDL_bool overlay_previous) 784 | { 785 | if(frame == NULL) 786 | return; 787 | 788 | frame->overlay_previous = overlay_previous; 789 | } 790 | -------------------------------------------------------------------------------- /program/source/gif/gifwrap.h: -------------------------------------------------------------------------------- 1 | /* 2 | SDL_gifwrap v0.9.0 3 | by Jonathan Dearborn 4 | Pronounce it however you like... I dare you. 5 | SDL_gifwrap is a wrapper for giflib that makes loading and saving multi-frame GIF files reasonable. 6 | It does not attempt to duplicate animation features, but rather loads and stores all the information you need 7 | in a convenient container. 8 | SDL_gifwrap manipulates the following objects: 9 | Image - GIF_Image is the container for all GIF data 10 | Frame - GIF_Frame is the container for a single frame of GIF bitmap data 11 | Surface - SDL_Surface is used to store GIF bitmap data when loading a GIF_Image 12 | Palette - SDL_Palette is used to store GIF palette data 13 | Indices - These represent a single frame of GIF bitmap data and each index into a palette to refer to an RGB color 14 | // TODO: Support indices on load, surfaces on save. 15 | // Maybe remove surface from struct and rely on functions to do the conversion to/from indices. 16 | 17 | Limitations: 18 | * Color resolution is not handled. Instead, it is assumed that the image is stored as 8 bits per color channel (a color resolution value of binary 111). 19 | In particular, this means that images with reduced color resolutions, say, optimized black & white images, may not be loaded correctly and may not be saved that way. 20 | * Saving to and loading from arbitrary (non-file) data sources via SDL_rwops is not implemented yet. 21 | == Typical loading usage == 22 | Load image data 23 | GIF_LoadImage() 24 | Access gif->frames[i]->surface and gif->frames[i]->delay 25 | Free image data 26 | GIF_FreeImage() 27 | == Typical saving usage == 28 | Create data buffer 29 | GIF_CreateImage() 30 | Set size of GIF screen 31 | GIF_SetCanvasSize() 32 | Set global palette 33 | GIF_CreatePalette() if using an array of SDL_Color objects 34 | GIF_SetGlobalPalette() 35 | Set animation loop count, GIF_LOOP_FOREVER is normal 36 | GIF_SetLooping() 37 | Create and add frames 38 | Fill an array of indices or an SDL_Surface with bitmap data 39 | Create frame buffer 40 | GIF_CreateFrameIndexed() or GIF_CreateFrame() 41 | Set local palette if using per-frame palette 42 | GIF_SetLocalPalette() 43 | Set frame delay (in milliseconds, though GIF will truncate to hundredths) 44 | GIF_SetDelay() 45 | Set frame's transparent color 46 | GIF_SetTransparentIndex() or GIF_SetTransparentColor() 47 | Add frame to image container 48 | GIF_AddFrame() 49 | Write GIF file to disk 50 | GIF_SaveImage() 51 | Free image data 52 | GIF_FreeImage() 53 | Descriptions of other useful functions can be found in this header file. 54 | == EXAMPLE == 55 | GIF_Image* gif = GIF_LoadImage("my_animation.gif"); 56 | Uint16 i; 57 | for(i = 0; i < gif->num_frames; ++i) 58 | { 59 | SDL_Texture* texture = SDL_CreateTextureFromSurface(gif->frames[i]->surface); 60 | 61 | SDL_RenderCopy(renderer, texture, NULL, NULL); 62 | SDL_RenderPresent(renderer); 63 | 64 | SDL_DestroyTexture(texture); 65 | SDL_Delay(gif->frames[i]->delay); 66 | } 67 | // Make changes... 68 | GIF_Frame* new_frame = GIF_CreateFrame(other_surface, SDL_TRUE); // Ownership is transferred to gif 69 | GIF_SetDelay(new_frame, 500); 70 | GIF_AddFrame(image, new_frame); 71 | // Then save. 72 | GIF_Save(gif, "new_animation.gif"); 73 | GIF_FreeImage(gif); 74 | == LICENSE == 75 | SDL_gifwrap is licensed under the terms of the MIT license: 76 | Copyright (c) 2017 Jonathan Dearborn 77 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and 78 | associated documentation files (the "Software"), to deal in the Software without restriction, including 79 | without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 80 | copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the 81 | following conditions: 82 | The above copyright notice and this permission notice shall be included in all copies or substantial 83 | portions of the Software. 84 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT 85 | LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 86 | NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 87 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 88 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 89 | */ 90 | 91 | #ifndef _SDL_GIFWRAP_H__ 92 | #define _SDL_GIFWRAP_H__ 93 | 94 | 95 | #ifdef __cplusplus 96 | extern "C" { 97 | #endif 98 | 99 | 100 | 101 | #define GIF_LOOP_FOREVER 0 102 | 103 | 104 | typedef struct _GIF_Frame GIF_Frame; 105 | 106 | 107 | // GIF container 108 | // Represents the data needed to render a full GIF file 109 | typedef struct _GIF_Image 110 | { 111 | Uint16 width, height; 112 | SDL_Palette* global_palette; 113 | 114 | SDL_bool use_background_color; // SDL_FALSE is default and uses background_index 115 | Uint8 background_index; // Defaults to 0 116 | SDL_Color background_color; 117 | 118 | Uint16 num_loops; // 0 for loop animation forever 119 | 120 | Uint16 num_frames; 121 | Uint32 frame_storage_size; 122 | GIF_Frame** frames; 123 | 124 | Uint16 num_comments; 125 | char** comments; 126 | 127 | } GIF_Image; 128 | 129 | 130 | // Represents a single image (or animation frame) 131 | typedef struct _GIF_Frame 132 | { 133 | int left_offset, top_offset; // Relative to containing image canvas 134 | Uint16 width, height; 135 | 136 | SDL_Palette* local_palette; 137 | 138 | SDL_bool use_transparent_color; 139 | SDL_Color transparent_color; 140 | 141 | SDL_bool use_transparent_index; 142 | Uint8 transparent_index; 143 | 144 | 145 | SDL_bool overlay_previous; // GIF disposal method 146 | Uint32 delay; // In milliseconds (though GIF stores hundredths, so it is truncated when saved). Defaults to 100. 147 | 148 | 149 | // Loaded frames will have an SDL_Surface 150 | SDL_Surface* surface; 151 | SDL_bool owns_surface; 152 | 153 | // You can save frames by just palette indices 154 | Uint8* indices; 155 | 156 | } GIF_Frame; 157 | 158 | 159 | 160 | // Create and initialize a blank GIF image. 161 | extern DECLSPEC GIF_Image* SDLCALL GIF_CreateImage(void); 162 | 163 | // Load a GIF image from a file. 164 | extern DECLSPEC GIF_Image* SDLCALL GIF_LoadImage(const char* filename); 165 | 166 | // Load a GIF image from an arbitrary data source. 167 | //extern DECLSPEC GIF_Image* SDLCALL GIF_LoadImage_RW(SDL_RWops* rwops, int freerwops); 168 | 169 | // Save the given image to a file. Returns SDL_FALSE on failure. 170 | extern DECLSPEC SDL_bool SDLCALL GIF_SaveImage(GIF_Image* image, const char* filename); 171 | 172 | // Save the given image to an arbitrary destination. Returns SDL_FALSE on failure. 173 | //extern DECLSPEC SDL_bool SDLCALL GIF_SaveImage_RW(GIF_Image* image, SDL_RWops* rwops, int freerwops); 174 | 175 | // Free the memory allocated for the given image. 176 | extern DECLSPEC void SDLCALL GIF_FreeImage(GIF_Image* image); 177 | 178 | 179 | // Sets the canvas size. 180 | extern DECLSPEC void SDLCALL GIF_SetCanvasSize(GIF_Image* image, Uint16 width, Uint16 height); 181 | 182 | // Sets the background color. 183 | extern DECLSPEC void SDLCALL GIF_SetBackgroundColor(GIF_Image* image, SDL_Color background_color); 184 | 185 | // Sets the background color index. 186 | extern DECLSPEC void SDLCALL GIF_SetBackgroundIndex(GIF_Image* image, Uint8 background_color_index); 187 | 188 | // Sets the number of animation loops. 0 loops means to loop forever. 189 | extern DECLSPEC void SDLCALL GIF_SetLooping(GIF_Image* image, Uint16 num_loops); 190 | 191 | 192 | 193 | 194 | // Sets the global palette of the given image. The given palette's ownership is transferred to the GIF image, so it will be freed by GIF_Free(). 195 | extern DECLSPEC void SDLCALL GIF_SetGlobalPalette(GIF_Image* image, SDL_Palette* palette); 196 | 197 | 198 | // These palette functions are just helpful. You can manage palette data yourself with SDL_AllocPalette(), SDL_SetPaletteColors(), and SDL_FreePalette(). 199 | 200 | // Returns a new palette with a range of colors. 201 | extern DECLSPEC SDL_Palette* SDLCALL GIF_CreateBasicPalette(void); 202 | 203 | // Creates a new palette from the given colors. GIF can only use the first 256 colors. The colors are copied into a new palette object. 204 | extern DECLSPEC SDL_Palette* SDLCALL GIF_CreatePalette(int ncolors, SDL_Color* colors); 205 | 206 | // Copies a palette. 207 | extern DECLSPEC SDL_Palette* SDLCALL GIF_CopyPalette(SDL_Palette* palette); 208 | 209 | 210 | 211 | // Adds the given frame to the end of the given image. The given frame's ownership is transferred to the image, so it will be freed by GIF_FreeImage(). 212 | extern DECLSPEC void SDLCALL GIF_AddFrame(GIF_Image* image, GIF_Frame* frame); 213 | 214 | // Inserts the given frame before the frame at a specified index of the given image. The given frame's ownership is transferred to the image, so it will be freed by GIF_FreeImage(). 215 | extern DECLSPEC void SDLCALL GIF_InsertFrame(GIF_Image* image, GIF_Frame* frame, Uint16 index); 216 | 217 | // Removes a frame from the given image and returns the frame. 218 | extern DECLSPEC GIF_Frame* SDLCALL GIF_RemoveFrame(GIF_Image* image, Uint16 index); 219 | 220 | // Removes a frame from the given image and frees the frame. 221 | extern DECLSPEC void SDLCALL GIF_DeleteFrame(GIF_Image* image, Uint16 index); 222 | 223 | 224 | // Creates a new frame and returns it for further modification. If give_ownership is true, the given surface's ownership is transferred to the GIF frame, so it will be freed by GIF_FreeFrame() or GIF_FreeImage(). 225 | extern DECLSPEC GIF_Frame* SDLCALL GIF_CreateFrame(SDL_Surface* surface, SDL_bool give_ownership); 226 | 227 | // Creates a new frame and returns it for further modification. The given indices are copied. 228 | extern DECLSPEC GIF_Frame* SDLCALL GIF_CreateFrameIndexed(Uint16 width, Uint16 height, Uint8* indices); 229 | 230 | // Free the memory allocated for the given frame. 231 | extern DECLSPEC void SDLCALL GIF_FreeFrame(GIF_Frame* frame); 232 | 233 | 234 | 235 | // Sets the global palette of the given frame. This will override the global palette for this frame. The given palette's ownership is transferred to the GIF image, so it will be freed by GIF_Free(). 236 | extern DECLSPEC void SDLCALL GIF_SetLocalPalette(GIF_Frame* frame, SDL_Palette* palette); 237 | 238 | // Sets the given frame's delay time to the given delay, in milliseconds. GIF only stores delays in hundredths of a second, so the saved image will truncate the given delay. 239 | extern DECLSPEC void SDLCALL GIF_SetDelay(GIF_Frame* frame, Uint32 delay); 240 | 241 | // Enables transparency for the given frame and sets the transparent color. 242 | extern DECLSPEC void SDLCALL GIF_SetTransparentColor(GIF_Frame* frame, SDL_Color transparent_color); 243 | 244 | // Enables transparency for the given frame and sets the transparent color index. 245 | extern DECLSPEC void SDLCALL GIF_SetTransparentIndex(GIF_Frame* frame, Uint8 color_index); 246 | 247 | // Disables transparency for the given frame. 248 | extern DECLSPEC void SDLCALL GIF_UnsetTransparency(GIF_Frame* frame); 249 | 250 | // Sets the disposal method for the colors of the previous frame. SDL_FALSE discards the previous frame colors. 251 | extern DECLSPEC void SDLCALL GIF_SetDisposal(GIF_Frame* frame, SDL_bool overlay_previous); 252 | 253 | 254 | #ifdef __cplusplus 255 | } 256 | #endif 257 | 258 | #endif 259 | -------------------------------------------------------------------------------- /program/source/hairy/fidget.fs: -------------------------------------------------------------------------------- 1 | R""( 2 | 3 | #version 330 core 4 | 5 | in gdata{ 6 | vec4 color; 7 | } exg; 8 | 9 | out vec4 fragColor; 10 | 11 | void main(void) { 12 | fragColor = exg.color; 13 | } 14 | 15 | )"" 16 | -------------------------------------------------------------------------------- /program/source/hairy/fidget.gs: -------------------------------------------------------------------------------- 1 | R""( 2 | 3 | //Hairy Ball Fidget Spinner V1 4 | #version 330 core 5 | 6 | layout (triangles) in; 7 | layout (line_strip, max_vertices = 8) out; 8 | 9 | in vdata{ 10 | vec4 color; 11 | flat vec3 normal; 12 | flat vec3 fragpos; 13 | } exv[]; 14 | 15 | out gdata{ 16 | vec4 color; 17 | } exg; 18 | 19 | //Parameters 20 | float r = 0.025; 21 | int NSEG = 8; 22 | float sensitivity = 0.2; 23 | 24 | uniform float vx; //Axis (1,0,0) 25 | uniform float vy; //Axis (0,1,0) 26 | 27 | vec3 lpos = vec3(5,5,5); 28 | 29 | void emitHair(int ind){ 30 | 31 | vec3 n = normalize(exv[ind].normal); 32 | 33 | vec3 n0 = n; 34 | 35 | vec3 p = gl_in[ind].gl_Position.xyz; 36 | 37 | float shade = clamp(dot(n0, lpos), 0.3, 0.9); 38 | exg.color = vec4(vec3(1.0), 2.0)-exv[ind].color;//vec4(vec3(shade), 1.0); 39 | 40 | //Starting Position 41 | gl_Position = vec4(p, 1.0); 42 | EmitVertex(); 43 | 44 | //Add Positions 45 | for(int i = 0; i < NSEG; i++){ 46 | 47 | //Compute Position 48 | p += r*n; 49 | gl_Position = vec4(p, 1.0); 50 | 51 | EmitVertex(); 52 | 53 | //Update Direction for Next Segment 54 | vec3 r = vx*vec3(1,0,0)+vy*vec3(0,1,0); //Rotation Vector 55 | n = normalize(mix(n, cross(n, r), sensitivity)); //Cross Product 56 | } 57 | 58 | EndPrimitive(); 59 | } 60 | 61 | void main() { 62 | emitHair(0); 63 | emitHair(1); 64 | emitHair(2); 65 | } 66 | 67 | )"" 68 | -------------------------------------------------------------------------------- /program/source/hairy/fidget.vs: -------------------------------------------------------------------------------- 1 | R""( 2 | 3 | #version 330 core 4 | 5 | layout (location = 0) in vec3 in_Position; 6 | layout (location = 1) in vec3 in_Normal; 7 | 8 | uniform mat4 model; 9 | uniform mat4 vp; 10 | 11 | out vdata{ 12 | vec4 color; 13 | flat vec3 normal; 14 | flat vec3 fragpos; 15 | } exv; 16 | 17 | void main(void) { 18 | exv.fragpos = (model * vec4(in_Position, 1.0f)).xyz; 19 | exv.normal = (model * vec4(in_Normal, 1.0f)).xyz; 20 | gl_Position = vp * vec4(exv.fragpos, 1.0f); 21 | exv.color = vec4(in_Normal, 1.0); 22 | } 23 | 24 | )"" 25 | -------------------------------------------------------------------------------- /program/source/hairy/hairy.cpp: -------------------------------------------------------------------------------- 1 | #include "../../program.h" 2 | 3 | class Fidget : public Program { 4 | private: 5 | 6 | //Shader Source 7 | const std::string vs_source = 8 | #include "fidget.vs" 9 | ; 10 | const std::string fs_source = 11 | #include "fidget.fs" 12 | ; 13 | const std::string gs_source = 14 | #include "fidget.gs" 15 | ; 16 | const std::string vs_solid = 17 | #include "solid.vs" 18 | ; 19 | const std::string fs_solid = 20 | #include "solid.fs" 21 | ; 22 | 23 | //Utility Classes 24 | Model mesh; 25 | Shader* effect; 26 | Shader* solid; 27 | 28 | //Dynamics Properties 29 | float acc = 0.1; //Acceleration 30 | float frc = 0.01; //Friction 31 | 32 | public: 33 | 34 | int WIDTH, HEIGHT; 35 | float zoom = 0.05; 36 | glm::mat4 camera = glm::lookAt(glm::vec3(0, 0, 10), glm::vec3(0, 0, 0), glm::vec3(0,1,0)); 37 | glm::mat4 projection; 38 | 39 | string test = "Worked!"; 40 | 41 | float rot[3] = {0.0f}; 42 | float vrot[3] = {0.0f}; //Initial Velocity 43 | float trot[3] = {0.0f}; //Target Velocity () 44 | 45 | Fidget(svec* s, parse::data* d):Program(s){ 46 | WIDTH = d->w; 47 | HEIGHT = d->h; 48 | 49 | projection = glm::ortho(-(float)WIDTH*zoom, (float)WIDTH*zoom, -(float)HEIGHT*zoom, (float)HEIGHT*zoom, -50.0f, 50.0f); 50 | 51 | //Setup Stuff 52 | effect = new Shader({vs_source, gs_source, fs_source}, {"in_Position", "in_Normal"}, true); 53 | solid = new Shader({vs_solid, fs_solid}, {"in_Position", "in_Normal"}, true); 54 | 55 | mesh.construct(_build); 56 | 57 | //Split and Update 58 | for(int i = 0; i < 3; i++) 59 | split(&mesh); 60 | mesh.update(); 61 | } 62 | 63 | virtual void pipeline(){ 64 | glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 65 | 66 | solid->use(); //Prepare Shader 67 | solid->uniform("model", mesh.model); //Set Model Matrix 68 | solid->uniform("vp", projection*camera); //View Projection Matrix 69 | 70 | mesh.render(GL_TRIANGLES); //Render Model with Lines 71 | 72 | //Add Hair Effect 73 | effect->use(); //Prepare Shader 74 | effect->uniform("model", mesh.model); //Set Model Matrix 75 | effect->uniform("vp", projection*camera); //View Projection Matrix 76 | 77 | //Information about the Rotation 78 | effect->uniform("vx", vrot[0]); 79 | effect->uniform("vy", vrot[1]); 80 | 81 | mesh.render(GL_TRIANGLES); //Render Model with Lines 82 | 83 | 84 | } 85 | 86 | virtual void event(event::eventdata& e){ 87 | glm::mat4 R1 = glm::rotate(glm::mat4(1), glm::radians(vrot[0]), glm::vec3(1, 0, 0)); 88 | glm::mat4 R2 = glm::rotate(glm::mat4(1), glm::radians(vrot[1]), glm::vec3(0, 1, 0)); 89 | mesh.model = R1*R2*mesh.model; 90 | 91 | if(e.clicked){ 92 | vrot[1] += acc*e.rx; 93 | vrot[0] += acc*e.ry; 94 | } 95 | 96 | vrot[0] += frc*(trot[0]-vrot[0]); 97 | vrot[1] += frc*(trot[1]-vrot[1]); 98 | vrot[2] += frc*(trot[2]-vrot[2]); 99 | 100 | rot[0] += vrot[0]; 101 | rot[1] += vrot[1]; 102 | rot[2] += vrot[2]; 103 | } 104 | 105 | virtual void onpipe(std::string s){ 106 | /* ... */ 107 | } 108 | 109 | ~Fidget(){ 110 | // delete effect; 111 | delete solid; 112 | } 113 | 114 | std::function _build = [&](Model* h){ 115 | 116 | //Radius r 117 | float r = 10.0f; 118 | float a = 72.0f/360.0f*2.0f*3.14159265f; 119 | 120 | //Helper Methods 121 | auto addpos = [&](float a, float b, float c){ 122 | h->positions.push_back(a); 123 | h->positions.push_back(b); 124 | h->positions.push_back(c); 125 | }; 126 | 127 | //For a Line-Strip 128 | /* 129 | auto triangle = [&](int a, int b, int c){ 130 | h->indices.push_back(a); 131 | h->indices.push_back(b); 132 | h->indices.push_back(b); 133 | h->indices.push_back(c); 134 | h->indices.push_back(c); 135 | h->indices.push_back(a); 136 | }; 137 | */ 138 | 139 | //For a Triangles 140 | auto triangle = [&](int a, int b, int c){ 141 | h->indices.push_back(a); 142 | h->indices.push_back(b); 143 | h->indices.push_back(c); 144 | }; 145 | 146 | //Top Cap 147 | addpos(0, r, 0); 148 | 149 | for(int i = 0; i < 5; i++){ //Top 150 | float x1 = r*cos(atan(0.5))*cos(a*i); 151 | float y1 = r*cos(atan(0.5))*sin(a*i); 152 | float z1 = r*sin(atan(0.5)); 153 | addpos(x1,z1,y1); 154 | } 155 | 156 | for(int i = 0; i < 5; i++){ //Bottom 157 | float x2 = r*cos(atan(0.5))*cos(a*i+a/2.0f); 158 | float y2 = r*cos(atan(0.5))*sin(a*i+a/2.0f); 159 | float z2 = -r*sin(atan(0.5)); 160 | addpos(x2,z2,y2); 161 | } 162 | 163 | //Bottom Cap 164 | addpos(0,-r,0); 165 | 166 | for(int i = 0; i < 12; i++){ 167 | glm::vec3 n = glm::vec3(h->positions[3*i+0],h->positions[3*i+1],h->positions[3*i+2]); 168 | n = glm::normalize(n); 169 | h->normals.push_back(n.x); 170 | h->normals.push_back(n.y); 171 | h->normals.push_back(n.z); 172 | } 173 | 174 | //Top Triangles 175 | triangle(0,1,2); 176 | triangle(0,2,3); 177 | triangle(0,3,4); 178 | triangle(0,4,5); 179 | triangle(0,5,1); 180 | 181 | //Bottom Triangles 182 | triangle(6,7,11); 183 | triangle(7,8,11); 184 | triangle(8,9,11); 185 | triangle(9,10,11); 186 | triangle(10,6,11); 187 | 188 | //Connecting Triangles (Bottom) 189 | triangle(6,7,2); 190 | triangle(7,8,3); 191 | triangle(8,9,4); 192 | triangle(9,10,5); 193 | triangle(10,6,1); 194 | 195 | //Connecting Triangles (Top) 196 | triangle(2,3,7); 197 | triangle(1,2,6); 198 | triangle(3,4,8); 199 | triangle(4,5,9); 200 | triangle(5,1,10); 201 | 202 | }; 203 | 204 | 205 | std::function split = [&](Model* h){ 206 | 207 | std::vector newind; 208 | 209 | auto addpos = [&](glm::vec3 p){ 210 | h->positions.push_back(p.x); 211 | h->positions.push_back(p.y); 212 | h->positions.push_back(p.z); 213 | }; 214 | 215 | auto addnorm = [&](glm::vec3 p){ 216 | glm::vec3 n = glm::normalize(p); 217 | h->normals.push_back(n.x); 218 | h->normals.push_back(n.y); 219 | h->normals.push_back(n.z); 220 | }; 221 | 222 | auto triangle = [&](int a, int b, int c){ 223 | newind.push_back(a); 224 | newind.push_back(b); 225 | newind.push_back(c); 226 | }; 227 | 228 | float r = 10.0f; 229 | 230 | for(int i = 0; i < h->indices.size()/3; i++){ //Loop over Old Triangles 231 | 232 | //Indicies of the old triangle 233 | GLuint k1 = h->indices[3*i+0]; 234 | GLuint k2 = h->indices[3*i+1]; 235 | GLuint k3 = h->indices[3*i+2]; 236 | 237 | //Positions of the old triangle 238 | glm::vec3 a = glm::vec3(h->positions[3*k1+0], h->positions[3*k1+1], h->positions[3*k1+2]); 239 | glm::vec3 b = glm::vec3(h->positions[3*k2+0], h->positions[3*k2+1], h->positions[3*k2+2]); 240 | glm::vec3 c = glm::vec3(h->positions[3*k3+0], h->positions[3*k3+1], h->positions[3*k3+2]); 241 | 242 | //Compute Split Points 243 | glm::vec3 d = 0.5f*(a+b); 244 | glm::vec3 e = 0.5f*(b+c); 245 | glm::vec3 f = 0.5f*(c+a); 246 | 247 | //Normalize vectors to project onto sphere 248 | d = r*glm::normalize(d); 249 | e = r*glm::normalize(e); 250 | f = r*glm::normalize(f); 251 | 252 | //Starting Indices 253 | int NI = h->positions.size()/3; 254 | 255 | //Add New Positions to Model 256 | addpos(d); addpos(e); addpos(f); 257 | addnorm(d); addnorm(e); addnorm(f); 258 | 259 | triangle(k1, NI+0, NI+2); 260 | triangle(k2, NI+1, NI+0); 261 | triangle(k3, NI+2, NI+1); 262 | triangle(NI+0, NI+1, NI+2); 263 | } 264 | 265 | //Copy Indices 266 | h->indices = newind; 267 | }; 268 | 269 | }; 270 | 271 | //Exporter 272 | extern "C" { 273 | Program* create(svec* s, parse::data* d) { 274 | return new Fidget(s, d); 275 | } 276 | 277 | void destroy(Program* p) { 278 | delete p; 279 | } 280 | } 281 | -------------------------------------------------------------------------------- /program/source/hairy/solid.fs: -------------------------------------------------------------------------------- 1 | R""( 2 | 3 | #version 330 core 4 | 5 | out vec4 fragColor; 6 | 7 | in vdata{ 8 | vec4 color; 9 | flat vec3 normal; 10 | flat vec3 fragpos; 11 | } exv; 12 | 13 | void main(void) { 14 | fragColor = vec4(vec3(0.0), 1.0);//exv.color; 15 | } 16 | 17 | )"" 18 | -------------------------------------------------------------------------------- /program/source/hairy/solid.vs: -------------------------------------------------------------------------------- 1 | R""( 2 | 3 | #version 330 core 4 | 5 | layout (location = 0) in vec3 in_Position; 6 | layout (location = 1) in vec3 in_Normal; 7 | 8 | uniform mat4 model; 9 | uniform mat4 vp; 10 | 11 | out vdata{ 12 | vec4 color; 13 | flat vec3 normal; 14 | flat vec3 fragpos; 15 | } exv; 16 | 17 | void main(void) { 18 | exv.fragpos = 0.85*(model * vec4(in_Position, 1.0f)).xyz; 19 | exv.normal = (model * vec4(in_Normal, 1.0f)).xyz; 20 | gl_Position = vp * vec4(exv.fragpos, 1.0f); 21 | exv.color = vec4(in_Normal, 1.0); 22 | } 23 | 24 | )"" 25 | -------------------------------------------------------------------------------- /program/source/img/img.cpp: -------------------------------------------------------------------------------- 1 | #include "../../program.h" 2 | #include "img.h" 3 | 4 | class Image : public Program { 5 | private: 6 | 7 | //Utility Classes 8 | Square2D flat; 9 | Shader* shader; 10 | Texture* tex; 11 | 12 | //Shader Sources 13 | const std::string vs_source = 14 | #include "img.vs" 15 | ; 16 | const std::string fs_source = 17 | #include "img.fs" 18 | ; 19 | 20 | public: 21 | 22 | //Constructor 23 | Image(svec* s):Program(s){ 24 | shader = new Shader({vs_source, fs_source}, {"in_Quad, in_Tex"}, true); 25 | 26 | if(s == NULL){ 27 | logger::err("No Image Specified"); 28 | exit(0); 29 | } 30 | 31 | tex = new Texture(image::load(data->back())); 32 | } 33 | 34 | virtual void pipeline(){ 35 | glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 36 | shader->use(); 37 | shader->uniform("model", flat.model); 38 | shader->texture("imageTexture", *tex); 39 | flat.render(); 40 | } 41 | 42 | virtual void event(event::eventdata& e){ 43 | /* ... */ 44 | } 45 | 46 | virtual void onpipe(std::string s){ 47 | /* ... */ 48 | } 49 | 50 | ~Image(){ 51 | delete shader; 52 | delete tex; 53 | } 54 | }; 55 | 56 | extern "C" { 57 | Program* create(svec* s, parse::data* d) { 58 | return new Image(s); 59 | } 60 | 61 | void destroy(Program* p) { 62 | delete p; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /program/source/img/img.fs: -------------------------------------------------------------------------------- 1 | R""( 2 | 3 | #version 130 4 | in vec2 ex_Tex; 5 | out vec4 fragColor; 6 | 7 | uniform sampler2D imageTexture; 8 | 9 | void main(){ 10 | fragColor = texture(imageTexture, ex_Tex); 11 | if(fragColor.a == 0.0) discard; 12 | 13 | //Invert Color 14 | //fragColor = vec4(vec3(1.0) - fragColor.xyz, 1.0); 15 | } 16 | 17 | )"" 18 | -------------------------------------------------------------------------------- /program/source/img/img.h: -------------------------------------------------------------------------------- 1 | namespace image { 2 | 3 | SDL_Surface* load(std::string path){ 4 | SDL_Surface* loaded = IMG_Load(path.c_str()); 5 | if(loaded == NULL){ 6 | logger::err("Couldn't open image", path); 7 | exit(0); 8 | } 9 | SDL_Surface* optimized = SDL_ConvertSurfaceFormat(loaded, SDL_PIXELFORMAT_RGBA32, 0); 10 | SDL_FreeSurface(loaded); 11 | return optimized; 12 | } 13 | 14 | void save(SDL_Surface* surface, std::string path){ 15 | IMG_SavePNG(surface, path.c_str()); 16 | } 17 | 18 | template 19 | SDL_Surface* make(glm::vec2 size, T* data, std::function handle){ 20 | SDL_Surface *s = SDL_CreateRGBSurface(0, size.x, size.y, 32, 0, 0, 0, 0); 21 | SDL_LockSurface(s); 22 | 23 | unsigned char* img_raw = (unsigned char*)s->pixels; //Raw Data 24 | 25 | for(int i = 0; i < size.x*size.y; i++){ 26 | glm::vec4 color = handle(data[i]); //Construct from Algorithm 27 | *(img_raw+4*i) = (unsigned char)(255*color.x); 28 | *(img_raw+4*i+1) = (unsigned char)(255*color.y); 29 | *(img_raw+4*i+2) = (unsigned char)(255*color.z); 30 | *(img_raw+4*i+3) = (unsigned char)(255*color.w); 31 | } 32 | 33 | SDL_UnlockSurface(s); 34 | return s; 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /program/source/img/img.vs: -------------------------------------------------------------------------------- 1 | R""( 2 | 3 | #version 130 4 | in vec2 in_Quad; 5 | in vec2 in_Tex; 6 | out vec2 ex_Tex; 7 | 8 | uniform mat4 model; 9 | 10 | void main(void) { 11 | ex_Tex = in_Tex; 12 | gl_Position = model*vec4(in_Quad, -1.0, 1.0); 13 | } 14 | 15 | )"" 16 | -------------------------------------------------------------------------------- /program/source/spiky/fidget.fs: -------------------------------------------------------------------------------- 1 | R""( 2 | 3 | #version 330 core 4 | 5 | in gdata{ 6 | vec4 color; 7 | } exg; 8 | 9 | out vec4 fragColor; 10 | 11 | void main(void) { 12 | fragColor = exg.color; 13 | } 14 | 15 | )"" 16 | -------------------------------------------------------------------------------- /program/source/spiky/fidget.gs: -------------------------------------------------------------------------------- 1 | R""( 2 | 3 | //Spiky Ball Fidget Spinner V1 4 | 5 | #version 330 core 6 | 7 | layout (triangles) in; 8 | layout (line_strip, max_vertices = 8) out; 9 | 10 | in vdata{ 11 | vec4 color; 12 | flat vec3 normal; 13 | flat vec3 fragpos; 14 | } exv[]; 15 | 16 | out gdata{ 17 | vec4 color; 18 | } exg; 19 | 20 | uniform float vx; //Axis (1,0,0) 21 | uniform float vy; //Axis (0,1,0) 22 | 23 | void main() { 24 | 25 | //Expansion Value 26 | float k = 0.25; //Sensitivity 27 | float s = abs(vx)+abs(vy); 28 | s = k*s/(1+k*s); //Langmuir Style Attack 29 | 30 | //Max / Min Vals 31 | float _max = 5; 32 | float _min = 2; 33 | s = _min + s*(_max-_min); 34 | 35 | //Compute Centroid Position 36 | vec4 cpos = (gl_in[0].gl_Position+gl_in[1].gl_Position+gl_in[2].gl_Position); 37 | cpos.xyz = s*cpos.xyz / length(cpos); 38 | 39 | //Color of Centroid 40 | vec4 ccol = (exv[0].color + exv[1].color + exv[2].color)/3; 41 | 42 | //Construct Line Strip 43 | gl_Position = gl_in[0].gl_Position; 44 | exg.color = exv[0].color; 45 | EmitVertex(); 46 | gl_Position = gl_in[1].gl_Position; 47 | exg.color = exv[1].color; 48 | EmitVertex(); 49 | gl_Position = gl_in[2].gl_Position; 50 | exg.color = exv[2].color; 51 | EmitVertex(); 52 | gl_Position = gl_in[0].gl_Position; 53 | exg.color = exv[0].color; 54 | EmitVertex(); 55 | gl_Position = cpos; 56 | exg.color = ccol; 57 | EmitVertex(); 58 | gl_Position = gl_in[1].gl_Position; 59 | exg.color = exv[1].color; 60 | EmitVertex(); 61 | gl_Position = gl_in[2].gl_Position; 62 | exg.color = exv[2].color; 63 | EmitVertex(); 64 | gl_Position = cpos; 65 | exg.color = ccol; 66 | EmitVertex(); 67 | EndPrimitive(); 68 | 69 | } 70 | 71 | )"" 72 | -------------------------------------------------------------------------------- /program/source/spiky/fidget.vs: -------------------------------------------------------------------------------- 1 | R""( 2 | 3 | #version 330 core 4 | 5 | layout (location = 0) in vec3 in_Position; 6 | layout (location = 1) in vec3 in_Normal; 7 | 8 | uniform mat4 model; 9 | uniform mat4 vp; 10 | 11 | out vdata{ 12 | vec4 color; 13 | flat vec3 normal; 14 | flat vec3 fragpos; 15 | } exv; 16 | 17 | void main(void) { 18 | exv.fragpos = (model * vec4(in_Position, 1.0f)).xyz; 19 | exv.normal = (model * vec4(in_Normal, 1.0f)).xyz; 20 | gl_Position = vp * vec4(exv.fragpos, 1.0f); 21 | exv.color = vec4(vec3(1)-in_Normal, 1.0); 22 | } 23 | 24 | )"" 25 | -------------------------------------------------------------------------------- /program/source/spiky/solid.fs: -------------------------------------------------------------------------------- 1 | R""( 2 | 3 | #version 330 core 4 | 5 | out vec4 fragColor; 6 | 7 | in vdata{ 8 | vec4 color; 9 | flat vec3 normal; 10 | flat vec3 fragpos; 11 | } exv; 12 | 13 | void main(void) { 14 | fragColor = vec4(vec3(0.0), 1.0);//exv.color; 15 | } 16 | 17 | )"" 18 | -------------------------------------------------------------------------------- /program/source/spiky/solid.vs: -------------------------------------------------------------------------------- 1 | R""( 2 | 3 | #version 330 core 4 | 5 | layout (location = 0) in vec3 in_Position; 6 | layout (location = 1) in vec3 in_Normal; 7 | 8 | uniform mat4 model; 9 | uniform mat4 vp; 10 | 11 | out vdata{ 12 | vec4 color; 13 | flat vec3 normal; 14 | flat vec3 fragpos; 15 | } exv; 16 | 17 | void main(void) { 18 | exv.fragpos = 0.9*(model * vec4(in_Position, 1.0f)).xyz; 19 | exv.normal = (model * vec4(in_Normal, 1.0f)).xyz; 20 | gl_Position = vp * vec4(exv.fragpos, 1.0f); 21 | exv.color = vec4(in_Normal, 1.0); 22 | } 23 | 24 | )"" 25 | -------------------------------------------------------------------------------- /program/source/spiky/spiky.cpp: -------------------------------------------------------------------------------- 1 | #include "../../program.h" 2 | 3 | class Fidget : public Program { 4 | private: 5 | 6 | //Shader Source 7 | const std::string vs_source = 8 | #include "fidget.vs" 9 | ; 10 | const std::string fs_source = 11 | #include "fidget.fs" 12 | ; 13 | const std::string gs_source = 14 | #include "fidget.gs" 15 | ; 16 | const std::string vs_solid = 17 | #include "solid.vs" 18 | ; 19 | const std::string fs_solid = 20 | #include "solid.fs" 21 | ; 22 | 23 | //Utility Classes 24 | Model mesh; 25 | Shader* effect; 26 | Shader* solid; 27 | 28 | //Dynamics Properties 29 | float acc = 0.1; //Acceleration 30 | float frc = 0.01; //Friction 31 | 32 | public: 33 | 34 | int WIDTH, HEIGHT; 35 | float zoom = 0.05; 36 | glm::mat4 camera = glm::lookAt(glm::vec3(0, 0, 10), glm::vec3(0, 0, 0), glm::vec3(0,1,0)); 37 | glm::mat4 projection; 38 | 39 | string test = "Worked!"; 40 | 41 | float rot[3] = {0.0f}; 42 | float vrot[3] = {0.0f}; //Initial Velocity 43 | float trot[3] = {0.0f}; //Target Velocity () 44 | 45 | Fidget(svec* s, parse::data* d):Program(s){ 46 | WIDTH = d->w; 47 | HEIGHT = d->h; 48 | 49 | projection = glm::ortho(-(float)WIDTH*zoom, (float)WIDTH*zoom, -(float)HEIGHT*zoom, (float)HEIGHT*zoom, -50.0f, 50.0f); 50 | 51 | //Setup Stuff 52 | effect = new Shader({vs_source, gs_source, fs_source}, {"in_Position", "in_Normal"}, true); 53 | solid = new Shader({vs_solid, fs_solid}, {"in_Position", "in_Normal"}, true); 54 | 55 | mesh.construct(_build); 56 | 57 | split(&mesh); 58 | mesh.update(); 59 | } 60 | 61 | virtual void pipeline(){ 62 | glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 63 | 64 | solid->use(); //Prepare Shader 65 | solid->uniform("model", mesh.model); //Set Model Matrix 66 | solid->uniform("vp", projection*camera); //View Projection Matrix 67 | 68 | mesh.render(GL_TRIANGLES); //Render Model with Lines 69 | 70 | //Add Hair Effect 71 | effect->use(); //Prepare Shader 72 | effect->uniform("model", mesh.model); //Set Model Matrix 73 | effect->uniform("vp", projection*camera); //View Projection Matrix 74 | 75 | //Information about the Rotation 76 | effect->uniform("vx", vrot[0]); 77 | effect->uniform("vy", vrot[1]); 78 | 79 | mesh.render(GL_TRIANGLES); //Render Model with Lines 80 | 81 | 82 | } 83 | 84 | virtual void event(event::eventdata& e){ 85 | glm::mat4 R1 = glm::rotate(glm::mat4(1), glm::radians(vrot[0]), glm::vec3(1, 0, 0)); 86 | glm::mat4 R2 = glm::rotate(glm::mat4(1), glm::radians(vrot[1]), glm::vec3(0, 1, 0)); 87 | mesh.model = R1*R2*mesh.model; 88 | 89 | if(e.clicked){ 90 | vrot[1] += acc*e.rx; 91 | vrot[0] += acc*e.ry; 92 | } 93 | 94 | vrot[0] += frc*(trot[0]-vrot[0]); 95 | vrot[1] += frc*(trot[1]-vrot[1]); 96 | vrot[2] += frc*(trot[2]-vrot[2]); 97 | 98 | rot[0] += vrot[0]; 99 | rot[1] += vrot[1]; 100 | rot[2] += vrot[2]; 101 | } 102 | 103 | virtual void onpipe(std::string s){ 104 | /* ... */ 105 | } 106 | 107 | ~Fidget(){ 108 | delete effect; 109 | delete solid; 110 | } 111 | 112 | std::function _build = [&](Model* h){ 113 | 114 | //Radius r 115 | float r = 10.0f; 116 | float a = 72.0f/360.0f*2.0f*3.14159265f; 117 | 118 | //Helper Methods 119 | auto addpos = [&](float a, float b, float c){ 120 | h->positions.push_back(a); 121 | h->positions.push_back(b); 122 | h->positions.push_back(c); 123 | }; 124 | 125 | //For a Line-Strip 126 | /* 127 | auto triangle = [&](int a, int b, int c){ 128 | h->indices.push_back(a); 129 | h->indices.push_back(b); 130 | h->indices.push_back(b); 131 | h->indices.push_back(c); 132 | h->indices.push_back(c); 133 | h->indices.push_back(a); 134 | }; 135 | */ 136 | 137 | //For a Triangles 138 | auto triangle = [&](int a, int b, int c){ 139 | h->indices.push_back(a); 140 | h->indices.push_back(b); 141 | h->indices.push_back(c); 142 | }; 143 | 144 | //Top Cap 145 | addpos(0, r, 0); 146 | 147 | for(int i = 0; i < 5; i++){ //Top 148 | float x1 = r*cos(atan(0.5))*cos(a*i); 149 | float y1 = r*cos(atan(0.5))*sin(a*i); 150 | float z1 = r*sin(atan(0.5)); 151 | addpos(x1,z1,y1); 152 | } 153 | 154 | for(int i = 0; i < 5; i++){ //Bottom 155 | float x2 = r*cos(atan(0.5))*cos(a*i+a/2.0f); 156 | float y2 = r*cos(atan(0.5))*sin(a*i+a/2.0f); 157 | float z2 = -r*sin(atan(0.5)); 158 | addpos(x2,z2,y2); 159 | } 160 | 161 | //Bottom Cap 162 | addpos(0,-r,0); 163 | 164 | for(int i = 0; i < 12; i++){ 165 | glm::vec3 n = glm::vec3(h->positions[3*i+0],h->positions[3*i+1],h->positions[3*i+2]); 166 | n = glm::normalize(n); 167 | h->normals.push_back(n.x); 168 | h->normals.push_back(n.y); 169 | h->normals.push_back(n.z); 170 | } 171 | 172 | //Top Triangles 173 | triangle(0,1,2); 174 | triangle(0,2,3); 175 | triangle(0,3,4); 176 | triangle(0,4,5); 177 | triangle(0,5,1); 178 | 179 | //Bottom Triangles 180 | triangle(6,7,11); 181 | triangle(7,8,11); 182 | triangle(8,9,11); 183 | triangle(9,10,11); 184 | triangle(10,6,11); 185 | 186 | //Connecting Triangles (Bottom) 187 | triangle(6,7,2); 188 | triangle(7,8,3); 189 | triangle(8,9,4); 190 | triangle(9,10,5); 191 | triangle(10,6,1); 192 | 193 | //Connecting Triangles (Top) 194 | triangle(2,3,7); 195 | triangle(1,2,6); 196 | triangle(3,4,8); 197 | triangle(4,5,9); 198 | triangle(5,1,10); 199 | 200 | }; 201 | 202 | 203 | std::function split = [&](Model* h){ 204 | 205 | std::vector newind; 206 | 207 | auto addpos = [&](glm::vec3 p){ 208 | h->positions.push_back(p.x); 209 | h->positions.push_back(p.y); 210 | h->positions.push_back(p.z); 211 | }; 212 | 213 | auto addnorm = [&](glm::vec3 p){ 214 | glm::vec3 n = glm::normalize(p); 215 | h->normals.push_back(n.x); 216 | h->normals.push_back(n.y); 217 | h->normals.push_back(n.z); 218 | }; 219 | 220 | auto triangle = [&](int a, int b, int c){ 221 | newind.push_back(a); 222 | newind.push_back(b); 223 | newind.push_back(c); 224 | }; 225 | 226 | float r = 10.0f; 227 | 228 | for(int i = 0; i < h->indices.size()/3; i++){ //Loop over Old Triangles 229 | 230 | //Indicies of the old triangle 231 | GLuint k1 = h->indices[3*i+0]; 232 | GLuint k2 = h->indices[3*i+1]; 233 | GLuint k3 = h->indices[3*i+2]; 234 | 235 | //Positions of the old triangle 236 | glm::vec3 a = glm::vec3(h->positions[3*k1+0], h->positions[3*k1+1], h->positions[3*k1+2]); 237 | glm::vec3 b = glm::vec3(h->positions[3*k2+0], h->positions[3*k2+1], h->positions[3*k2+2]); 238 | glm::vec3 c = glm::vec3(h->positions[3*k3+0], h->positions[3*k3+1], h->positions[3*k3+2]); 239 | 240 | //Compute Split Points 241 | glm::vec3 d = 0.5f*(a+b); 242 | glm::vec3 e = 0.5f*(b+c); 243 | glm::vec3 f = 0.5f*(c+a); 244 | 245 | //Normalize vectors to project onto sphere 246 | d = r*glm::normalize(d); 247 | e = r*glm::normalize(e); 248 | f = r*glm::normalize(f); 249 | 250 | //Starting Indices 251 | int NI = h->positions.size()/3; 252 | 253 | //Add New Positions to Model 254 | addpos(d); addpos(e); addpos(f); 255 | addnorm(d); addnorm(e); addnorm(f); 256 | 257 | triangle(k1, NI+0, NI+2); 258 | triangle(k2, NI+1, NI+0); 259 | triangle(k3, NI+2, NI+1); 260 | triangle(NI+0, NI+1, NI+2); 261 | } 262 | 263 | //Copy Indices 264 | h->indices = newind; 265 | }; 266 | 267 | }; 268 | 269 | //Exporter 270 | extern "C" { 271 | Program* create(svec* s, parse::data* d) { 272 | return new Fidget(s, d); 273 | } 274 | 275 | void destroy(Program* p) { 276 | delete p; 277 | } 278 | } 279 | -------------------------------------------------------------------------------- /program/source/text/text.cpp: -------------------------------------------------------------------------------- 1 | #include "../../program.h" 2 | #include 3 | #include 4 | 5 | glm::vec4 hexparse(std::string s){ 6 | 7 | //Remove Leading 0x 8 | if(s.substr(0,2) == "0x") 9 | s = s.substr(2); 10 | 11 | std::stringstream ss; 12 | int val = 0; 13 | glm::vec4 vec(0); 14 | 15 | for(int i = 0; i < 4; i++){ 16 | if(s.empty()) break; 17 | ss.clear(); 18 | ss << std::hex << s.substr(0,2); 19 | ss >> val; 20 | vec[i] = val; 21 | s = s.substr(2); 22 | } 23 | 24 | return vec; 25 | } 26 | 27 | class Text : public Program { 28 | private: 29 | 30 | //Utility Classes 31 | Square2D flat; 32 | Shader* shader = NULL; 33 | Texture* tex = NULL; 34 | 35 | int fontsize = 18; 36 | string fontface = ".ttf"; 37 | int style; 38 | 39 | SDL_Color colorf; 40 | glm::vec4 color = glm::vec4(0,0,0,255); 41 | TTF_Font* font = NULL; 42 | glm::vec4 bgcolor = glm::vec4(0,0,0,0); 43 | 44 | int halign = 0; //0 = center, 1 = left, 2 = right 45 | int valign = 0; //0 = center, 1 = up, 2 = down 46 | int W, H; 47 | 48 | glm::mat4 model = glm::mat4(1.0); 49 | 50 | //Shader Sources 51 | const std::string vs_source = 52 | #include "text.vs" 53 | ; 54 | const std::string fs_source = 55 | #include "text.fs" 56 | ; 57 | 58 | void settext(std::string line){ 59 | 60 | if(tex != NULL) 61 | delete tex; 62 | 63 | tex = new Texture(); 64 | 65 | //Construct a surface from the text 66 | SDL_Surface* surface = TTF_RenderText_Blended(font, line.c_str(), colorf); 67 | 68 | tex->raw(surface, [](Texture* t){ //Setup Tex Parameters Correctly 69 | glTexParameteri(t->type, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 70 | glTexParameteri(t->type, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 71 | glTexParameteri(t->type, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 72 | glTexParameteri(t->type, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 73 | glTexParameteri(t->type, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); 74 | }); 75 | 76 | //Scale the text to font size 77 | glm::vec3 scale = glm::vec3((float)tex->w/(float)W, (float)tex->h/(float)H, 1.0); 78 | glm::vec3 shift = glm::vec3(0); //Default: Centered 79 | 80 | if(halign == 1) //Left 81 | shift.x -= ((float)W - (float)tex->w)/(float)W; 82 | else if(halign == 2) //Right 83 | shift.x += ((float)W - (float)tex->h)/(float)W; 84 | 85 | if(valign == 1) //Up 86 | shift.y += ((float)H - (float)tex->h)/(float)H; 87 | else if(valign == 2) //Down 88 | shift.y -= ((float)H - (float)tex->h)/(float)H; 89 | 90 | model = glm::mat4(1); 91 | model = glm::translate(model, shift); 92 | model = glm::scale(model, scale); 93 | 94 | } 95 | 96 | public: 97 | 98 | //Constructor 99 | Text(svec* s, parse::data* d):Program(s){ 100 | 101 | TTF_Init(); 102 | 103 | //Font Size / Face 104 | if(d->pflags["-fs"]) fontsize = stoi(d->params["-fs0"]); 105 | if(d->pflags["-ff"]) fontface = d->params["-ff0"]; 106 | else{ 107 | std::cout<<"No Font Specified"<flags["--fu"]) style |= TTF_STYLE_UNDERLINE; 131 | if(d->flags["--fs"]) style |= TTF_STYLE_STRIKETHROUGH; 132 | if(d->flags["--fi"]) style |= TTF_STYLE_ITALIC; 133 | if(d->flags["--fb"]) style |= TTF_STYLE_BOLD; 134 | 135 | TTF_SetFontStyle(font, style); 136 | 137 | //Extract Color 138 | if(d->pflags["-fc"]) 139 | color = hexparse(d->params["-fc0"]); 140 | colorf = {color.x, color.y, color.z, color.w}; 141 | 142 | if(d->pflags["-bc"]) 143 | bgcolor = hexparse(d->params["-bc0"])/255.0f; 144 | 145 | //Vertical / Horizontal Alignment 146 | if(d->pflags["-v"]) valign = stoi(d->params["-v0"]); 147 | if(d->pflags["-h"]) halign = stoi(d->params["-h0"]); 148 | W = d->w; H = d->h; 149 | 150 | //Create Shader 151 | shader = new Shader({vs_source, fs_source}, {"in_Quad, in_Tex"}, true); 152 | 153 | //Create Text Texture 154 | if(!s->empty()) 155 | settext(s->back()); 156 | 157 | } 158 | 159 | virtual void pipeline(){ 160 | if(tex == NULL) return; 161 | glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 162 | shader->use(); 163 | shader->uniform("model", model); 164 | shader->uniform("bgcolor", bgcolor); 165 | shader->texture("imageTexture", *tex); 166 | flat.render(); 167 | } 168 | 169 | virtual void event(event::eventdata& e){ 170 | /* ... */ 171 | } 172 | 173 | virtual void onpipe(std::string s){ 174 | settext(s); 175 | } 176 | 177 | ~Text(){ 178 | if(shader != NULL) delete shader; 179 | if(tex != NULL) delete tex; 180 | if(font != NULL) TTF_CloseFont(font); 181 | TTF_Quit(); 182 | } 183 | }; 184 | 185 | extern "C" { 186 | Program* create(svec* s, parse::data* d) { 187 | return new Text(s, d); 188 | } 189 | 190 | void destroy(Program* p) { 191 | delete p; 192 | } 193 | } 194 | -------------------------------------------------------------------------------- /program/source/text/text.fs: -------------------------------------------------------------------------------- 1 | R""( 2 | 3 | #version 130 4 | in vec2 ex_Tex; 5 | out vec4 fragColor; 6 | 7 | uniform sampler2D imageTexture; 8 | uniform vec4 bgcolor; 9 | 10 | void main(){ 11 | 12 | fragColor = vec4(bgcolor); 13 | vec4 texcolor = texture(imageTexture, ex_Tex); 14 | fragColor = mix(fragColor, texcolor, texcolor.a); 15 | 16 | } 17 | 18 | )"" 19 | -------------------------------------------------------------------------------- /program/source/text/text.vs: -------------------------------------------------------------------------------- 1 | R""( 2 | 3 | #version 130 4 | in vec2 in_Quad; 5 | in vec2 in_Tex; 6 | out vec2 ex_Tex; 7 | 8 | uniform mat4 model; 9 | 10 | void main(void) { 11 | ex_Tex = in_Tex; 12 | gl_Position = model*vec4(in_Quad, -1.0, 1.0); 13 | } 14 | 15 | )"" 16 | -------------------------------------------------------------------------------- /program/utility/instance.cpp: -------------------------------------------------------------------------------- 1 | class Instance{ 2 | public: 3 | 4 | Instance(Primitive* _m){ 5 | m = _m; 6 | }; 7 | 8 | ~Instance(){ 9 | for(auto& b: instances) 10 | glDeleteBuffers(1, &b); 11 | } 12 | 13 | Primitive* m; //Instanced Render Model (must be derived from primitive) 14 | std::vector instances; //Instance VBO Pointers 15 | unsigned int SIZE; //Number of Instances 16 | 17 | template void addBuffer(std::vector& buf); 18 | template void updateBuffer(std::vector& buf, int index); 19 | template void configBuffer(GLuint instance); 20 | 21 | void render(GLenum mode = GL_TRIANGLE_STRIP); //Default because of primitive models 22 | void render(GLenum mode, int size); //Default because of primitive models 23 | }; 24 | 25 | template 26 | void Instance::addBuffer(std::vector& buf){ 27 | GLuint instance; 28 | glGenBuffers(1, &instance); 29 | SIZE = buf.size(); //Update the Number of Instances 30 | 31 | glBindVertexArray(m->vao); 32 | glBindBuffer(GL_ARRAY_BUFFER, instance); //Bind Instance Buffer and Data 33 | glBufferData(GL_ARRAY_BUFFER, SIZE*sizeof(D), &buf[0], GL_STATIC_DRAW); 34 | 35 | configBuffer(instance); 36 | } 37 | 38 | template 39 | void Instance::configBuffer(GLuint instance){ 40 | glEnableVertexAttribArray(m->vbo.size()+instances.size()); 41 | glVertexAttribPointer(m->vbo.size()+instances.size(), sizeof(D)/sizeof(GLfloat), GL_FLOAT, GL_FALSE, 0, (void*)0); 42 | glVertexAttribDivisor(m->vbo.size()+instances.size(), 1); 43 | instances.push_back(instance); 44 | } 45 | 46 | template<> //For Matrices - Special Procedure 47 | void Instance::configBuffer(GLuint instance){ 48 | for(int i = 0; i < 4; i++){ 49 | glEnableVertexAttribArray(m->vbo.size()+instances.size()); 50 | glVertexAttribPointer(m->vbo.size()+instances.size(), 4, GL_FLOAT, GL_FALSE, 4*sizeof(glm::vec4), (void*)(i*sizeof(glm::vec4))); 51 | glVertexAttribDivisor(m->vbo.size()+instances.size(), 1); 52 | instances.push_back(instance); 53 | } 54 | } 55 | 56 | template 57 | void Instance::updateBuffer(std::vector& buf, int index){ 58 | glBindVertexArray(m->vao); 59 | glBindBuffer(GL_ARRAY_BUFFER, instances[index]); //Bind Instance Buffer and Data 60 | if(buf.size() != SIZE) glBufferData(GL_ARRAY_BUFFER, buf.size()*sizeof(D), &buf[0], GL_STATIC_DRAW); 61 | else glBufferSubData(GL_ARRAY_BUFFER, 0, SIZE*sizeof(D), &buf[0]); 62 | SIZE = buf.size(); 63 | } 64 | 65 | void Instance::render(GLenum mode){ 66 | glBindVertexArray(m->vao); 67 | glDrawArraysInstanced(mode, 0, m->SIZE, SIZE); //Instanced render 68 | } 69 | 70 | void Instance::render(GLenum mode, int size){ 71 | glBindVertexArray(m->vao); 72 | glDrawArraysInstanced(mode, 0, m->SIZE, size); //Instanced render 73 | } 74 | -------------------------------------------------------------------------------- /program/utility/model.cpp: -------------------------------------------------------------------------------- 1 | struct Primitive{ 2 | 3 | Primitive(){ 4 | glGenVertexArrays(1, &vao); 5 | glBindVertexArray(vao); 6 | addBuffers(2); 7 | } 8 | 9 | ~Primitive(){ 10 | glDisableVertexAttribArray(vao); 11 | clearBuffers(); 12 | glDeleteVertexArrays(1, &vao); 13 | } 14 | 15 | void addBuffers(int n){ 16 | GLuint nvbo; 17 | for(int i = 0; i < n; i++){ 18 | glGenBuffers(1, &nvbo); 19 | vbo.push_back(nvbo); 20 | } 21 | } 22 | 23 | void clearBuffers(){ 24 | while(!vbo.empty()){ 25 | glDeleteBuffers(1, &(vbo.back())); 26 | vbo.pop_back(); 27 | } 28 | } 29 | 30 | GLuint vao; 31 | std::vector vbo; 32 | size_t SIZE = 4; 33 | 34 | template 35 | void bind(int index, int count, int size, T* data){ 36 | glBindBuffer(GL_ARRAY_BUFFER, vbo[index]); 37 | glBufferData(GL_ARRAY_BUFFER, count*sizeof(T), data, GL_DYNAMIC_DRAW); 38 | attrib(index, size); 39 | } 40 | 41 | template 42 | void attrib(int index, int size){ 43 | std::cout<<"Data type not recognized."< c):Model(){ 138 | construct(c); 139 | } 140 | 141 | template //Pass Parameters to Model Constructing Function 142 | Model(std::function c, T... t):Model(){ 143 | construct(c, t...); 144 | } 145 | 146 | ~Model(){ 147 | glDisableVertexAttribArray(vao); 148 | clearBuffers(); 149 | glDeleteBuffers(1, &ibo); 150 | glDeleteVertexArrays(1, &vao); 151 | } 152 | 153 | std::vector positions, normals, colors; 154 | std::vector indices; 155 | 156 | bool indexed = true; 157 | GLuint ibo; 158 | 159 | void update(){ 160 | glBindVertexArray(vao); 161 | bind(0, positions.size(), 3, &positions[0]); 162 | if(!normals.empty()) bind(1, normals.size(), 3, &normals[0]); 163 | if(!colors.empty()) bind(2, colors.size(), 4, &colors[0]); 164 | 165 | SIZE = indices.size(); 166 | glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo); 167 | glBufferData(GL_ELEMENT_ARRAY_BUFFER, SIZE*sizeof(GLuint), &indices[0], GL_DYNAMIC_DRAW); 168 | } 169 | 170 | void construct(std::function constructor){ 171 | positions.clear(); 172 | normals.clear(); 173 | colors.clear(); 174 | indices.clear(); 175 | 176 | (constructor)(this); //Call user-defined constructor 177 | update(); //Update VAO / VBO / IBO 178 | } 179 | 180 | template 181 | void construct(std::function constructor, T... t){ 182 | positions.clear(); 183 | normals.clear(); 184 | colors.clear(); 185 | indices.clear(); 186 | 187 | (constructor)(this, t...); //Call user-defined constructor 188 | update(); //Update VAO / VBO / IBO 189 | } 190 | 191 | void render(GLenum mode = GL_TRIANGLES){ 192 | glBindVertexArray(vao); 193 | if(indexed){ 194 | glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo); 195 | glDrawElements(mode, SIZE, GL_UNSIGNED_INT, 0); 196 | } 197 | else glDrawArrays(mode, 0, positions.size()/3); 198 | } 199 | 200 | template 201 | void add(std::vector& v, D a); 202 | }; 203 | 204 | template<> 205 | void Primitive::attrib(int index, int size){ 206 | glEnableVertexAttribArray(index); 207 | glVertexAttribPointer(index, size, GL_FLOAT, GL_FALSE, 0, 0); 208 | } 209 | 210 | template<> 211 | void Model::add(std::vector& v, glm::vec3 a){ 212 | v.push_back(a.x); 213 | v.push_back(a.y); 214 | v.push_back(a.z); 215 | } 216 | 217 | template<> 218 | void Model::add(std::vector& v, glm::vec4 a){ 219 | v.push_back(a.x); 220 | v.push_back(a.y); 221 | v.push_back(a.z); 222 | v.push_back(a.w); 223 | } 224 | -------------------------------------------------------------------------------- /program/utility/shader.cpp: -------------------------------------------------------------------------------- 1 | class Shader{ 2 | public: 3 | Shader(slist shaders, slist in){ 4 | program = glCreateProgram(); //Generate Shader 5 | setup(shaders); //Add Individual Shaders 6 | for(auto &n : in) //Add all Attributes of Shader 7 | glBindAttribLocation(program, &n - in.begin(), n.c_str()); 8 | link(); //Link the shader program! 9 | } 10 | 11 | Shader(slist shaders, slist in, bool source){ 12 | program = glCreateProgram(); //Generate Shader 13 | 14 | std::vector _src = shaders; 15 | 16 | char* src; 17 | int32_t size; 18 | 19 | if(shaders.size() == 2){ 20 | 21 | vertexShader = glCreateShader(GL_VERTEX_SHADER); 22 | src = const_cast(_src[0].c_str()); 23 | size = _src[0].length(); 24 | glShaderSource(vertexShader, 1, &src, &size); 25 | compile(vertexShader); 26 | 27 | fragmentShader = glCreateShader(GL_FRAGMENT_SHADER); 28 | src = const_cast(_src[1].c_str()); 29 | size = _src[1].length(); 30 | glShaderSource(fragmentShader, 1, &src, &size); 31 | compile(fragmentShader); 32 | 33 | } 34 | else if(shaders.size() == 3){ 35 | 36 | vertexShader = glCreateShader(GL_VERTEX_SHADER); 37 | src = const_cast(_src[0].c_str()); 38 | size = _src[0].length(); 39 | glShaderSource(vertexShader, 1, &src, &size); 40 | compile(vertexShader); 41 | 42 | geometryShader = glCreateShader(GL_GEOMETRY_SHADER); 43 | src = const_cast(_src[1].c_str()); 44 | size = _src[1].length(); 45 | glShaderSource(geometryShader, 1, &src, &size); 46 | compile(geometryShader); 47 | 48 | fragmentShader = glCreateShader(GL_FRAGMENT_SHADER); 49 | src = const_cast(_src[2].c_str()); 50 | size = _src[2].length(); 51 | glShaderSource(fragmentShader, 1, &src, &size); 52 | compile(fragmentShader); 53 | 54 | } 55 | else std::cout<<"Number of Shaders not Recognized"< void texture(std::string name, const T& t); 82 | template void uniform(std::string name, const T u); 83 | template void uniform(std::string name, const T (&u)[N]); 84 | }; 85 | 86 | void Shader::setup(slist _s){ 87 | boost::filesystem::path data_dir(boost::filesystem::current_path()); 88 | std::vector s = _s; 89 | 90 | if(s.size() == 2){ 91 | vertexShader = addProgram((data_dir/s[0]).string(), GL_VERTEX_SHADER); 92 | fragmentShader = addProgram((data_dir/s[1]).string(), GL_FRAGMENT_SHADER); 93 | } 94 | else if(s.size() == 3){ 95 | vertexShader = addProgram((data_dir/s[0]).string(), GL_VERTEX_SHADER); 96 | geometryShader = addProgram((data_dir/s[1]).string(), GL_GEOMETRY_SHADER); 97 | fragmentShader = addProgram((data_dir/s[2]).string(), GL_FRAGMENT_SHADER); 98 | } 99 | else std::cout<<"Number of shaders not recognized."<(result.c_str()); 106 | 107 | int shaderID = glCreateShader(shaderType); 108 | glShaderSource(shaderID, 1, &src, &size); 109 | if(!compile(shaderID)){ 110 | std::cout<<"Failed to compile "< 170 | void Shader::uniform(std::string name, T u){ 171 | std::cout<<"Error: Data type not recognized for uniform "< 174 | void Shader::uniform(std::string name, const T (&u)[N]){ 175 | std::cout<<"Error: Data type not recognized for uniform "< void Shader::uniform(std::string name, const bool u){ 178 | glUniform1i(glGetUniformLocation(program, name.c_str()), u); } 179 | 180 | template<> void Shader::uniform(std::string name, const int u){ 181 | glUniform1i(glGetUniformLocation(program, name.c_str()), u); } 182 | 183 | template<> void Shader::uniform(std::string name, const float u){ 184 | glUniform1f(glGetUniformLocation(program, name.c_str()), u); } 185 | 186 | template<> void Shader::uniform(std::string name, const double u){ //GLSL Intrinsically Single Precision 187 | glUniform1f(glGetUniformLocation(program, name.c_str()), (float)u); } 188 | 189 | template<> void Shader::uniform(std::string name, const glm::vec2 u){ 190 | glUniform2fv(glGetUniformLocation(program, name.c_str()), 1, &u[0]); } 191 | 192 | template<> void Shader::uniform(std::string name, const glm::vec3 u){ 193 | glUniform3fv(glGetUniformLocation(program, name.c_str()), 1, &u[0]); } 194 | 195 | template<> void Shader::uniform(std::string name, const float (&u)[3]){ 196 | glUniform3fv(glGetUniformLocation(program, name.c_str()), 1, &u[0]); } 197 | 198 | template<> void Shader::uniform(std::string name, const float (&u)[4]){ 199 | glUniform4fv(glGetUniformLocation(program, name.c_str()), 1, &u[0]); } 200 | 201 | template<> void Shader::uniform(std::string name, const glm::vec4 u){ 202 | glUniform4fv(glGetUniformLocation(program, name.c_str()), 1, &u[0]); } 203 | 204 | template<> void Shader::uniform(std::string name, const glm::mat3 u){ 205 | glUniformMatrix3fv(glGetUniformLocation(program, name.c_str()), 1, GL_FALSE, &u[0][0]); } 206 | 207 | template<> void Shader::uniform(std::string name, const glm::mat4 u){ 208 | glUniformMatrix4fv(glGetUniformLocation(program, name.c_str()), 1, GL_FALSE, &u[0][0]); } 209 | 210 | template<> void Shader::uniform(std::string name, const std::vector u){ 211 | glUniformMatrix4fv(glGetUniformLocation(program, name.c_str()), u.size(), GL_FALSE, &u[0][0][0]); } 212 | 213 | template 214 | void Shader::texture(std::string name, const T& t){ 215 | glActiveTexture(GL_TEXTURE0 + boundtextures); 216 | glBindTexture(t.type, t.texture); 217 | uniform(name, boundtextures++); 218 | } 219 | -------------------------------------------------------------------------------- /program/utility/target.cpp: -------------------------------------------------------------------------------- 1 | class Target { 2 | public: 3 | Target(int W, int H, bool c = false, bool d = true){ 4 | WIDTH = W; HEIGHT = H; dAttach = d; cAttach = c; 5 | glGenFramebuffers(1, &fbo); 6 | } 7 | 8 | ~Target(){ glDeleteFramebuffers(1, &fbo); } //Default destructor 9 | 10 | unsigned int WIDTH, HEIGHT; 11 | bool dAttach = false, cAttach = true; //Whether we have a depth / color attachment 12 | GLuint fbo; //FBO (OpenGL: everything is an int) 13 | 14 | template 15 | void bind(T& t, bool d){ //Bind a texture to the FBO 16 | if(d) glFramebufferTexture(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, t.texture, 0); 17 | else glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, t.texture, 0); 18 | } 19 | 20 | template 21 | void setup(T& t, T& d){ //Add color and depth textures! 22 | glBindFramebuffer(GL_FRAMEBUFFER, fbo); 23 | 24 | if(dAttach) bind(d, true); 25 | if(cAttach) bind(t, false); 26 | else{ //If we don't have a color attachment, don't draw to it 27 | glDrawBuffer(GL_NONE); 28 | glReadBuffer(GL_NONE); 29 | } 30 | 31 | if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) 32 | std::cout<<"Framebuffer Incomplete"< void target(T a){ 44 | glClearColor(a[0], a[1], a[2], 1.0f); 45 | target(); 46 | } 47 | 48 | template //Raw Buffer Sampling 49 | void sample(T* m, glm::vec2 p, glm::vec2 d, GLenum ATTACH = GL_COLOR_BUFFER_BIT, GLenum FORMAT = GL_RGBA){ 50 | if(d.x <= 0 || d.y <= 0 || p.x+d.x > WIDTH || p.y+d.y > HEIGHT) return; 51 | glBindFramebuffer(GL_FRAMEBUFFER, fbo); glReadBuffer(ATTACH); 52 | glReadPixels(p.x, p.y, d.x, d.y, FORMAT, GL_UNSIGNED_BYTE, m); 53 | } 54 | }; 55 | 56 | class Billboard: public Target{ //Billboard specialization 57 | public: 58 | Texture texture, depth; //Two normal textures 59 | 60 | Billboard(int W, int H, bool c = true, bool d = true): 61 | Target(W, H, c, d){ 62 | if(dAttach) depth.depth(WIDTH, HEIGHT); 63 | if(cAttach) texture.empty(WIDTH, HEIGHT); 64 | setup(texture, depth); //Bind the two normal textures to the billboard 65 | } 66 | 67 | Billboard(SDL_Surface* s): //Render target preloaded with an image 68 | Billboard(s->w, s->h, true, false){ 69 | texture.raw(s); //Construct the texture from raw surface data 70 | bind(texture, false); //Bind it to the billboard as color texture 71 | } 72 | }; 73 | 74 | class Cubemap: public Target{ //Cubemap specialization 75 | public: 76 | Cubetexture texture, depth; //Two cubemap textures 77 | 78 | Cubemap(int W, int H, bool c = true, bool d = true):Target(W, H, c, d){ 79 | if(dAttach) depth.depth(WIDTH, HEIGHT); 80 | if(cAttach) texture.empty(WIDTH, HEIGHT); 81 | setup(texture, depth); 82 | } 83 | }; 84 | -------------------------------------------------------------------------------- /program/utility/texture.cpp: -------------------------------------------------------------------------------- 1 | class Texture; 2 | using tfunc = std::function; //Arbitrary function operating on texture pointer 3 | 4 | class Texture{ 5 | public: 6 | Texture(){ glGenTextures( 1, &texture ); }; //Default constructor 7 | Texture(SDL_Surface* s):Texture(){ raw(s); }; //Load raw surface data into texture 8 | Texture(int W, int H, bool d = false):Texture(){ //Create empty texture of defined size 9 | if(!d) empty(W, H); 10 | else depth(W, H); 11 | }; 12 | int w = 0, h = 0; 13 | 14 | ~Texture(){ glDeleteTextures(1, &texture); } 15 | 16 | static tfunc parameter; //Texture Parameter Setter! 17 | GLuint texture; //Texture int (OpenGL: everything is an int!) 18 | GLenum type = GL_TEXTURE_2D; //Texture type (default is this) 19 | 20 | void empty(int W, int H, tfunc param = parameter, GLenum F = GL_RGBA); 21 | void depth(int W, int H, tfunc param = parameter); 22 | void raw(SDL_Surface* s, tfunc param = parameter); 23 | 24 | }; 25 | 26 | class Cubetexture: public Texture{ //Cubetexture specialization. 27 | public: //Same thing, 6 times 28 | Cubetexture():Texture(){ 29 | type = GL_TEXTURE_CUBE_MAP; 30 | }; 31 | 32 | Cubetexture(int W, int H, bool d = false):Cubetexture(){ //Create empty texture of defined size 33 | if(!d) empty(W, H); 34 | else depth(W, H); 35 | }; 36 | 37 | void empty(int W, int H, tfunc param = parameter, GLenum F = GL_RGBA); 38 | void depth(int W, int H, tfunc param = parameter); 39 | }; 40 | void Texture::empty(int W, int H, tfunc param, GLenum F){ 41 | glBindTexture( type, texture ); 42 | glTexImage2D(type, 0, F, W, H, 0, F, GL_UNSIGNED_BYTE, NULL); 43 | (param)(this); //Call Parameter Setter 44 | } 45 | 46 | void Texture::depth(int W, int H, tfunc param){ 47 | empty(W, H, param, GL_DEPTH_COMPONENT); 48 | } 49 | 50 | void Texture::raw(SDL_Surface* s, tfunc param){ //Generate a texture from raw surface data 51 | glBindTexture( type, texture ); 52 | glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); 53 | glTexImage2D(type, 0, GL_RGBA, s->w, s->h, 0, GL_RGBA, GL_UNSIGNED_BYTE, s->pixels); 54 | (param)(this); //Call the parameter setting function! 55 | SDL_FreeSurface(s); 56 | w = s->w; h = s->h; 57 | } 58 | 59 | //Default parameter setting function! 60 | //Note that you can pass your own to the texture building functions above!z 61 | tfunc Texture::parameter = [](Texture* t){ 62 | glTexParameteri(t->type, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 63 | glTexParameteri(t->type, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 64 | glTexParameteri(t->type, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 65 | glTexParameteri(t->type, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 66 | glTexParameteri(t->type, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); 67 | }; 68 | 69 | void Cubetexture::empty(int W, int H, tfunc param, GLenum F){ 70 | glBindTexture(type, texture); 71 | for(unsigned int i = 0; i < 6; i++) 72 | glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, F, W, H, 0, F, GL_UNSIGNED_BYTE, NULL); 73 | (param)(this); 74 | } 75 | 76 | void Cubetexture::depth(int W, int H, tfunc param){ 77 | empty(W, H, param, GL_DEPTH_COMPONENT); 78 | }; 79 | -------------------------------------------------------------------------------- /screenshots/fidget.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weigert/splash/8ba1b31bbebdd7ab4da052c4a3470e2bb94a5754/screenshots/fidget.gif -------------------------------------------------------------------------------- /screenshots/smbsplash.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weigert/splash/8ba1b31bbebdd7ab4da052c4a3470e2bb94a5754/screenshots/smbsplash.gif -------------------------------------------------------------------------------- /screenshots/ttf.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weigert/splash/8ba1b31bbebdd7ab4da052c4a3470e2bb94a5754/screenshots/ttf.gif -------------------------------------------------------------------------------- /setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Get the user dir for the user that is executing the script 4 | DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" 5 | HOME=/home/$(echo $DIR | cut -d/ -f3) 6 | 7 | # Setup script for splash 8 | printf "Setting up splash...\n" 9 | 10 | printf "Setting up install directory ... " 11 | if [ -d "$HOME/.config/splash" ]; then 12 | if ! [ -d "$HOME/.config/splash/exec" ]; then 13 | mkdir "$HOME/.config/splash/exec" 14 | fi 15 | if ! [ -d "$HOME/.config/splash/pipe" ]; then 16 | mkdir "$HOME/.config/splash/pipe" 17 | fi 18 | else 19 | mkdir "$HOME/.config/splash" 20 | mkdir "$HOME/.config/splash/exec" 21 | mkdir "$HOME/.config/splash/pipe" 22 | fi 23 | printf "done!\n" 24 | printf "Install location: $HOME/.config/splash\n" 25 | 26 | # Check for Existence of Dependencies 27 | printf "Setting up configuration file\n" 28 | cp config ~/.config/splash/config 29 | 30 | printf "Compile splash? [Y / N] " 31 | read b 32 | if [ $b = "Y" ];then 33 | echo "Compiling splash..." 34 | make -C "splash" splash 35 | fi 36 | 37 | printf "Compile example programs? [Y / N] " 38 | read b 39 | if [ $b == "Y" ]; then 40 | echo "Compiling programs..." 41 | make -C "program" all 42 | fi 43 | 44 | printf "splash: Success" 45 | -------------------------------------------------------------------------------- /splash/Makefile: -------------------------------------------------------------------------------- 1 | #================================ 2 | # Splash Makefile 3 | #================================ 4 | 5 | # Compiler Configuration 6 | CC = g++ -std=c++17 7 | CFLAGS = -Wfatal-errors -O 8 | LFLAGS = -I/usr/local/include -L/usr/local/lib 9 | LINK = -ldl -lX11 -lXfixes -lXrender -lGL -lGLEW -lboost_system -lboost_filesystem 10 | 11 | build: 12 | @if [ ! -d "./tmp" ]; then mkdir tmp; fi; 13 | @echo "compiling splash in ./tmp" 14 | @$(CC) splash.cpp $(CFLAGS) $(LFLAGS) $(LINK) -o tmp/splash; 15 | @echo "done" 16 | 17 | install: 18 | @mv tmp/splash /usr/local/bin/splash 19 | @echo "moved to /usr/local/bin/splash" 20 | @echo "test with: splash fidget" 21 | @rmdir tmp 22 | 23 | .PHONY: splash 24 | splash: build install 25 | -------------------------------------------------------------------------------- /splash/helper/logger.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | namespace logger{ 4 | bool verbose = false; 5 | bool debug = true; 6 | 7 | template 8 | void raw(T m){ 9 | std::cout< 13 | void raw(T m, Types... r){ 14 | std::cout< 19 | bool err(T m){ 20 | if(!debug) return false; 21 | std::cout<<"Error: "; 22 | raw(m); 23 | return false; 24 | } 25 | 26 | template 27 | bool err(T m, Types... r){ 28 | if(!debug) return false; 29 | std::cout<<"Error: "; 30 | raw(m, r...); 31 | return false; 32 | } 33 | 34 | template 35 | void fatal(T m){ 36 | std::cout<<"Fatal Error: "; 37 | raw(m); 38 | exit(0); 39 | } 40 | 41 | template 42 | void fatal(T m, Types... r){ 43 | std::cout<<"Fatal Error: "; 44 | raw(m, r...); 45 | exit(0); 46 | } 47 | 48 | template 49 | void write(T m){ 50 | if(!verbose) return; 51 | std::cout<<"Splash: "; 52 | raw(m); 53 | } 54 | 55 | template 56 | void write(T m, Types... r){ 57 | if(!verbose) return; 58 | std::cout<<"Splash: "; 59 | raw(m, r...); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /splash/helper/parse.h: -------------------------------------------------------------------------------- 1 | namespace parse{ 2 | using namespace std; 3 | 4 | //Parse Data Struct 5 | struct data{ 6 | 7 | unordered_map flags; 8 | unordered_map pflags; 9 | unordered_map params; 10 | 11 | int x = 0, y = 0, w = 1920, h = 1080; 12 | 13 | bool program = false; 14 | std::string prog; 15 | 16 | } in; 17 | 18 | 19 | void input(int n, int ac, char* as[]){ 20 | if(n == ac) return; 21 | 22 | string arg = string(as[n]); 23 | 24 | //Flag Argument 25 | if(arg.substr(0, 2) == "--" && arg.length() > 2) 26 | in.flags[arg] = true; 27 | 28 | //Parameter Argument 29 | else if(arg.substr(0, 1) == "-" && arg.length() > 1){ 30 | int k = n + 1; 31 | if(k == ac) //out of bounds 32 | return; 33 | 34 | string p = ""; 35 | for(int b = 0; k + b < ac; b++){ 36 | in.pflags[arg] = true; 37 | p = string(as[k+b]); 38 | if(p.substr(0, 1) == "-") 39 | break; 40 | in.params[arg+to_string(b)] = p; 41 | n = k + b; 42 | } 43 | } 44 | 45 | input(++n, ac, as); 46 | } 47 | 48 | //Master Fetch 49 | void input(int ac, char* as[]){ 50 | if(ac < 2) return; 51 | in.prog = std::string(as[1]); 52 | in.program = true; 53 | 54 | input(2, ac, as); 55 | 56 | //Do some necessary parsing 57 | if(in.pflags["-p"]) 58 | in.x = stoi(in.params["-p0"]); 59 | if(in.pflags["-p"]) 60 | in.y = stoi(in.params["-p1"]); 61 | if(in.pflags["-p"]) 62 | in.w = stoi(in.params["-p2"]); 63 | if(in.pflags["-p"]) 64 | in.h = stoi(in.params["-p3"]); 65 | 66 | logger::verbose = in.flags["--v"]; 67 | } 68 | 69 | }; 70 | -------------------------------------------------------------------------------- /splash/helper/pipe.h: -------------------------------------------------------------------------------- 1 | namespace spipe { 2 | using namespace std; 3 | 4 | bool available(){ 5 | if(!isatty(fileno(stdin))) 6 | return true; 7 | logger::write("No piped data"); 8 | return false; 9 | } 10 | 11 | bool read(vector* lines){ 12 | string line; 13 | lines->clear(); 14 | while (getline(cin, line)){ 15 | lines->push_back(line); 16 | logger::write("Piped Input: ", line); 17 | } 18 | cin.clear(); 19 | return lines->empty(); 20 | } 21 | 22 | std::string next(){ 23 | int n = 0; 24 | 25 | const char * home = getenv ("HOME"); 26 | fs::path fp = fs::path(home); 27 | while(fs::exists(root/"pipe"/("pipe"+std::to_string(n)))) 28 | n++; 29 | 30 | return ("pipe"+std::to_string(n)); 31 | } 32 | 33 | std::string fifo(int fpipe){ 34 | 35 | std::string line; 36 | char buf[256] = {""}; 37 | 38 | int m = 0; 39 | int n = ::read(fpipe, buf, sizeof(buf)); 40 | while(n > 0){ 41 | line += buf; 42 | m += n; 43 | n = ::read(fpipe, buf, sizeof(buf)); 44 | } 45 | 46 | return line.substr(0, m-1); 47 | 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /splash/include/event.cpp: -------------------------------------------------------------------------------- 1 | namespace event { 2 | 3 | struct eventdata{ 4 | bool clicked = false; 5 | bool clicktrigger = false; 6 | int mx, my = 0; 7 | int rx, ry = 0; 8 | } data; 9 | 10 | //Input Events 11 | bool input(Splash& s){ 12 | 13 | XEvent event; 14 | XConfigureEvent *xc; 15 | 16 | while (XPending(s.Xdisplay)){ 17 | XNextEvent(s.Xdisplay, &event); 18 | switch (event.type){ 19 | case ButtonPress: 20 | data.clicktrigger = true; 21 | data.clicked = true; 22 | data.mx = event.xbutton.x; 23 | data.my = event.xbutton.y; 24 | data.rx = 0; 25 | data.ry = 0; 26 | break; 27 | case ButtonRelease: 28 | data.clicked = false; 29 | data.mx = event.xbutton.x; 30 | data.my = event.xbutton.y; 31 | data.rx = 0; 32 | data.ry = 0; 33 | break; 34 | case MotionNotify: 35 | data.rx = event.xmotion.x - data.mx; 36 | data.ry = event.xmotion.y - data.my; 37 | data.mx = event.xmotion.x; 38 | data.my = event.xmotion.y; 39 | break; 40 | case ConfigureNotify: 41 | xc = &(event.xconfigure); 42 | s.w = xc->width; 43 | s.h = xc->height; 44 | break; 45 | } 46 | } 47 | 48 | return true; 49 | } 50 | 51 | void handle(){ 52 | //Reset Triggers 53 | if(data.clicktrigger) data.clicktrigger = false; 54 | } 55 | 56 | } //End of Namespace 57 | -------------------------------------------------------------------------------- /splash/include/program.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | using svec = std::vector; 9 | 10 | #include 11 | 12 | #ifndef SPLASH_PROGRAM 13 | #define SPLASH_PROGRAM 14 | 15 | class Program { 16 | public: 17 | 18 | Program(svec* s){ 19 | 20 | data = s; 21 | 22 | ppipe = (root/"pipe"/spipe::next()).string(); 23 | mkfifo(ppipe.c_str(), 0666); 24 | 25 | fpipe = open(ppipe.c_str(), O_RDONLY | O_NONBLOCK); 26 | if(fpipe < 0) logger::fatal("Failed to Open Pipe"); 27 | logger::raw(ppipe); 28 | 29 | } 30 | 31 | virtual ~Program(){ 32 | 33 | delete data; 34 | close(fpipe); 35 | if(remove(ppipe.c_str()) != 0) 36 | logger::raw("Failed to delete pipe"); 37 | 38 | } 39 | 40 | bool alive = true; 41 | 42 | svec* data; 43 | std::string ppipe; 44 | int fpipe; //Pipe File Handle 45 | 46 | virtual void pipeline() = 0; //Rendering Pipeline 47 | virtual void event(event::eventdata&) = 0; //Event Handler 48 | virtual void onpipe(std::string) = 0; //Piped Data Handler 49 | 50 | static void* hndl; 51 | static Program* get(std::string, svec* s, parse::data* d); 52 | static void destroy(Program* p); 53 | 54 | }; 55 | 56 | // Constructor and Destructor Factories 57 | 58 | void* Program::hndl; 59 | typedef Program* (*create_t)(svec* s, parse::data* d); 60 | typedef void destroy_t(Program*); 61 | 62 | Program* Program::get(std::string f, svec* s, parse::data* d){ 63 | 64 | hndl = dlopen(f.c_str(), RTLD_NOW); 65 | if(hndl == NULL){ 66 | std::cout< 2 | 3 | #include 4 | #include "glm/gtc/matrix_transform.hpp" 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | using namespace std; 17 | 18 | typedef GLXContext (*glXCreateContextAttribsARBProc)(Display*, GLXFBConfig, GLXContext, Bool, const int*); 19 | 20 | namespace event { 21 | bool active = true; 22 | } 23 | 24 | //Supported Desktop Environments 25 | enum DE{ 26 | NADE, //Not-A-Desktop-Environment (unrecognized) 27 | OBX, //Openbox 28 | XFCE, //XFCE 29 | GNOME, //Gnome 30 | I3 //I3 31 | }; 32 | 33 | class Splash{ 34 | public: 35 | 36 | Splash(){}; 37 | 38 | Splash(parse::data in): 39 | x{in.x}, y{in.y}, w{in.w}, h{in.h}, 40 | interact{!in.flags["--ni"]}, background{!in.flags["--fg"]}, 41 | all{in.flags["--a"]} 42 | { setup(); } 43 | 44 | void setup(){ 45 | makeWindow("SPLASH"); 46 | makeContext(); 47 | 48 | glewExperimental = GL_TRUE; //Launch GLEW 49 | glewInit(); 50 | 51 | //Options 52 | glLineWidth(2.0); 53 | glEnable(GL_BLEND); 54 | glEnable(GL_DEPTH_TEST); 55 | glDepthFunc(GL_LESS); 56 | glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 57 | glEnable(GL_MULTISAMPLE); 58 | glViewport(0, 0, parse::in.w, parse::in.h); 59 | glClearColor(0.0f, 0.0f, 0.0f, 0.0f); 60 | } 61 | 62 | Display *Xdisplay; 63 | Window Xroot, Xwindow; 64 | 65 | GLXWindow gWindow; 66 | GLXContext gContext; 67 | GLXFBConfig fbconfig; 68 | 69 | //Positional Data (default) 70 | int x = 100, y = 100; 71 | int w = 50, h = 50; 72 | 73 | //Properties 74 | bool interact = true; 75 | bool background = false; 76 | bool all = false; 77 | 78 | void makeWindow(string title); 79 | void makeContext(); 80 | 81 | void desetup(); //Setup specific for the DE 82 | string getDE(){ 83 | if(const char* env_p = std::getenv("DESKTOP_SESSION")) 84 | return std::string(env_p); 85 | else return "NADE"; 86 | } 87 | 88 | bool property(string type, string prop, int set); 89 | bool property(string type, unsigned long prop, int set); 90 | 91 | ~Splash(){ 92 | XDestroyWindow(Xdisplay, Xwindow); 93 | XCloseDisplay(Xdisplay); 94 | }; 95 | }; 96 | 97 | void Splash::desetup(){ 98 | string de = getDE(); 99 | logger::write("DE:", de); 100 | 101 | Atom a = XInternAtom(Xdisplay, "_NET_WM_WINDOW_TYPE", 0); 102 | Atom b = XInternAtom(Xdisplay, "_NET_WM_WINDOW_TYPE_SPLASH", 0); 103 | XChangeProperty(Xdisplay, Xwindow, a, XA_ATOM, 32, 104 | PropModeReplace, (unsigned char*)&b, 1); 105 | 106 | //Disable Compositor Shading 107 | /* example; https://www.systutorials.com/docs/linux/man/1-compton/ */ 108 | a = XInternAtom(Xdisplay, "SPLASH_SHADOW", 0); 109 | unsigned long i = (!parse::in.flags["--ns"])?1:0; 110 | XChangeProperty(Xdisplay, Xwindow, a, XA_CARDINAL, 32, 111 | PropModeReplace, (unsigned char*)&i, 1); 112 | 113 | property("_NET_WM_STATE", "_NET_WM_STATE_SKIP_TASKBAR", 1); //Does not appear in Alt+Tab 114 | property("_NET_WM_STATE", "_NET_WM_STATE_SKIP_PAGER", 1); 115 | 116 | //Set the Class Hint 117 | XClassHint *class_hints = XAllocClassHint(); 118 | string _name = "SPLASH"; 119 | string _class = "SPLASH"; 120 | class_hints->res_name = (char*)_name.c_str(); 121 | class_hints->res_class = (char*)_class.c_str(); 122 | XSetClassHint(Xdisplay, Xwindow, class_hints); 123 | XFree(class_hints); 124 | } 125 | 126 | /* 127 | These two can be unified... 128 | just add the compton flag anyway. 129 | */ 130 | 131 | 132 | bool Splash::property(string type, unsigned long prop, int set){ 133 | Atom _type = XInternAtom(Xdisplay, type.c_str(), 1); 134 | if(_type == None) return false; 135 | int attempt = XChangeProperty(Xdisplay, Xwindow, _type, XA_CARDINAL, 32, 136 | PropModeReplace, (unsigned char *)&prop, 1); 137 | return (attempt != 0); 138 | } 139 | 140 | static Bool WaitForMapNotify(Display *d, XEvent *e, char *arg){ 141 | return d && e && arg && (e->type == MapNotify) && (e->xmap.window == *(Window*)arg); 142 | } 143 | 144 | bool Splash::property(string _type, string _prop, int set){ 145 | Atom type = XInternAtom( Xdisplay, _type.c_str(), 1 ); 146 | Atom prop = XInternAtom( Xdisplay, _prop.c_str(), 1 ); 147 | if(type == None) 148 | return logger::err("Failed to find atom", _type); 149 | if(prop == None) 150 | return logger::err("Failed to find atom", _prop); 151 | 152 | XClientMessageEvent xclient; 153 | memset( &xclient, 0, sizeof (xclient) ); 154 | xclient.type = ClientMessage; 155 | xclient.window = Xwindow; 156 | xclient.message_type = type; 157 | xclient.format = 32; 158 | xclient.data.l[0] = set; 159 | xclient.data.l[1] = prop; 160 | xclient.data.l[2] = 0; 161 | xclient.data.l[3] = 0; 162 | xclient.data.l[4] = 0; 163 | XSendEvent( Xdisplay, Xroot, False, SubstructureRedirectMask | SubstructureNotifyMask, 164 | (XEvent *)&xclient ); 165 | 166 | return true; 167 | } 168 | 169 | void Splash::makeWindow(string t){ 170 | 171 | Xdisplay = XOpenDisplay(NULL); 172 | if (!Xdisplay) 173 | logger::fatal("Couldn't connect to X server"); 174 | 175 | int Xscreen = DefaultScreen(Xdisplay); 176 | Xroot = RootWindow(Xdisplay, Xscreen); 177 | 178 | const int vdata[] = { 179 | GLX_RENDER_TYPE, GLX_RGBA_BIT, GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT, GLX_DOUBLEBUFFER, True, 180 | GLX_RED_SIZE, 8, GLX_GREEN_SIZE, 8, GLX_BLUE_SIZE, 8, GLX_ALPHA_SIZE, 8, GLX_DEPTH_SIZE, 16, 181 | None 182 | }; 183 | fbconfig = 0; 184 | int nfbcfgs; 185 | GLXFBConfig *fbconfigs = glXChooseFBConfig(Xdisplay, Xscreen, vdata, &nfbcfgs); 186 | XVisualInfo* visual; 187 | XRenderPictFormat * pformat; 188 | for(int i = 0; i < nfbcfgs; i++) { 189 | visual = (XVisualInfo*) glXGetVisualFromFBConfig(Xdisplay, fbconfigs[i]); 190 | if(!visual) 191 | continue; 192 | 193 | pformat = XRenderFindVisualFormat(Xdisplay, visual->visual); 194 | if(!pformat) 195 | continue; 196 | 197 | fbconfig = fbconfigs[i]; 198 | if(pformat->direct.alphaMask > 0) { 199 | break; 200 | } 201 | } 202 | 203 | if(!fbconfig) logger::fatal("No matching FB config found"); 204 | 205 | XSetWindowAttributes attr = {0,}; 206 | attr.colormap = XCreateColormap(Xdisplay, Xroot, visual->visual, AllocNone); 207 | attr.background_pixmap = None; 208 | attr.border_pixmap = None; 209 | attr.border_pixel = 0; 210 | attr.event_mask = 211 | StructureNotifyMask | 212 | EnterWindowMask | 213 | LeaveWindowMask | 214 | ExposureMask | 215 | ButtonPressMask | 216 | ButtonReleaseMask | 217 | OwnerGrabButtonMask | 218 | ButtonMotionMask | 219 | KeyPressMask | 220 | KeyReleaseMask; 221 | 222 | int attr_mask = CWBackPixmap | CWColormap | CWBorderPixel | CWEventMask | CWCursor; 223 | Xwindow = XCreateWindow(Xdisplay, Xroot, 224 | x, y, w, h, 0, 225 | visual->depth, InputOutput, visual->visual, 226 | attr_mask, &attr); 227 | if( !Xwindow ) 228 | logger::fatal("Couldn't create window"); 229 | gWindow = Xwindow; 230 | 231 | XTextProperty textprop; 232 | textprop.value = (unsigned char*)t.c_str(); 233 | textprop.encoding = XA_STRING; 234 | textprop.format = 8; 235 | textprop.nitems = t.length(); 236 | 237 | XSizeHints hints; 238 | hints.x = x; //Required for i3! 239 | hints.y = y; 240 | hints.width = w; 241 | hints.height = h; 242 | hints.flags = USPosition | USSize; 243 | 244 | XWMHints *startup_state = XAllocWMHints(); 245 | startup_state->initial_state = NormalState; 246 | startup_state->flags = StateHint; 247 | XSetWMProperties(Xdisplay, Xwindow, &textprop, &textprop, 248 | NULL, 0, &hints, startup_state, NULL); 249 | XFree(startup_state); 250 | 251 | desetup(); 252 | 253 | if(all){ 254 | Atom a = XInternAtom(Xdisplay, "_NET_WM_STATE", 0); 255 | Atom b = XInternAtom(Xdisplay, "_NET_WM_STATE_STICKY", 0); 256 | XChangeProperty(Xdisplay, Xwindow, a, XA_ATOM, 32, 257 | PropModeReplace, (unsigned char*)&b, 1); 258 | 259 | //Openbox wants this 260 | if(getDE() == "openbox") property("_NET_WM_DESKTOP", 0xFFFFFFFF, 1); 261 | } 262 | 263 | XMapWindow(Xdisplay, Xwindow); 264 | 265 | //After Mapping 266 | if(!background) property("_NET_WM_STATE", "_NET_WM_STATE_ABOVE", 1); 267 | else property("_NET_WM_STATE", "_NET_WM_STATE_BELOW", 1); 268 | 269 | //Interaction removing needs to happen after mapping 270 | if(!interact){ 271 | XRectangle rect; 272 | XserverRegion region = XFixesCreateRegion(Xdisplay, &rect, 1); 273 | XFixesSetWindowShapeRegion(Xdisplay, Xwindow, ShapeInput, 0, 0, region); 274 | XFixesDestroyRegion(Xdisplay, region); 275 | } 276 | } 277 | 278 | void Splash::makeContext(){ 279 | int dummy; 280 | if (!glXQueryExtension(Xdisplay, &dummy, &dummy)) 281 | logger::fatal("X server does not support OpenGL"); 282 | 283 | /* //Non-Core Profile 284 | int context_attribs[] = { 285 | GLX_CONTEXT_MAJOR_VERSION_ARB, 3, 286 | GLX_CONTEXT_MINOR_VERSION_ARB, 0, 287 | None //Always Terminate with None 288 | }; 289 | 290 | //Old Context Creation Style 291 | gContext = glXCreateNewContext(Xdisplay, fbconfig, GLX_RGBA_TYPE, 0, True); 292 | */ 293 | 294 | //Core Profile Context 295 | int context_attribs[] = { 296 | GLX_CONTEXT_MAJOR_VERSION_ARB, 4, 297 | GLX_CONTEXT_MINOR_VERSION_ARB, 0, 298 | None //Always Terminate with None! 299 | //https://www.khronos.org/registry/OpenGL/extensions/ARB/GLX_ARB_create_context.txt 300 | }; 301 | 302 | glXCreateContextAttribsARBProc glXCreateContextAttribsARB = 0; 303 | glXCreateContextAttribsARB = (glXCreateContextAttribsARBProc) 304 | glXGetProcAddressARB( (const GLubyte *) "glXCreateContextAttribsARB" ); 305 | gContext = glXCreateContextAttribsARB( Xdisplay, fbconfig, 0, 306 | True, context_attribs ); 307 | 308 | if (!gContext) 309 | logger::fatal("Failed to create OpenGL context"); 310 | 311 | if (!glXMakeContextCurrent(Xdisplay, gWindow, gWindow, gContext)) 312 | logger::fatal("Couldn't make context current"); 313 | } 314 | -------------------------------------------------------------------------------- /splash/resource/help.txt: -------------------------------------------------------------------------------- 1 | R""( 2 | Splash requires specification of an execution mode. 3 | 4 | Reading: 5 | [] Required 6 | <> Optional 7 | 8 | splash [mode] 9 | 10 | Modes (built-in): 11 | 12 | help Display help message 13 | 14 | fidget Display a 3D fidget spinner (lol) 15 | : None 16 | 17 | img Display .png image 18 | [data]: Image file name 19 | 20 | gif Display .gif image 21 | [data]: GIF file name 22 | Note: Only works for full replacing gifs! 23 | 24 | text Render truetype text (static) 25 | [data]: Text to render 26 | 27 | Options: 28 | 29 | -p [x y w h] Position Splash Quad 30 | -t [s] Splash timeout in seconds (integer) 31 | 32 | Flags: 33 | 34 | --t Test configuration file 35 | --v Verbose mode 36 | 37 | --bg Place splash in background 38 | --fg Place splash in foreground (default) 39 | --ni Splash no-interact 40 | --ns Disable splash shadows (compton) 41 | --a Display splash on all desktops 42 | 43 | Program specific flags: 44 | 45 | text 46 | 47 | -ff [font] Font face (searches in ~/.fonts/, default "arial.ttf") 48 | -fc [hex] Font color (hex code, e.g. 0xFFFFFFFF, 0xFFFFFF, FFFFFF) 49 | -bc [hex] Background color (hex code, e.g. 0xFFFFFFFF, 0xFFFFFF, FFFFFF) 50 | -fs [size] Font size, positive integer 51 | -v [0,1,2] Vertical Align [center, up, down] 52 | -h [0,1,2] Horizontal Align [center, left, right] 53 | 54 | )"" 55 | -------------------------------------------------------------------------------- /splash/resource/info.txt: -------------------------------------------------------------------------------- 1 | R""(splash 2021 by Nicholas McDonald)"" 2 | -------------------------------------------------------------------------------- /splash/splash.cpp: -------------------------------------------------------------------------------- 1 | #include "splash.h" 2 | 3 | int main( int argc, char* args[] ) { 4 | 5 | parse::input(argc, args); 6 | if(!parse::in.program){ 7 | logger::err("No program"); 8 | return 0; 9 | } 10 | 11 | if(parse::in.prog == "help"){ 12 | logger::raw(help); 13 | return 0; 14 | } 15 | 16 | if(parse::in.prog == "info"){ 17 | logger::raw(info); 18 | return 0; 19 | } 20 | 21 | vector* lines = new vector; 22 | if(spipe::available()) 23 | spipe::read(lines); 24 | 25 | Splash splash(parse::in); 26 | 27 | const char * home = getenv ("HOME"); 28 | if(home == NULL) 29 | logger::fatal("Home environment variable not set"); 30 | fs::path fp = fs::path(home); 31 | 32 | Program* p = Program::get((fp/".config/splash/exec"/parse::in.prog).string(), lines, &parse::in); 33 | if(p == NULL) 34 | logger::fatal("Couldn't load program", parse::in.prog); 35 | 36 | auto start = std::chrono::high_resolution_clock::now(); 37 | 38 | signal(SIGINT, &sighandler); 39 | 40 | while(event::active && p->alive){ 41 | 42 | event::input(splash); 43 | 44 | if(parse::in.pflags["-t"]){ 45 | auto cur = std::chrono::high_resolution_clock::now(); 46 | int s = std::chrono::duration_cast(cur - start).count(); 47 | if(s > stoi(parse::in.params["-t0"])){ 48 | logger::write("Killing splash"); 49 | event::active = false; 50 | } 51 | } 52 | 53 | p->pipeline(); //Execute the Program 54 | p->event(event::data); //Program Event Handling 55 | 56 | std::string line = spipe::fifo(p->fpipe); 57 | if(!line.empty()) p->onpipe(line); 58 | 59 | event::handle(); 60 | 61 | glXSwapBuffers(splash.Xdisplay, splash.gWindow); 62 | } 63 | 64 | Program::destroy(p); 65 | return 0; 66 | } 67 | -------------------------------------------------------------------------------- /splash/splash.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | namespace fs = boost::filesystem; 10 | 11 | const fs::path home = fs::path(getenv("HOME")); 12 | const fs::path root = home/".config/splash"; 13 | 14 | #include "helper/logger.h" 15 | #include "helper/parse.h" 16 | #include "helper/pipe.h" 17 | 18 | #include "include/window.cpp" 19 | #include "include/event.cpp" 20 | #include "include/program.cpp" 21 | 22 | void sighandler(int signal){ 23 | event::active = false; 24 | } 25 | 26 | const std::string info = 27 | #include "resource/info.txt" 28 | ; 29 | 30 | const std::string help = 31 | #include "resource/help.txt" 32 | ; 33 | -------------------------------------------------------------------------------- /test/README.md: -------------------------------------------------------------------------------- 1 | # splash testdata 2 | 3 | these are a number of files for testing default behavior. 4 | 5 | to run all test, simply execute 6 | 7 | ./test.sh 8 | 9 | from this directory `/splash/test`, or copy out the command you want to run. 10 | 11 | ## examples 12 | 13 | ### piped text display / clock 14 | 15 | launch a text splash with the appropriate flags 16 | 17 | splash text --a --ns --ni -fs 300 --fg -ff "Roboto-Thin.ttf" -fc 0xff0000 18 | 19 | this wlil then return the location of the pipe (`~/.config/splash/pipe/pipe0`, counting up). anything piped into the pipe will be rendered as text. to display a clock using splash, you could run e.g. 20 | 21 | watch -n1 'date +'%H:%M:%S' > ~/.config/splash/pipe/pipe0' 22 | 23 | make sure the pipe name is correct and that the font exists in `~/.fonts` 24 | -------------------------------------------------------------------------------- /test/canyon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weigert/splash/8ba1b31bbebdd7ab4da052c4a3470e2bb94a5754/test/canyon.png -------------------------------------------------------------------------------- /test/font.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weigert/splash/8ba1b31bbebdd7ab4da052c4a3470e2bb94a5754/test/font.ttf -------------------------------------------------------------------------------- /test/smb.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weigert/splash/8ba1b31bbebdd7ab4da052c4a3470e2bb94a5754/test/smb.gif -------------------------------------------------------------------------------- /test/test.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | #Splash Example Commands 3 | 4 | #Display Raw Gif in Background 5 | echo "smb.gif" | splash gif -p 660 340 600 400 --bg --ni --a --ns 6 | 7 | #Test the Fidget Spinner (doesn't take data) 8 | splash fidget -p 710 290 500 500 --ns --fg 9 | splash hairy -p 710 290 500 500 --ns --fg 10 | splash spiky -p 710 290 500 500 --ns --fg 11 | 12 | #Test displaying an image! 13 | echo "canyon.png" | splash img -p 360 140 1200 800 --fg 14 | 15 | #Test displaying some text 16 | echo "dude i just literally wrote on your desktop background" | splash text --a --ns --ni --fg -fs 32 -ff "Roboto-Bold.ttf" -fc 0xFF0000 17 | 18 | #Piped Clock (Run both of these in different consoles!) 19 | 20 | # splash text --a --ns --ni -fs 300 --fg -ff "Roboto-Thin.ttf" -fc 0xff0000 21 | # watch -n1 'date +'%H:%M:%S' > ~/.config/splash/pipe/pipe0' 22 | --------------------------------------------------------------------------------