OLED Graphics Display driver

brad
 
Posts: 31
Joined: Tue Jan 06, 2015 5:19 am

OLED Graphics Display driver

Sat Jul 04, 2015 8:48 am

I saw a bunch of posts about a guy running doom on some LCD's. Never saw any code for it though. I thought I'd give these OLED displays a go.
http://www.aliexpress.com/item/0-96-Inch-Yellow-and-Blue-I2C-IIC-Serial-128X64-OLED-LCD-LED-Display-Module-for/2053302733.html

We can't run doom on them, but lets see how they perform for other real time functions.
I ordered a few - $4 each is a good price. And they are the same size as the vocore. It makes for a nice screen what doesn't increase the vocore size. Adafruit sells them for $20+, but in many varieties.

I decided the screen needed to work similar to a video card. Some shared memory that if you update it updates the display. This means, write a deamon that displays whatever is in the shared memory. Since these have 1 bit per pixel (on or off) then its quite simple.

The code for the software graphics deamon.

Code: Select all
/* SSD1306 display deamon
*/

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <unistd.h>
#include <fcntl.h>
#include <linux/i2c-dev.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <string.h>

#include "deamon_functions.h"

#define FILEPATH "/tmp/graphics"
#define NUMCHARS (1025)
#define FILESIZE (NUMCHARS * sizeof(unsigned char))
#define refreshRate 15000

int main (int argc, char *argv[]){
    
    int fd, result,i;
    unsigned char *map; // mapped array of bytes
   unsigned char oldmap[1024];
   
   //open file for writting
   fd = open(FILEPATH, O_RDWR | O_CREAT | O_TRUNC, (mode_t)0600);
   if (fd == -1){
      perror("Error opening file for writting");
      exit(EXIT_FAILURE);
   }

   //stretch file size to size of graphics memory
   result = lseek(fd, FILESIZE-1, SEEK_SET);
   if (result == -1){
      close(fd);
      perror("Error stretching file size");
      exit(EXIT_FAILURE);
   }

   //need to write to file to give it size
   result = write(fd, "\0", 1);
   if (result != 1){
      close(fd);
      perror("Error writting last byte to file");
      exit(EXIT_FAILURE);
   }
   
   //Map the file
   map = mmap(0, FILESIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
   if (map == MAP_FAILED){
      close(fd);
      perror("Error mmapping file");
      exit(EXIT_FAILURE);
   }
   

   /* i2c stuff
    *
    * */
   
   //Open OLED i2c file
   i2c_oled_fd  = open("/dev/i2c-0", O_RDWR);
   if (i2c_oled_fd  < 0) {
      perror("Error opening i2c file");
      exit(EXIT_FAILURE);
   }
   if (ioctl(i2c_oled_fd, I2C_SLAVE, deviceI2CAddress) < 0) {
      printf("Error ioctl");
      exit(EXIT_FAILURE);
   }
   
   // Power up Display
   init();
   memcpy(oldmap,map,1024);
   data(map,1024);
   
   //setup the refresh timer vars
   int16_t loc;
   clock_t begin, end;
   double time_spent;
   
   //keep updating the display forever
   //can put in exit commands if you want
   while (1){
      begin=clock();
      //we don't want to waste cpu power if there is no changes.
      if (map[1024]==1){
         //for each row (8 pixels to a row = 64/8 = 8 rows)
         //change if you have a different size screen
         for (i=0;i<8;i++){
            //memory buffer location is colums*row
            loc=i*128;
            //check if any memory has changed for that row
            if (memcmp(map+loc,oldmap+loc,128)){
               //update display with only that row
               //this saves a lot of time updating the display.
               //if updating all pixels refresh rate is very slow (i2c speed issue)
               display_block(i,0,127,map);
               //copy the row memory to the comparison memory
               memcpy(oldmap+loc,map+loc,128);
            }
         
         }
         //work done, set ready to recieve.
         map[1024]=0;
      }
      //lets make the fresh rate every 15ms to save CPU overload
      //make this time bigger if you want to use less cpu
      //15ms is about as fast as you can go
      end=clock();
      time_spent = (double)(end-begin)/CLOCKS_PER_SEC*1000000;
      
      if (time_spent<refreshRate){
         usleep(refreshRate-time_spent);
      }
      
   }
   close(i2c_oled_fd);
   
   //unmap file
   if (munmap(map, FILESIZE) == -1){
      perror("Error un-mapping failed");      
   }
   close(fd);
   return 0;
}


Code for the deamon_functions.h was translated from adafruit code. I translated some of this code https://github.com/the-raspberry-pi-guy/OLED
https://github.com/adafruit/Adafruit_SSD1306

Code: Select all
#define I2C_DEVID  (0x78 >>1);      // Translate Address
#define CMD_COMMAND      0x80      // Single command
#define CMD_CONT_CMD      0x00      // Continous command
#define CMD_DATA      0x40      // Single Data
#define CMD_CONT_DATAT    0xC0      // Continuous Data
#define CMD_NORMAL   0xA0      //
#define CMD_SETPAGE   0x22      //

       
#define  SET_LOW_COLUMN 0x00
#define  SET_HIGH_COLUMN     0x10
#define  SET_MEMORY_MODE     0x20
#define  SET_COL_ADDRESS     0x21
#define  SET_PAGE_ADDRESS    0x22
#define  RIGHT_HORIZ_SCROLL  0x26
#define  LEFT_HORIZ_SCROLL   0x27
#define  VERT_AND_RIGHT_HORIZ_SCROLL 0x29
#define  VERT_AND_LEFT_HORIZ_SCROLL  0x2A
#define  DEACTIVATE_SCROLL   0x2E
#define  ACTIVATE_SCROLL     0x2F
#define  SET_START_LINE     0x40
#define  SET_CONTRAST        0x81
#define  CHARGE_PUMP         0x8D
#define  SEG_REMAP           0xA0
#define  SET_REVERSE       0xA1
#define  SET_VERT_SCROLL_AREA 0xA3
#define  DISPLAY_ALL_ON_RESUME 0xA4
#define  DISPLAY_ALL_ON      0xA5
#define  NORMAL_DISPLAY      0xA6
#define  INVERT_DISPLAY      0xA7
#define  DISPLAY_OFF         0xAE
#define  DISPLAY_ON          0xAF
#define  COM_SCAN_INC        0xC0
#define  COM_SCAN_DEC        0xC8
#define  SET_DISPLAY_OFFSET  0xD3
#define  SET_COM_PINS        0xDA
#define  SET_VCOM_DETECT     0xDB
#define  SET_DISPLAY_CLOCK_DIV 0xD5
#define  SET_PRECHARGE       0xD9
#define  SET_MULTIPLEX       0xA8

#define  MEMORY_MODE_HORIZ  0x00
#define  MEMORY_MODE_VERT  0x01
#define  MEMORY_MODE_PAGE  0x02

// Global variables
unsigned char     buffer[4];
int       i2c_oled_fd;
int      deviceI2CAddress = I2C_DEVID;
int rows = 64;
int cols = 128;
int buffer_rows=64, buffer_cols=128;
int bytes_per_col;
unsigned char *bitmap_data;
int col_offset = 0;


void send_command(unsigned char cmd){
   unsigned char buf[3];
   buf[0]=0x80;
   buf[1]=cmd;
   write(i2c_oled_fd,buf,2);
}

void data(unsigned char * bytes, int length){
   int max_xfer = 32;
   int len = max_xfer;
   int count=0;
   unsigned char b[max_xfer+1];
   b[0]=0x40;
   while (count<length){
      if (length-count<max_xfer){
         len=length-count;
      }
      memcpy(b+1,bytes+count,len);
      write(i2c_oled_fd,b,len+1);
      count+=max_xfer;

   }
}

void init(){
   //initialise display
   send_command(DISPLAY_OFF);
   send_command(SET_DISPLAY_CLOCK_DIV);
   send_command(0x80);


   if (buffer_rows == 64){
      send_command(SET_MULTIPLEX);
      send_command(0x3F);
      send_command(SET_COM_PINS);
      send_command(0x12);
   }else{
      send_command(SET_MULTIPLEX);
      send_command(0x1F);
      send_command(SET_COM_PINS);
      send_command(0x02);
   } 
   send_command(SET_DISPLAY_OFFSET);
   send_command(0x00);
   send_command(SET_START_LINE | 0x00);

   send_command(CHARGE_PUMP);
   send_command(0x14);
   send_command(SET_MEMORY_MODE);
   send_command(0x00);
   send_command(SEG_REMAP | 0x01);
   send_command(COM_SCAN_DEC);
   send_command(SET_CONTRAST);
   send_command(0x8f);

   send_command(SET_PRECHARGE);
   send_command(0xF1);
   send_command(SET_VCOM_DETECT);
   send_command(0x40);
   send_command(DISPLAY_ALL_ON_RESUME);
   send_command(NORMAL_DISPLAY);
   send_command(DISPLAY_ON);
}

void display_block(int row, int col_start, int col_end, unsigned char *map){
   int page_start = row;
   int page_end = row;
   int len = col_end-col_start;
   int data_start = (page_start*buffer_cols)+col_start;
   send_command(SET_MEMORY_MODE);
   send_command(MEMORY_MODE_VERT);
   send_command(SET_PAGE_ADDRESS);
   send_command(page_start);
   send_command(page_end);
   send_command(SET_COL_ADDRESS);
   send_command(col_start);
   send_command(col_end);
   
   data(map + data_start, len);   
}


Now we have a shared memory file at /tmp/graphics which we can update with our other software to put stuff on the screen.
When nothing changes in the /tmp/graphics memory map, the CPU usage is very close to 0.

So lets make a bouncing ball app that relies on constant screen updates and see how much the CPU is suffering.
Lets hope it runs smooth!
Code: Select all
/* Bouncing ball code for OLED Deamon on openWRT

*/

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <unistd.h>
#include <fcntl.h>
#include <linux/i2c-dev.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <string.h>

#include "font5x8.h"
#include "display_functions.h"

#define FILEPATH "/tmp/graphics"
#define NUMCHARS (1025)
#define FILESIZE (NUMCHARS * sizeof(unsigned char))

void bounce(unsigned char * map, int speed){
   int ball_x, ball_y, radius=1;
   int dy, dx;
   int window_width=128;
   int window_height=64;
   ball_x = window_width /2;
   ball_y = window_height /2;
   
   dx = dy = speed;
   
   while (1){
      //wait for ready to send
      while (map[1024]==1){
         usleep(1000); //1ms sleep to let other things do work
      }
      draw_circle(ball_x,ball_y,radius, 0, map);
      
      if (ball_y < radius+17 || ball_y > window_height - radius -2){
         dy *= -1;
      }
      if (ball_x < radius || ball_x > window_width - radius -2){
         dx *= -1;
      }
      ball_x += dx;
      ball_y += dy;
      draw_circle(ball_x,ball_y,radius, 1, map);
      map[1024]=1;
   }
}

int main (int argc, char *argv[]){
    int fd, i;
    unsigned char *map; //graphics memory map

   bytes_per_col = rows / 8;

   //open mmap file
   fd = open(FILEPATH, O_RDWR);
   if (fd == -1){
      perror("Error opening file");
      exit(EXIT_FAILURE);
   }
   
   map = mmap(0, FILESIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
   if (map == MAP_FAILED){
      close(fd);
      perror("Error mmapping file");
      exit(EXIT_FAILURE);
   }

   //clear screen
   for (i=0;i<1024;i++){
      map[i]=0x00;
   }
   map[1024]=1;
   
   //wait for ready to send
   while (map[1024]==1){
      usleep(1000);
   }
   
   //start the bouncing ball forever loop
   bounce(map, atoi(argv[1]));
   
   //unmap memory
   if (munmap(map, FILESIZE) == 1){
      perror("Error unmapping file");
   }
   close(fd);
   return 0;
}


and display_functions.h
Code: Select all
#define I2C_DEVID  (0x78 >>1);      // Translate Address
#define CMD_COMMAND      0x80      // Single command
#define CMD_CONT_CMD      0x00      // Continous command
#define CMD_DATA      0x40      // Single Data
#define CMD_CONT_DATAT    0xC0      // Continuous Data
#define CMD_NORMAL   0xA0      //
#define CMD_SETPAGE   0x22      //

       
#define  SET_LOW_COLUMN 0x00
#define  SET_HIGH_COLUMN     0x10
#define  SET_MEMORY_MODE     0x20
#define  SET_COL_ADDRESS     0x21
#define  SET_PAGE_ADDRESS    0x22
#define  RIGHT_HORIZ_SCROLL  0x26
#define  LEFT_HORIZ_SCROLL   0x27
#define  VERT_AND_RIGHT_HORIZ_SCROLL 0x29
#define  VERT_AND_LEFT_HORIZ_SCROLL  0x2A
#define  DEACTIVATE_SCROLL   0x2E
#define  ACTIVATE_SCROLL     0x2F
#define  SET_START_LINE     0x40
#define  SET_CONTRAST        0x81
#define  CHARGE_PUMP         0x8D
#define  SEG_REMAP           0xA0
#define  SET_REVERSE       0xA1
#define  SET_VERT_SCROLL_AREA 0xA3
#define  DISPLAY_ALL_ON_RESUME 0xA4
#define  DISPLAY_ALL_ON      0xA5
#define  NORMAL_DISPLAY      0xA6
#define  INVERT_DISPLAY      0xA7
#define  DISPLAY_OFF         0xAE
#define  DISPLAY_ON          0xAF
#define  COM_SCAN_INC        0xC0
#define  COM_SCAN_DEC        0xC8
#define  SET_DISPLAY_OFFSET  0xD3
#define  SET_COM_PINS        0xDA
#define  SET_VCOM_DETECT     0xDB
#define  SET_DISPLAY_CLOCK_DIV 0xD5
#define  SET_PRECHARGE       0xD9
#define  SET_MULTIPLEX       0xA8

#define  MEMORY_MODE_HORIZ  0x00
#define  MEMORY_MODE_VERT  0x01
#define  MEMORY_MODE_PAGE  0x02

// Global variables
unsigned char     buffer[4];
int       i2c_oled_fd;
int      deviceI2CAddress = I2C_DEVID;
int rows = 64;
int cols = 128;
int buffer_rows=64, buffer_cols=128;
int bytes_per_col;
unsigned char bitmap_data[1024];
int col_offset = 0;

//Functions
int printHex( unsigned char * packet, int n){
   int j;
   for (j=0;j<n;j++){
      printf("%02X ", ((unsigned char *) packet)[j] );   
   }
   printf("\n\n");
   return 1;
}
void send_command(unsigned char cmd){
   unsigned char buf[3];
   buf[0]=0x80;
   buf[1]=cmd;
   write(i2c_oled_fd,buf,2);
}
void send_dat(unsigned char dat)
{
   buffer[0]=0x40;
   buffer[1]=dat;
   write(i2c_oled_fd,buffer,2);
}
void data(unsigned char * bytes, int length){
   //clock_t begin, end;
   //double time_spent;
   //begin=clock();
   int max_xfer = 32;
   int len = max_xfer;
   int count=0;
   unsigned char b[max_xfer+1];
   b[0]=0x40;
   while (count<length){
      if (length-count<max_xfer){
         len=length-count;
      }
      //printHex(b,len+1);
      memcpy(b+1,bytes+count,len);
      write(i2c_oled_fd,b,len+1);
      count+=max_xfer;

   }
   //end=clock();
   //time_spent = (double)(end-begin)/CLOCKS_PER_SEC;
   //printf("%f\n",time_spent);
}
int draw_pixel(int x, int y, int on, unsigned char * map){
   if (x<0 || x>=cols || y<0 || y>=buffer_rows){
      return 1;
   }
   
   if (on){
      map[x+(y/8)*128] |= (1 << (y&7));
   }else{
      map[x+(y/8)*128] &= ~(1 << (y&7));
   }
   return 0;
}
void drawLine(int x0, int y0, int x1, int y1, int on, unsigned char * map){
   int dx = abs(x0-x1);
   int dy = abs(y0-y1);
   int xdir = (x1-x0)/dx;
   int ydir = (y1-y0)/dy;
   if (dy>dx){
      int xstep = (dx*1000)/dy;
      int i;
      for (i=0; i<=dy; i++){
         draw_pixel((x0+(xstep*i/1000*xdir)),(y0+i*ydir),on,map);
      }
   }else{
      int ystep = (dx*1000)/dy;
      int i;
      for (i=0; i<=dx; i++){
         draw_pixel((x0+i*xdir),(y0+(ystep*i/1000*ydir)),on,map);
      }
   }
}
void draw_circle(int x0, int y0, int r, int on, unsigned char *map){
   int f = 1 - r;
   int ddf_x = 1;
   int ddf_y = -2 * r;
   int x = 0;
   int y = r;
   
   draw_pixel(x0,y0+r, on, map);
   draw_pixel(x0,y0-r, on, map);
   draw_pixel(x0+r,y0, on, map);
   draw_pixel(x0-r,y0, on, map);
   
   while (x<y){
      if (f >= 0){
         y--;
         ddf_y += 2;
         f += ddf_y;
      }
      x++;
      ddf_x += 2;
      f += ddf_x;
      
      draw_pixel(x0 + x,y0 + y, on, map);
      draw_pixel(x0 - x,y0 + y, on, map);
      draw_pixel(x0 + x,y0 - y, on, map);
      draw_pixel(x0 - x,y0 - y, on, map);
      draw_pixel(x0 + y,y0 + x, on, map);
      draw_pixel(x0 - y,y0 + x, on, map);
      draw_pixel(x0 + y,y0 - x, on, map);
      draw_pixel(x0 - y,y0 - x, on, map);
   }
}

void invert_display(){
    send_command(INVERT_DISPLAY);
}
void flip_display(int flipped){   
        if (flipped){
            send_command(COM_SCAN_INC);
            send_command(SEG_REMAP | 0x00);
        }else{
            send_command(COM_SCAN_DEC);
            send_command(SET_COM_PINS);
            send_command(0x02);
      }
}
void normal_display(){
    send_command(NORMAL_DISPLAY);
}
void set_contrast_level(int contrast){
   send_command(SET_CONTRAST);
   send_command(contrast);
}
void clear_screen(){
   int i;
   unsigned char buf[17]={0x00};
   buf[0]=0x40;
   for ( i=0; i < 64; i++) {
      write(i2c_oled_fd,buf,17);
   }
   
}

void init(){
   //initialise display
   send_command(DISPLAY_OFF);
   send_command(SET_DISPLAY_CLOCK_DIV);
   send_command(0x80);


   if (buffer_rows == 64){
      send_command(SET_MULTIPLEX);
      send_command(0x3F);
      send_command(SET_COM_PINS);
      send_command(0x12);
   }else{
      send_command(SET_MULTIPLEX);
      send_command(0x1F);
      send_command(SET_COM_PINS);
      send_command(0x02);
   } 
   send_command(SET_DISPLAY_OFFSET);
   send_command(0x00);
   send_command(SET_START_LINE | 0x00);

   send_command(CHARGE_PUMP);
   send_command(0x14);
   send_command(SET_MEMORY_MODE);
   send_command(0x00);
   send_command(SEG_REMAP | 0x01);
   send_command(COM_SCAN_DEC);
   send_command(SET_CONTRAST);
   send_command(0x8f);

   send_command(SET_PRECHARGE);
   send_command(0xF1);
   send_command(SET_VCOM_DETECT);
   send_command(0x40);
   send_command(DISPLAY_ALL_ON_RESUME);
   send_command(NORMAL_DISPLAY);
   send_command(DISPLAY_ON);
}



void draw_text(int x, int y, char * string, unsigned char * map){
   int i,j,r;
   int p;
   unsigned char mask;
   for (i=0;i<strlen(string);i++){
      p = (int)string[i] * font_cols;
      for (j=0;j<font_cols;j++){//col in range(0,font_cols):
         mask = font_bytes[p];
         p++;
         for (r=0;r<font_rows;r++){//row in ange(0,8):
            
            draw_pixel(x,y+r,mask & 0x1, map);
            
            mask >>= 1;
         }
         x++;
         
      }
   }
}
void draw_text2(int x, int y, char * string, int size, int space, int on, unsigned char * map){
   int i,j,r,mask,p,py,sy,sx,px;
   for (i=0;i<strlen(string);i++){
      p = string[i] * font_cols;
      for (j=0;j<font_cols;j++){
         
         mask = font_bytes[p];
         
         p++;
         py = y;
         for (r=0;r<8;r++){
            for (sy=0;sy<size;sy++){
               px = x;
               for (sx=0;sx<size;sx++){
                  
                  draw_pixel(px,py,mask & on, map);
                  
                  px++;
               }
               py++;
            }
            mask >>= 1;
         }
         x += size;
      }   
      x += space;
    }
}


Yeah, thats a lot of CPU to render at a smooth 60FPS. :o But it works very smooth! Now for some miniature game making. :geek:
Image
Displaying CPU usage on the screen used less than 5% cpu.Only large CPU usage on things that need constant rendering. Which is ok. ;)

brad
 
Posts: 31
Joined: Tue Jan 06, 2015 5:19 am

Re: OLED Graphics Display driver

Sun Jul 05, 2015 10:40 am

Continued....
I saw this on youtube https://youtu.be/GlCye0TyHrE
The guy made a basic wolf3d mock up and ran it on a very tiny micro.
I did a bit of a crude convert on his code and it works nice with my graphics driver.

Image

Code: Select all
/**
 * LCDWolf3D
 * jaburns https://github.com/jaburns/lcd-wolf3d
 * Plus changes for openwrt
 */
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <unistd.h>
#include <fcntl.h>
#include <linux/i2c-dev.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <string.h>
#include <termios.h>
#include <math.h>

#include "font5x8.h"
#include "display_functions.h"
#define FILEPATH "/tmp/graphics"
#define NUMCHARS (1025)
#define FILESIZE (NUMCHARS * sizeof(unsigned char))


#define UNSIGNED_DIFF( _a, _b ) ( (_a) < (_b) ? (_b) - (_a) : (_a) - (_b) )
#define SET_PIXEL( _x, _y ) ( screenBuffer.data[ (_x) + 128 * ((_y) / 8) ] |=  ( 1 << ( (_y) % 8 ) ) )
#define CLR_PIXEL( _x, _y ) ( screenBuffer.data[ (_x) + 128 * ((_y) / 8) ] &= ~( 1 << ( (_y) % 8 ) ) )
#define FLP_PIXEL( _x, _y ) ( screenBuffer.data[ (_x) + 128 * ((_y) / 8) ] ^=  ( 1 << ( (_y) % 8 ) ) )

#define GET_WALL_DOT( _z, _x, _y ) _z[ (_x) + ( ((_y) >> 3) << 6 ) ]  & ( 1 << ( (_y) % 8 ) )

#define BLOCK_SIZE    20
#define BLOCK_SIZE_F  20.0f

#define HIT_WIDTH 5.0f

#define MAP_WIDTH   24
#define MAP_HEIGHT  24

#define VISPLANEDIST_TIMES_WALLHEIGHT  2000.0f

// Holds the result of a ray being cast.
struct SLICE
{
    int targetBlockX;
    int targetBlockY;
    int textureId;
    int textureOffset;
    int sliceHeight;
};

struct SCREENBUFFER
{
    int width;
    int height;
    unsigned char *data;
};



const unsigned char tex_wood[] = {
    0x6,0x6,0x76,0xF6,0xF6,0xF6,0x36,0x6,0xF6,0xF6,0x6,0xF6,0xF6,0x6,0x6,0x6,0x86,0xF6,0xF6,0x6,0xF6,0x6,0xF6,0x6,0x6,0x6,0xF6,0xF6,0x6,0xF6,0xF6,0x6,0xF6,0x6,
    0xF6,0xF6,0x6,0xF6,0xF6,0xE6,0xC6,0xE6,0xF6,0x6,0xF6,0xF6,0x6,0xF6,0x6,0xF6,0x6,0x6,0xF6,0x6,0xF6,0x6,0xF6,0xF6,0x6,0xF6,0xF6,0x6,0xF6,0xF6,
    0x7F,0xF0,0xC0,0x81,0x3,0xC1,0xF0,0x0,0xFF,0xFF,0x0,0xFF,0xFF,0xFF,0xFC,0xFE,0xFF,0xFF,0x7,0x0,0xFF,0x0,0xFF,0x0,0x0,0x0,0xFF,0xFF,0x0,0xFF,0x87,0xF0,0xFF,
    0x0,0xFF,0xFF,0x0,0xFF,0x7F,0xFF,0xFF,0xFF,0xFF,0x0,0xFF,0xFF,0x0,0xFF,0x0,0xFF,0x0,0x0,0xFF,0x0,0xFF,0x0,0xF,0xFF,0x0,0xFF,0xFF,0x0,0xFF,0x0,
    0x0,0x7,0x1F,0x7F,0x3F,0xF,0x0,0x0,0xFF,0xFF,0x0,0xFF,0x7,0x7F,0xFF,0xFF,0xFF,0x3,0x0,0xC0,0xFF,0x0,0xFF,0x0,0x0,0x0,0xFF,0xFF,0x0,0xFF,0xFF,0x3F,0xF,0x0,0xFF,
    0xFF,0x0,0xFF,0xE0,0xC1,0x83,0xC1,0xE0,0x0,0xFF,0xFF,0x0,0xFF,0x0,0xFF,0x0,0x0,0xFF,0x0,0xFF,0xFF,0x0,0x3F,0x0,0xFF,0xFF,0x0,0xFF,0xFC,
    0x1E,0xE0,0x0,0x0,0x80,0xF0,0xE,0x0,0xFF,0xFF,0x0,0xFF,0x0,0x0,0x1,0x1,0x0,0x0,0xF0,0xFF,0x1,0x0,0xFF,0x0,0x0,0x0,0xFF,0xFF,0x0,0xFF,0x0,0x80,0xF0,0x0,0xFF,
    0xFF,0x0,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x0,0xFF,0xFF,0x0,0xFF,0x0,0xFF,0x0,0x0,0xFF,0x0,0x3,0xFF,0xFE,0xE0,0x0,0xFF,0xFF,0x0,0xFF,0x1,
    0x0,0x0,0x7,0xC,0x7,0x0,0x0,0x0,0xFF,0xFF,0x0,0xFF,0xFF,0xFC,0xF0,0xE0,0xF0,0xFE,0xFF,0x7,0x0,0xFC,0xFF,0x0,0x0,0x0,0xFF,0xFF,0x0,0xFF,0xFE,0xFF,0xFF,0x0,0xFF,
    0xFF,0x0,0xFF,0x80,0x3,0xF,0x7,0x1,0x0,0xFF,0xFF,0x0,0xFF,0x0,0xFF,0x0,0x0,0xFF,0xE0,0x0,0x7,0xFF,0xFF,0x0,0xFF,0xFF,0x0,0xFF,0xF0,
    0x3E,0xF8,0xE0,0xC0,0xE0,0xF8,0x1F,0x0,0xFF,0xFF,0x0,0xFF,0x3,0x1F,0x3F,0x7F,0x7F,0x1F,0x1,0x0,0xF0,0xFF,0x7,0x0,0x0,0x0,0xFF,0xFF,0x0,0xFF,0x1F,0x7,0x0,0x0,0xFF,
    0xFF,0x0,0xFF,0xFF,0xFE,0xF8,0xFC,0xFE,0x0,0xFF,0xFF,0x0,0xFF,0x0,0xFF,0x0,0x0,0xF,0xFF,0xE0,0x0,0x3,0x3F,0x0,0xFF,0xFF,0x0,0xFF,0x0,
    0x0,0x3,0x1F,0xFF,0xFF,0x1F,0x0,0x0,0xFF,0xFF,0x0,0xFF,0xFC,0xE0,0x0,0x0,0x0,0x0,0xE0,0xFE,0x7F,0x7,0x0,0xE0,0x0,0x0,0xFF,0xFF,0x0,0xFF,0xF0,0xFC,0xFF,0x0,0xFF,
    0xFF,0x0,0xFF,0x1F,0xFF,0xFF,0x7F,0xF,0x0,0xFF,0xFF,0x0,0xFF,0x0,0xFF,0x0,0x0,0x0,0x1F,0xFF,0xFE,0xF0,0x0,0x0,0xFF,0xFF,0x0,0xFF,0xE0,
    0xF8,0xC0,0x80,0x80,0x80,0x80,0xF8,0x0,0xFF,0xFF,0x0,0xFF,0x81,0x7,0x1F,0x3E,0x7C,0x3E,0x1F,0x7,0x80,0xC0,0xF8,0xFF,0xC0,0x0,0xFF,0xFF,0x0,0xFF,0xFF,0x7F,0x3F,
    0x0,0xFF,0x7F,0x0,0xFF,0x80,0x0,0x1,0x0,0x80,0x0,0xFF,0xFF,0x0,0xFF,0xC0,0x1F,0xFE,0xC0,0x0,0x0,0x0,0x7,0x1F,0x1E,0x0,0xFF,0xFF,0x0,0xFF,0xFF,
};


const unsigned char tex_bricks[] = {
    0x0,0xCC,0xFC,0xF0,0xF0,0xF0,0xF8,0xF8,0xF8,0xF8,0xF8,0xF8,0xFC,0x0,0x0,0x0,0xCC,0xFC,0xF8,0xF8,0xF8,0xF8,0xF8,0xF8,0xF8,0xF8,0xF8,0xF8,0xF8,0xF8,0xFC,0xFC,
    0xFC,0xFC,0xFC,0xFC,0xFC,0x0,0x0,0x0,0xC4,0xFC,0xFC,0xFC,0xF4,0xF4,0xFC,0xFC,0xFC,0xFC,0xF8,0xF8,0xF0,0xF0,0xF8,0xF8,0xFC,0xFC,0xFC,0xFC,0xFC,0xFC,0x0,0x0,
    0xF0,0xF3,0xF3,0xD3,0xF3,0xF3,0xF3,0xF3,0xF3,0xF3,0xF3,0xD3,0xD3,0xF0,0xF0,0xF0,0x3,0x3,0x3,0x33,0xB3,0xC3,0xE3,0xE3,0xE3,0xE3,0xE3,0xF3,0xF3,0xF3,0xF3,0xF3,
    0xF3,0xF3,0xF3,0xF3,0xF3,0xF0,0xF0,0xF0,0xF3,0xF3,0xD3,0xD3,0xD3,0xF3,0xF3,0xF3,0xD3,0xF3,0xD3,0xF3,0xF3,0x3,0x3,0x3,0xF3,0xF3,0xE3,0xE3,0xE3,0xC3,0xD0,0xD0,
    0xF,0xCB,0xCF,0x8F,0x8F,0x8F,0x8F,0x8F,0x8F,0x8F,0xCF,0xCF,0x4F,0xCF,0xCF,0xCF,0xC0,0xC0,0xC0,0x48,0xCD,0xCF,0xCF,0xCF,0xCF,0xCF,0xCF,0xCF,0xF,0xF,0xF,0xCF,
    0xCF,0x8F,0xF,0x8F,0x8F,0x8F,0x8F,0xF,0x8F,0x8F,0x8F,0x4F,0xCF,0xCF,0xCF,0xCF,0xCF,0xCF,0xCF,0xCF,0xCF,0xC0,0xC0,0xC0,0xC9,0xCE,0xCF,0xCF,0xCF,0xCF,0xF,0xF,
    0x0,0x3F,0x3A,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x0,0x0,0x0,0x30,
    0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x0,0x0,
    0xFF,0xFD,0xFF,0xFD,0xFF,0xFD,0xFD,0xFF,0xFF,0xFF,0xFF,0x0,0x0,0x0,0xC0,0xFE,0xFE,0xFE,0xFE,0xFE,0xFE,0xFE,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
    0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x0,0x0,0x0,0xE1,0xFF,0xFE,0xFE,0xFE,0xFE,0xFE,0xFE,0xFE,0xFE,0xFE,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
    0x0,0x4,0xF8,0xF8,0xF8,0xF8,0xF8,0xF8,0xF8,0xFC,0xFC,0xFC,0xFC,0xFC,0xFC,0xFC,0xFC,0xFC,0xFC,0xFC,0xFC,0xFC,0xFC,0xFC,0xFC,0xFC,0xFC,0xFC,0xFC,0xFC,0xFC,0xFC,
    0x0,0x0,0x0,0xC,0xFC,0xF8,0xF8,0xF8,0xF8,0xF8,0xF8,0xF8,0xF8,0xFC,0xFC,0xFC,0xFC,0xFC,0xFC,0xFC,0xFC,0xFC,0xFC,0xFC,0xFC,0xFC,0xFC,0xFC,0xFC,0xF8,0x0,0x0,
    0xE0,0xE3,0xE3,0xE3,0xE3,0xF3,0xF3,0xF3,0xF3,0xF3,0xF3,0xF3,0xF3,0xF3,0xF3,0xF3,0xF3,0xF3,0x3,0x3,0x3,0x13,0xF3,0xE3,0xE3,0xE3,0xE3,0xE3,0xE3,0xE3,0xE3,0xF3,
    0xF0,0xF0,0xF0,0xF3,0xF3,0xF3,0xF3,0xF3,0xF3,0xF3,0xF3,0xF3,0xF3,0xF3,0xF3,0x3,0x3,0x3,0x13,0xE3,0xE3,0xE3,0xE3,0xE3,0xF3,0xF3,0xF3,0xF3,0xF3,0xF3,0xF0,0xF0,
    0x1F,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F,0x0,0x0,0x0,0x18,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F,
    0x1F,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F,0x0,0x0,0x0,0x18,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F
};


const unsigned char tex_bird[] = {
    0x8,0xCC,0xFC,0xC8,0x8,0x8,0x88,0xA8,0x28,0x28,0x28,0xA8,0xAC,0xA8,0xA8,0xA8,0xAC,0xAC,0xA8,0xA8,0xA8,0xA8,0xA8,0xA8,0xA8,0xA8,0xA8,0xA8,0xA8,0xA8,0xAC,
    0xAC,0xAC,0xAC,0xAC,0xAC,0xAC,0xA8,0xA8,0xA8,0xAC,0xAC,0xAC,0xAC,0xAC,0xAC,0xAC,0xAC,0xAC,0xAC,0xA8,0xA8,0xA8,0x28,0x28,0x28,0x2C,0x8C,0xC,0x4C,0xC,0xFC,
    0x1C,0x0,0xF0,0xF3,0xF3,0xFF,0xFF,0x0,0xF3,0xFF,0xFF,0xFE,0x1D,0x9,0x3,0x7,0x1,0x0,0x1,0x3,0x3,0x7,0x7,0xF,0xF,0xF,0x1F,0x1F,0x3F,0xFF,0xE3,0x3,0x3,0x3,
    0x3,0x3,0xB,0xE3,0xF3,0x33,0x1F,0x1F,0xF,0xF,0xF,0x7,0x7,0x3,0x3,0x1,0x0,0x0,0xF,0xF,0xF,0x1F,0xFF,0x7E,0x9F,0xE1,0x0,0xFC,0xFE,0xC3,0xD0,0xD0,
    0xF,0xCB,0xCF,0xFF,0xFF,0x0,0xFF,0xFF,0x3,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2,0x1,0x1,0x7,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x4,0x7,
    0x1,0x1,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x10,0x0,0x8,0x0,0x0,0x0,0x0,0x1,0x2,0xFF,0xFF,0x0,0xFF,0xFF,0xCF,0xF,0xF,
    0x0,0x3F,0x3A,0xFF,0xFF,0x0,0xFF,0x81,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
    0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x81,0xFF,0x0,0xFF,0xFF,0x3F,0x0,0x0,
    0xFF,0xFD,0xFF,0xFF,0xFF,0x0,0xFF,0xFF,0xE3,0x83,0x1,0x1,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x80,0x20,0x10,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
    0x0,0x0,0x0,0x8,0x10,0x20,0x80,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x83,0xE3,0xFF,0xFF,0x0,0xFF,0xFF,0xFF,0xFF,0xFF,
    0x0,0x4,0xF8,0xFF,0xFF,0x0,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x33,0x3,0x82,0xC0,0x4,0x4,0x86,0xC7,0xC3,0x82,0x0,0x70,0x30,0x30,0x10,0x8,0x0,0x0,0x0,0x0,0x0,
    0x0,0x8,0x10,0x30,0x30,0x70,0x0,0x82,0xC3,0xC7,0x86,0x4,0x4,0xC0,0x82,0x3,0x33,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x0,0xFF,0xFF,0xF8,0x0,0x0,
    0xE0,0xE3,0xE3,0xEF,0xFF,0xF0,0xE3,0xE7,0xEF,0xCF,0xCF,0xDF,0xDF,0x9F,0x9F,0xBF,0x3F,0x3E,0x7C,0x7F,0x79,0xF8,0xF0,0xF0,0xF0,0xF0,0xF0,0xF8,0xC4,0x80,0x0,0x0,
    0x0,0x0,0x80,0xC4,0xF8,0xF0,0xF0,0xF0,0xF0,0xF0,0xF8,0x79,0x7F,0x7C,0x3E,0x3F,0xBF,0x9F,0x9F,0xDF,0xDF,0xDF,0xEF,0xEF,0xE7,0xE3,0xF0,0xFF,0xFF,0xF3,0xF0,0xF0,
    0x1F,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F,0x3,0x2,0x6,0x1E,0x1C,0x1C,0x1D,0x1D,0x19,0x19,0x1B,0x13,0x13,0x36,
    0x36,0x13,0x13,0x1B,0x19,0x19,0x1D,0x1D,0x1C,0x1C,0x1E,0x1E,0x1E,0x1F,0x1F,0x3,0x1,0x1,0x19,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F
};


const unsigned char tex_bricks2[]  = {
    0xF8,0xF8,0xF8,0xF8,0xF8,0xF8,0x18,0x18,0x0,0x0,0x0,0xFE,0xFE,0xFE,0xFE,0xFE,0xFE,0xFE,0xFE,0xFE,0x7E,0xFE,0xFE,0xFE,0xFE,0x4E,0x6,0x0,0x0,0x1E,0x3E,0xFE,
    0xFE,0xFE,0xFE,0xFE,0xFE,0xFE,0xFE,0x1E,0xE,0x2,0x0,0x0,0x0,0xF8,0xFC,0xFC,0xFC,0xFC,0xFC,0x7C,0xF8,0xF8,0x78,0xF8,0xF8,0xF8,0xF8,0xF8,0xF8,0x78,0xF8,0xF8,
    0x3,0x3,0x3,0x3,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x7,0x7F,0xFF,0xFF,0xFF,0xF7,0xD3,0xF7,0xBF,0xFF,0xBF,0xEF,0xF7,0xFF,0x10,0x0,0x0,0x0,0x0,0x0,0xE0,0xF0,0xF0,
    0xF0,0xF0,0xF8,0xF8,0xF8,0xF8,0xF8,0x8,0x0,0x0,0x0,0x0,0x1F,0xF,0x7,0x7,0x7,0x7,0x7,0x6,0x7,0x3,0x3,0x3,0x3,0x2,0x3,0x3,0x2,0x3,
    0xFF,0xFF,0xBF,0x3F,0xFF,0xFF,0xFF,0x3,0x1,0x0,0x0,0x0,0x80,0x83,0x8F,0x8F,0x87,0x87,0x85,0x87,0x86,0x3,0x7,0x3,0x1,0x2,0x80,0x80,0x80,0x80,0x80,0xDF,0xCF,
    0xC7,0xCF,0xC3,0xC3,0xC3,0xC3,0x83,0x3,0x0,0x0,0x0,0xE0,0xFE,0xFF,0xFF,0x3F,0x0,0x0,0xFC,0xFE,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x2F,0x3F,0xFF,0xFF,
    0x3,0xC3,0xC3,0xC3,0xE3,0xE3,0xE1,0xE0,0xE0,0xE0,0x0,0x0,0xFF,0xFF,0xDF,0xFF,0x3F,0xFF,0xFF,0xE7,0x3,0x0,0x0,0x0,0x7E,0xFF,0xFF,0xFF,0xFF,0xFF,0x5B,0xEF,
    0x1F,0x27,0xFF,0x9F,0xFF,0xFF,0xFF,0x0,0x0,0x0,0x0,0xFF,0xFF,0xFF,0xFF,0xFF,0x0,0x0,0x0,0xC0,0xC3,0xC1,0xC1,0xC1,0x81,0x81,0x81,0x81,0x81,0x81,0x3,0x3,
    0x0,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xBB,0xFF,0x0,0x0,0x7F,0xFF,0xD7,0xBF,0xF7,0xFF,0xFF,0xFF,0x0,0x0,0x0,0x0,0x0,0x7,0x3F,0x7F,0x7F,0x3F,0x17,0x1F,
    0x1D,0x1F,0x1C,0x1C,0x1F,0xF,0x0,0x0,0x0,0x0,0x0,0xF,0x1F,0xF,0xF,0x7,0x0,0x0,0x0,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F,0x1,0x0,0x0,
    0x0,0x7F,0x7F,0x7F,0x7F,0x7F,0x1F,0x1F,0x1F,0x1,0x0,0x0,0x0,0xFF,0xFF,0xFF,0xF7,0xFD,0xFF,0x7F,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xFC,0xFC,0xFC,0xFC,0xFC,0xFC,
    0xFC,0xFC,0xFC,0x7C,0xFC,0xFC,0xFC,0xFE,0xFE,0xFE,0xFE,0xFE,0x7E,0xFE,0xDE,0xFE,0xFE,0xBE,0xFE,0xFE,0x7E,0x7E,0xFE,0x7E,0xFE,0xFE,0xFE,0x6,0x0,0x0,0x0,
    0x0,0xF0,0xF8,0xF8,0xF8,0xF8,0x78,0x78,0xF0,0xF0,0xF0,0x70,0xE0,0xE0,0xE0,0xE0,0xE0,0xE0,0xE0,0xE0,0xE0,0xE0,0xE0,0xC0,0xC0,0xC0,0xC0,0xC7,0xC7,0xC3,0xC3,
    0xC3,0xC3,0x3,0x3,0x3,0x5,0x7,0x7,0x6,0xC,0x7,0x3,0x7,0x5,0x7,0x3,0x7,0x5,0x7,0x7,0x7,0x6,0x7,0x7,0x5,0x7,0x7,0x6,0x5,0x0,0x0,0x0,0x0,
    0x0,0xF,0x7,0x3,0x3,0x3,0x1,0x1,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x1,0x2,0x0,0x3,0x1,0x2,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0xF,
    0xF,0xF,0xF,0xE,0x6,0xE,0xE,0x0,0x0,0x18,0x1E,0x1E,0x1E,0x1E,0x1E,0x1E,0x1E,0x1E,0x1E,0x1E,0x1E,0x1E,0x1C,0x4,0x0,0x0
};


const float ONETWENTY8_OVER_PI = 40.743665431525F;


// 20 x 21
const int sprite_pistol[]  = {
    0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,
    0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,
    0,0,0,0,0,0,0,1,1,2,1,1,0,0,0,0,0,0,0,0,
    0,0,0,0,0,0,0,1,1,2,1,1,0,0,0,0,0,0,0,0,
    0,0,0,0,0,0,1,1,1,2,1,1,1,0,0,0,0,0,0,0,
    0,0,0,0,0,1,1,1,1,2,1,1,1,1,0,0,0,0,0,0,
    0,0,0,0,0,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,
    0,0,0,0,0,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,
    0,0,0,0,0,1,1,2,2,2,2,2,1,1,0,0,0,0,0,0,
    0,0,0,0,0,1,1,1,2,2,2,1,1,1,1,0,0,0,0,0,
    0,0,0,0,1,1,1,1,1,1,1,1,1,1,2,1,0,0,0,0,
    0,0,0,1,2,2,1,1,1,1,1,1,1,2,2,1,0,0,0,0,
    0,0,0,1,2,2,1,1,1,1,1,1,1,2,2,2,1,0,0,0,
    0,0,0,1,2,1,1,2,2,2,2,2,2,2,2,2,1,0,0,0,
    0,0,1,2,2,1,2,2,2,2,2,2,2,2,2,2,1,0,0,0,
    0,0,1,2,2,1,2,2,2,2,2,2,2,2,2,2,1,0,0,0,
    0,0,1,2,2,2,1,2,2,2,2,2,2,2,2,2,1,0,0,0,
    0,0,1,1,1,1,1,1,2,2,2,2,2,1,1,1,1,0,0,0,
    0,0,1,1,1,1,1,1,2,2,2,2,1,1,1,1,1,1,0,0,
    0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,
    1,1,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1,1,1
};


const unsigned char *textures[] =
{
    0,
    tex_wood,
    tex_bricks,
    tex_bird,
    tex_bricks2
};


const int level[] = {
  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
  1,2,2,3,2,2,3,2,2,3,2,2,4,4,4,4,4,4,4,4,4,4,4,1,
  1,2,0,0,0,0,0,0,0,0,0,2,4,0,0,0,0,0,0,0,0,0,4,1,
  1,3,0,0,0,0,0,0,0,0,0,3,4,0,0,0,0,0,0,0,0,0,4,1,
  1,2,0,0,0,3,2,2,2,0,0,2,4,0,0,4,0,4,0,4,0,0,4,1,
  1,2,0,0,0,2,2,0,0,0,0,2,4,0,0,0,0,0,0,0,0,0,4,1,
  1,3,0,0,0,3,3,0,0,0,0,3,4,0,0,4,0,0,0,4,0,0,4,1,
  1,2,2,0,2,2,2,0,0,0,0,2,4,0,0,0,0,4,4,4,0,0,4,1,
  1,0,2,3,2,0,2,2,0,2,2,2,4,4,4,4,4,0,0,0,4,4,4,1,
  1,0,0,0,0,0,0,4,0,4,4,4,4,4,4,4,0,0,0,0,0,4,4,1,
  1,0,0,0,0,0,0,4,0,0,0,0,0,0,4,0,0,0,0,0,0,0,4,1,
  1,0,0,0,0,0,0,4,0,0,0,0,0,0,4,0,0,0,3,0,0,0,4,1,
  1,0,0,0,0,0,0,4,0,4,4,4,0,0,0,0,0,0,0,0,0,0,4,1,
  1,0,0,0,0,0,0,4,3,4,4,4,4,4,4,4,0,0,0,0,0,4,4,1,
  1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,4,0,0,0,4,4,4,1,
  1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,4,4,0,0,0,1,
  1,4,4,4,4,4,4,4,4,0,0,0,0,0,0,0,0,1,1,0,0,0,0,1,
  1,4,0,4,0,0,0,0,4,0,0,0,0,0,0,0,1,0,0,1,0,0,0,1,
  1,4,0,0,0,0,5,0,4,0,0,0,0,0,0,0,1,0,0,1,0,0,0,1,
  1,4,0,4,0,0,0,0,4,0,0,0,0,0,0,0,0,1,1,0,0,0,0,1,
  1,4,0,4,4,4,4,4,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,
  1,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,
  1,4,4,4,4,4,4,4,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,
  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1
};

struct SCREENBUFFER screenBuffer;

float px =  70.0f;
float py = 170.0f;
float pa = -3.14159265f / 2.0f;


void castRay( float x, float y, float angle, int screenX, struct SLICE *result )
{
    float ox = x;        // origin coordinates
    float oy = y;

    float offsetAngle = ( (float)screenX * ( 3.14159265f / 3.0f ) ) / 128.0f - ( 3.14159265f / 6.0f );
    angle += offsetAngle;

   int yAxisWall = 0;

    float blockX = (float)( (int)( x / BLOCK_SIZE ) );    // The coordinate of the block we're sitting in
    float blockY = (float)( (int)( y / BLOCK_SIZE ) );

    //int stepXpositive;
   // int stepYpositive;

    float cosine = cos( angle );
    float sine   = sin( angle );

    if( sine   <  0.000001f && sine   >= 0.0f ) sine   =  0.000001f;
    if( sine   > -0.000001f && sine   <  0.0f ) sine   = -0.000001f;
    if( cosine <  0.000001f && cosine >= 0.0f ) cosine =  0.000001f;
    if( cosine > -0.000001f && cosine <  0.0f ) cosine = -0.000001f;

    float oneOverCosine = 1.0f / cosine;
    float oneOverSine   = 1.0f / sine;

    float xNext_x, xNext_y, xNext_l;
    float yNext_x, yNext_y, yNext_l;

    do
    {
        xNext_x = blockX * BLOCK_SIZE_F;
        if( cosine > 0 ) xNext_x += BLOCK_SIZE_F;
        xNext_x -= x;
        xNext_l = xNext_x * oneOverCosine;

        yNext_y = blockY * BLOCK_SIZE_F;
        if( sine > 0 ) yNext_y += BLOCK_SIZE_F;
        yNext_y -= y;
        yNext_l = yNext_y * oneOverSine;

        if( xNext_l < yNext_l )
        {
            yAxisWall = 1;
            xNext_y = xNext_l * sine;
            blockX += cosine > 0 ? 1.0f : -1.0f ;
            x += xNext_x;
            y += xNext_y;
        }
        else
        {
            yAxisWall = 0;
            yNext_x = yNext_l * cosine;
            blockY += sine > 0 ? 1.0f : -1.0f ;
            x += yNext_x;
            y += yNext_y;
        }

        result->textureId =  level[ (int)( blockX + MAP_WIDTH * blockY ) ] ;
    }
    while( result->textureId == 0 );



    result->targetBlockX = blockX;
    result->targetBlockY = blockY;

    if( yAxisWall ) {
        result->textureOffset = (int)( (int)( y * 64.0f / BLOCK_SIZE_F ) % 64 );
    } else {
        result->textureOffset = (int)( (int)( x * 64.0f / BLOCK_SIZE_F ) % 64 );
    }

    float dx = x - ox;
    float dy = y - oy;
    float dd = sqrt( dx*dx + dy*dy ) * cos( offsetAngle );

    result->sliceHeight = (int)( VISPLANEDIST_TIMES_WALLHEIGHT / dd );
}



void drawSlice( int screenX, struct SLICE *slice )
{
    int offsetY   = ( slice->sliceHeight >  64 ) ? ( 0 ) : ( 32 - ( slice->sliceHeight >> 1 ) );
    int overflowY = ( slice->sliceHeight <= 64 ) ? ( 0 ) : ( ( slice->sliceHeight - 64 ) >> 1 );
   int y;
    for(y = 0 ; y < slice->sliceHeight && y < 64 ; ++y )
    {
        if( GET_WALL_DOT( textures[ slice->textureId ] , slice->textureOffset , ( ( y + overflowY ) << 6 ) / slice->sliceHeight ) )
        {
            SET_PIXEL( screenX, offsetY + y );
        }
    }
}

int shouldInterpolate( struct SLICE *sliceA, struct SLICE *sliceB )
{
    if( sliceA->textureId != sliceB->textureId ) return 0;

    int xDiff = UNSIGNED_DIFF( sliceA->targetBlockX, sliceB->targetBlockX );
    int yDiff = UNSIGNED_DIFF( sliceA->targetBlockY, sliceB->targetBlockY );

    if( xDiff == 0 && yDiff <= 1 ) return 1;
    if( xDiff <= 1 && yDiff == 0 ) return 1;

    return 0;
}

void render()
{
   memset( screenBuffer.data, 0, 1024 ); 
   int x,y;

    struct SLICE sliceA, sliceB;
    struct SLICE sliceX[ 3 ];

    castRay( px, py, pa, 0, &sliceA );

    for( x = 4 ; x < 128 ; x += 4 )
    {
        castRay( px, py, pa, x, &sliceB );

        if( shouldInterpolate( &sliceA , &sliceB ) )
        {
            sliceX[1].textureId     = sliceA.textureId;
            sliceX[1].textureOffset = ( sliceA.textureOffset + sliceB.textureOffset ) >> 1 ;
            sliceX[1].sliceHeight   = ( sliceA.sliceHeight   + sliceB.sliceHeight   ) >> 1 ;
        }
        else castRay( px, py, pa, x - 4, &sliceX[1] );

        sliceX[0].textureId     =   sliceA.textureId;
        sliceX[0].textureOffset = ( sliceA.textureOffset + sliceX[1].textureOffset ) >> 1 ;
        sliceX[0].sliceHeight   = ( sliceA.sliceHeight   + sliceX[1].sliceHeight   ) >> 1 ;

        sliceX[2].textureId     =   sliceX[1].textureId;
        sliceX[2].textureOffset = ( sliceX[1].textureOffset + sliceB.textureOffset ) >> 1 ;
        sliceX[2].sliceHeight   = ( sliceX[1].sliceHeight   + sliceB.sliceHeight   ) >> 1 ;

        drawSlice( x - 3, &sliceX[0] );
        drawSlice( x - 2, &sliceX[1] );
        drawSlice( x - 1, &sliceX[2] );
        drawSlice( x,     &sliceB    );

        sliceA = sliceB;
    }

    //Draw pistol
    int i = 0;
    for(  y = 43 ; y < 64 ; ++y )
    for( x = 54 ; x < 74 ; ++x )
    {
        switch( sprite_pistol[ i++ ] )  {
            case 1: SET_PIXEL( x, y ); break;
            case 2: CLR_PIXEL( x, y ); break;
        }
    }

    // Send the data on the screen buffer to the LCD screen
    screenBuffer.data[1024]=1;
}



int main (int argc, char *argv[]){
   int fd, i;
    //unsigned char *map; //graphics memory map

   bytes_per_col = rows / 8;

   static struct termios oldt, newt;
   

   tcgetattr(STDIN_FILENO, &oldt);
   newt=oldt;
   newt.c_lflag &= ~(ICANON);
   tcsetattr(STDIN_FILENO,TCSANOW,&newt);
   
   //open mmap file
   fd = open(FILEPATH, O_RDWR);
   if (fd == -1){
      perror("Error opening file");
      exit(EXIT_FAILURE);
   }
   
   screenBuffer.data = mmap(0, FILESIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
   if (screenBuffer.data == MAP_FAILED){
      close(fd);
      perror("Error mmapping file");
      exit(EXIT_FAILURE);
   }

   //clear screen
   for (i=0;i<1024;i++){
      screenBuffer.data[i]=0x00;
   }
   screenBuffer.data[1024]=1;
   
   //wait for ready to send
   while (screenBuffer.data[1024]==1){
      usleep(1000);
   }
   char c[2]={0x00};
   render();
   while((c[0]=getchar())!= '-'){
      

      float dx = 0;
      float dy = 0;

      if( c[0]=='w' ) {
         dx =  5.0f*cos( pa );
         dy =  5.0f*sin( pa );
      }
      if( c[0]=='s' ) {
         dx = -5.0f*cos( pa );
         dy = -5.0f*sin( pa );
      }
      if( c[0]=='n') {
         dx =  5.0f*cos( pa - 3.14159265f / 2.0f );
         dy =  5.0f*sin( pa - 3.14159265f / 2.0f );
      }
      if( c[0]=='m' ) {
         dx = -5.0f*cos( pa - 3.14159265f / 2.0f );
         dy = -5.0f*sin( pa - 3.14159265f / 2.0f );
      }
      if(c[0]=='a' ) {
         pa -= 3.14159265f / 25.0f;
      }
      if( c[0]=='d' ) {
         pa += 3.14159265f / 25.0f;
      }

      px += dx;
      /*
      // Collision detection.  Still glitchy.
      uint16_t testA = (uint16_t)( ( px - HIT_WIDTH ) / BLOCK_SIZE_F );
      uint16_t testB = (uint16_t)( ( py - HIT_WIDTH ) / BLOCK_SIZE_F );
      uint16_t testC = (uint16_t)( ( py + HIT_WIDTH ) / BLOCK_SIZE_F );
      if( pgm_read_byte( &level[ testA + MAP_WIDTH * testB ] ) || pgm_read_byte( &level[ testA + MAP_WIDTH * testC ] ) ) {
         px = (float)( testA + 1 ) * BLOCK_SIZE_F + HIT_WIDTH;
      }

      testA = (uint16_t)( ( px + HIT_WIDTH ) / BLOCK_SIZE_F );
      if( pgm_read_byte( &level[ testA + MAP_WIDTH * testB ] ) || pgm_read_byte( &level[ testA + MAP_WIDTH * testC ] ) ) {
         px = (float)( testA ) * BLOCK_SIZE_F - HIT_WIDTH;
      }
      */
      py += dy;
      /*
      testA = (uint16_t)( ( py - HIT_WIDTH ) / BLOCK_SIZE_F );
      testB = (uint16_t)( ( px - HIT_WIDTH ) / BLOCK_SIZE_F );
      testC = (uint16_t)( ( px + HIT_WIDTH ) / BLOCK_SIZE_F );
      if( pgm_read_byte( &level[ testB + MAP_WIDTH * testA ] ) || pgm_read_byte( &level[ testC + MAP_WIDTH * testA ] ) ) {
         py = (float)( testA + 1 ) * BLOCK_SIZE_F + HIT_WIDTH;
      }

      testA = (uint16_t)( ( py + HIT_WIDTH ) / BLOCK_SIZE_F );
      if( pgm_read_byte( &level[ testB + MAP_WIDTH * testA ] ) || pgm_read_byte( &level[ testC + MAP_WIDTH * testA ] ) ) {
         py = (float)( testA ) * BLOCK_SIZE_F - HIT_WIDTH;
      }*/
      render();
   }
   tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
   return 0;
}

Ben Abra
 
Posts: 1
Joined: Tue Feb 09, 2016 8:37 pm

Re: OLED Graphics Display driver

Tue Feb 09, 2016 8:42 pm

I met the similar problem before ,but i sloved the problem with the oled supplier`s help
[url]panoxdisplay.com[/url]

Return to VoCore & VoCore+Dock

Who is online

Users browsing this forum: No registered users and 33 guests