├── ProcessingTextmodeEngine.pde
├── README.md
├── UNLICENSE.txt
├── data
├── Px437_IBM_BIOS-16.vlw
└── face.png
└── renderTextMode.pde
/ProcessingTextmodeEngine.pde:
--------------------------------------------------------------------------------
1 | // Processing Textmode Engine
2 | // Version 0.1 (initial release)
3 | // by Don Miller / NO CARRIER
4 | // http://www.no-carrier.com
5 | // Have fun! :)
6 |
7 | PFont font; // textmode font
8 | PGraphics b; // screen buffer
9 | PImage img; // used for face.png
10 |
11 | int segSize; // segment size, see renderTextMode tab for (lots) more detail
12 | int display = 1; // choose which demo to display, starts with face image
13 | float tick; // counter, used for sin calcuations for shape rotation
14 |
15 | boolean doDraw = true; // draw or pause
16 | boolean blockMode = false; // block mode or ASCII mode
17 | boolean fps = false; // show FPS or not
18 | boolean textMode = true; // show buffer (stretched to fit screen) or textmode
19 |
20 | void setup() {
21 | size(1024, 768, P2D); // need to use P2D as renderer, as we use P3D for buffer
22 | img = loadImage("face.png");
23 | noSmooth(); // keep it blocky :)
24 | noCursor(); // don't need this!
25 | initTextmode(); // set up buffer, load font for textmode output
26 | }
27 |
28 | void draw() {
29 | if (doDraw) {
30 | tick = tick + 0.01;
31 |
32 | helloWorld(); // let's show off the textmode engine, shall we?
33 |
34 | // render options
35 | if (textMode) { // choose how to render:
36 | background(0);
37 | renderTextMode(); // display textmode...
38 | } else {
39 | background(0);
40 | image(b, 0, 0, width, height); // ...or display buffer (stretched to fit screen)
41 | }
42 | if (fps) {
43 | fill(0);
44 | rect(0, 0, 32, 16);
45 | fill(255);
46 | text(int(frameRate), 0, 0);
47 | }
48 | }
49 | }
50 |
51 | void helloWorld() {
52 | b.beginDraw();
53 | b.background(0);
54 | switch(display) {
55 | case 1:
56 | drawImage();
57 | break;
58 | case 2:
59 | drawBox();
60 | break;
61 | case 3:
62 | drawBoxFilled();
63 | break;
64 | case 4:
65 | drawSphere();
66 | break;
67 | }
68 | b.endDraw();
69 | }
70 |
71 | void drawImage() // draws and rotates a face image
72 | {
73 | b.translate(b.width/2, b.height/2);
74 | b.rotateY((tick*100)*TWO_PI/360);
75 | b.translate(-img.width/2, -img.height/2);
76 | b.image(img, 0, 0);
77 | }
78 |
79 | void drawBox() { // draws a wireframe cube with simple lighting
80 | b.lights();
81 | b.pushMatrix();
82 | b.translate(b.width/2, b.height/2, 0);
83 | b.rotateY(tick);
84 | b.rotateX(-tick);
85 | b.noFill();
86 | b.strokeWeight(6);
87 | b.stroke(255, 255, 0);
88 | b.box(100);
89 | b.popMatrix();
90 | }
91 |
92 | void drawBoxFilled() { // draws a filled cube with a spotlight
93 | b.spotLight(0, 255, 0, b.width/2, b.height/2, 400, 0, 0, -1, PI/4, 2);
94 | b.pushMatrix();
95 | b.translate(b.width/2, b.height/2, 0);
96 | b.rotateY(tick);
97 | b.rotateX(-tick);
98 | b.fill(255);
99 | b.strokeWeight(6);
100 | b.stroke(255, 255, 0);
101 | b.box(100);
102 | b.popMatrix();
103 | }
104 |
105 | void drawSphere() { // draws a filled sphere with a spotlight
106 | b.spotLight(255, 0, 0, b.width/2, b.height/2, 400, 0, 0, -1, PI/4, 2);
107 | b.pushMatrix();
108 | b.translate(b.width/2, b.height/2, 0);
109 | b.rotateY(-tick);
110 | b.rotateX(+tick);
111 | b.strokeWeight(4);
112 | b.stroke(255, 165, 0);
113 | b.fill(255);
114 | b.sphereDetail(10);
115 | b.sphere(80);
116 | b.popMatrix();
117 | }
118 |
119 | void keyPressed() {
120 | if (key == 'q') {
121 | fps = !fps;
122 | }
123 | if (key == 'a') {
124 | blockMode = !blockMode;
125 | }
126 | if (key == 'z') {
127 | textMode = !textMode;
128 | }
129 | if (key == 'p') {
130 | doDraw = !doDraw;
131 | }
132 | if (key == ' ') { // this is the spacebar ;)
133 | display++;
134 | if (display > 4) {
135 | display = 1;
136 | }
137 | }
138 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Processing Textmode Engine
2 | ## Version 0.1 (initial release) May 2016
3 |
4 | The Processing Textmode Engine renders output in [Processing](http://www.processing.org) to a full color text display,
5 | reminiscent of early computer or [BBS](https://en.wikipedia.org/wiki/Bulletin_board_system) graphics. I wrote it to use for realtime visual performance, but can be used to create movies, still images, or even games.
6 | It is free and open source. :v:
7 |
8 | Creating a textmode engine is something I've wanted to do for a long time. I
9 | grew up using [DOS](https://en.wikipedia.org/wiki/MS-DOS) and hanging out on BBSes, so I love textmode and [ANSI art](https://www.google.com/search?tbm=isch&q=ansi+art&gws_rd=ssl). I
10 | used [TheDraw](https://en.wikipedia.org/wiki/TheDraw) as a kid and now use [PabloDraw](http://picoe.ca/products/pablodraw/) for creating ANSI art. Even [Sixteen Colors](http://www.sixteencolors.net) has a [JS based textmode editor](http://draw.sixteencolors.net/). And as far as
11 | textmode converting goes, I've experimented with [AALib](http://aa-project.sourceforge.net/aalib/), [libcaca](http://caca.zoy.org/), and even [TextFX](http://sol.gfxile.net/textfx/index.html).
12 | I looked at the [Textmode Demo Framework](http://www.pouet.net/prod.php?which=64192) released by the [TMDC folks](http://tmdc.scene.org/), too. But I wanted
13 | something I could use with Processing, something fast and fun. And that's what
14 | this is. It may not be as accurate as the others, but I am hoping since it uses
15 | Processing (rather than C) that it may be more accessible to a wide range of
16 | artists and creative coders.
17 |
18 | The code is well commented, but here's the tl;dr on how it works: you draw to a small
19 | offscreen buffer and then convert the pixels in that buffer to text and draw to the
20 | main screen. Brightness controls the glyphs and the average color of the pixel area
21 | controls the color of that glyph. The background is always black, like conventional
22 | ANSI art tools.
23 |
24 | I hope you enjoy using it as much as I do! :pray: For more info, pics, and video, please
25 | visit: http://www.no-carrier.com
26 |
27 | Don Miller / NO CARRIER
28 |
29 | Click on the image below to be taken to Vimeo to check out the engine in action:
30 | [](https://vimeo.com/165805982)
31 |
32 | And here's a link to a video from a Textmode Lightsynth I'm working on. More soon :smile:
33 | [](https://vimeo.com/165815175)
34 |
--------------------------------------------------------------------------------
/UNLICENSE.txt:
--------------------------------------------------------------------------------
1 | This is free and unencumbered software released into the public domain.
2 |
3 | Anyone is free to copy, modify, publish, use, compile, sell, or
4 | distribute this software, either in source code form or as a compiled
5 | binary, for any purpose, commercial or non-commercial, and by any
6 | means.
7 |
8 | In jurisdictions that recognize copyright laws, the author or authors
9 | of this software dedicate any and all copyright interest in the
10 | software to the public domain. We make this dedication for the benefit
11 | of the public at large and to the detriment of our heirs and
12 | successors. We intend this dedication to be an overt act of
13 | relinquishment in perpetuity of all present and future rights to this
14 | software under copyright law.
15 |
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22 | OTHER DEALINGS IN THE SOFTWARE.
23 |
24 | For more information, please refer to
25 |
--------------------------------------------------------------------------------
/data/Px437_IBM_BIOS-16.vlw:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/no-carrier/ProcessingTextmodeEngine/043a92490d166fcc0700c31da48317da97e75f8b/data/Px437_IBM_BIOS-16.vlw
--------------------------------------------------------------------------------
/data/face.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/no-carrier/ProcessingTextmodeEngine/043a92490d166fcc0700c31da48317da97e75f8b/data/face.png
--------------------------------------------------------------------------------
/renderTextMode.pde:
--------------------------------------------------------------------------------
1 | // math for comments below is based on a 1024x768px screen
2 | // with segSize variable set to 4, as you can see below in initTextmode()
3 |
4 | void initTextmode() {
5 | segSize = 4;
6 | // 800x600 / segSize of 4 = 200x150 buffer --- 1024x768 / segSize of 4 = 256x192 buffer
7 | // see renderTextMode about this whole buffer and segment size thing
8 | b = createGraphics(width/segSize, height/segSize, P3D); // we need P3D for our shapes
9 | // 16x16 IBM Bios font from: http://int10h.org/oldschool-pc-fonts/readme/
10 | font = loadFont("Px437_IBM_BIOS-16.vlw");
11 | textFont(font, 16); // size of font
12 | textAlign(LEFT, TOP); // helps line it all up
13 | }
14 |
15 | // the tl;dr for this whole damn tab is this:
16 | // we have a 256x192px off-screen buffer that we draw on
17 | // we break that up into 4x4px segments, for a total of 64x48
18 | // after we get the average RGB and brightness for each 4x4px segment
19 | // we assign a colored 16x16px glyph and write it to our 1024x768px screen
20 |
21 | // 1024x768 screen / 4 (for segment size) = 256x192px buffer
22 | // 256x192px / 4 = 64x48 segments of 4x4px each
23 | // 16x16px colored glyph assigned to each 64x48 4x4px segment = 1024x768px
24 | // MATH CHECKS OUT - WOOHOO!
25 | // (guess it wasn't really a tl;dr, but its shorter than what you see below...)
26 |
27 |
28 | void renderTextMode() {
29 | b.loadPixels(); // this loads pixel data from the buffer into an array for faster access.
30 |
31 | // now we have to go through all of the pixels, analyzing their color and brightness to
32 | // assign an appropriate color and glyph. our buffer is currently 256x192px.
33 |
34 | for (int h=0; h 204) { // and the brightness is above 204
100 | text("█", startX * segSize, startY * segSize); // we assign this character.
101 | } else if (segb > 152) { // if its above 152, we assign it
102 | text("▓", startX * segSize, startY * segSize); // this character. and so on...
103 | } else if (segb > 100) {
104 | text("▒", startX * segSize, startY * segSize);
105 | } else if (segb > 48) {
106 | text("░", startX * segSize, startY * segSize); // and if the brightness for our segment
107 | } // is under 48, we don't assign a glyph.
108 | } else { // here is our ASCII mode,
109 | if (segb > 230) { // where we do the same as above,
110 | text("#", startX * segSize, startY * segSize); // but we more characters!
111 | } else if (segb > 207) {
112 | text("&", startX * segSize, startY * segSize);
113 | } else if (segb > 184) {
114 | text("$", startX * segSize, startY * segSize);
115 | } else if (segb > 161) {
116 | text("X", startX * segSize, startY * segSize);
117 | } else if (segb > 138) {
118 | text("x", startX * segSize, startY * segSize);
119 | } else if (segb > 115) {
120 | text("=", startX * segSize, startY * segSize);
121 | } else if (segb > 92) {
122 | text("+", startX * segSize, startY * segSize);
123 | } else if (segb > 69) {
124 | text(";", startX * segSize, startY * segSize);
125 | } else if (segb > 46) {
126 | text(":", startX * segSize, startY * segSize);
127 | } else if (segb > 23) {
128 | text(".", startX * segSize, startY * segSize); // again, if brightness is less than 23, we
129 | } // don't assign a glyph, which makes it black.
130 | }
131 | }
132 | }
133 | }
134 |
--------------------------------------------------------------------------------