Author Topic: Nspire Raycaster  (Read 106875 times)

0 Members and 1 Guest are viewing this topic.

Offline DJ Omnimaga

  • Clacualters are teh gr33t
  • CoT Emeritus
  • LV15 Omnimagician (Next: --)
  • *
  • Posts: 55943
  • Rating: +3154/-232
  • CodeWalrus founder & retired Omnimaga founder
    • View Profile
    • Dream of Omnimaga Music
Re: Nspire Raycaster
« Reply #210 on: April 14, 2010, 02:29:23 pm »
The possibilities are endless!
For some reasons, I always get tempted to say Ndless instead of endless when it comes to Nspire stuff :P

Offline bwang

  • LV7 Elite (Next: 700)
  • *******
  • Posts: 634
  • Rating: +30/-11
    • View Profile
Re: Nspire Raycaster
« Reply #211 on: April 14, 2010, 05:56:36 pm »
New trivial update fixes the problems with sprites being slow when you get close.

Offline AaroneusTheGreat

  • Moderator
  • LV5 Advanced (Next: 300)
  • *****
  • Posts: 287
  • Rating: +26/-1
    • View Profile
Re: Nspire Raycaster
« Reply #212 on: April 14, 2010, 09:14:45 pm »
I'm not sure I'd call that trivial, good work! :)

Offline DJ Omnimaga

  • Clacualters are teh gr33t
  • CoT Emeritus
  • LV15 Omnimagician (Next: --)
  • *
  • Posts: 55943
  • Rating: +3154/-232
  • CodeWalrus founder & retired Omnimaga founder
    • View Profile
    • Dream of Omnimaga Music
Re: Nspire Raycaster
« Reply #213 on: April 14, 2010, 11:47:15 pm »
Nice to see it fixed :)

Btw, something I noticed when you reduce the screen resolution with * key: if you reduce it way too much, you can no longer resize it up again. In one occasion it resized horizontally only (two versions ago) and my Nspire screen was shifted about 10 or 15 pixels to the left, even outside the raycaster and it lasted until next reboot. And with the previous version when resizing back once the calc rebooted.

Offline bwang

  • LV7 Elite (Next: 700)
  • *******
  • Posts: 634
  • Rating: +30/-11
    • View Profile
Re: Nspire Raycaster
« Reply #214 on: April 15, 2010, 01:07:45 am »
The resolution changer multiplies the dimensions by 0.9 and then casts them to integers. After a while, the dimensions drop to 0. When you scale them back up again, they stay zero.

The weird problems are probably caused by the dimensions going out of bounds. I'll look at that.

Offline DJ Omnimaga

  • Clacualters are teh gr33t
  • CoT Emeritus
  • LV15 Omnimagician (Next: --)
  • *
  • Posts: 55943
  • Rating: +3154/-232
  • CodeWalrus founder & retired Omnimaga founder
    • View Profile
    • Dream of Omnimaga Music
Re: Nspire Raycaster
« Reply #215 on: April 15, 2010, 01:36:22 am »
aaah Ok ^^

Offline phj

  • LV1 Newcomer (Next: 20)
  • *
  • Posts: 8
  • Rating: +1/-0
    • View Profile
Re: Nspire Raycaster
« Reply #216 on: April 15, 2010, 01:54:20 pm »
The sprites disappear when you 're behind an invisible wall. The other walls don't.

Offline Builderboy

  • Physics Guru
  • CoT Emeritus
  • LV13 Extreme Addict (Next: 9001)
  • *
  • Posts: 5673
  • Rating: +613/-9
  • Would you kindly?
    • View Profile
Re: Nspire Raycaster
« Reply #217 on: April 15, 2010, 01:59:00 pm »
Bwang how are you managing the clipping for the sprites?  Do you have a full z buffer or what?

Offline DJ Omnimaga

  • Clacualters are teh gr33t
  • CoT Emeritus
  • LV15 Omnimagician (Next: --)
  • *
  • Posts: 55943
  • Rating: +3154/-232
  • CodeWalrus founder & retired Omnimaga founder
    • View Profile
    • Dream of Omnimaga Music
Re: Nspire Raycaster
« Reply #218 on: April 15, 2010, 04:34:09 pm »
invisible wall. The other walls don't.
There are invisible walls?

Offline bwang

  • LV7 Elite (Next: 700)
  • *******
  • Posts: 634
  • Rating: +30/-11
    • View Profile
Re: Nspire Raycaster
« Reply #219 on: April 15, 2010, 05:33:00 pm »
My Z-Buffer is still 1-D; that's why the sprites disappear.
I think "invisible wall" means a wall that is intersected by a ray, but is off-screen.

Offline DJ Omnimaga

  • Clacualters are teh gr33t
  • CoT Emeritus
  • LV15 Omnimagician (Next: --)
  • *
  • Posts: 55943
  • Rating: +3154/-232
  • CodeWalrus founder & retired Omnimaga founder
    • View Profile
    • Dream of Omnimaga Music
Re: Nspire Raycaster
« Reply #220 on: April 15, 2010, 05:40:29 pm »
oooh ok, maybe he meant that. I'm not too familiar with the 3D/raycasting stuff

Offline phj

  • LV1 Newcomer (Next: 20)
  • *
  • Posts: 8
  • Rating: +1/-0
    • View Profile
Re: Nspire Raycaster
« Reply #221 on: April 16, 2010, 12:35:04 pm »
I mean that, because the variable wall height only applies to graphics, the rest of the wall is invisible and as far I know the walls are infinitely high so a part of every wall is invisible.

Offline bwang

  • LV7 Elite (Next: 700)
  • *******
  • Posts: 634
  • Rating: +30/-11
    • View Profile
Re: Nspire Raycaster
« Reply #222 on: April 16, 2010, 12:37:34 pm »
For some reason, if I make my Z-Buffer a 320x240 array of floats, trying to write anything into it crashes the calculator. Anyone have any ideas why?

phj: no, the walls are not infinitely high internally.

EDIT: This is odder than I thought. Even if I manually check whether the array indices are in-bounds (if (x < w && y < h)...), the program still crashes.

EDIT AGAIN: I had the same problem a while back with a 320x240 array of ints. I also had the same problem with a 320 * 240 1-dimensional array of ints. Should I post source code?
« Last Edit: April 16, 2010, 01:16:44 pm by bwang »

Offline DJ Omnimaga

  • Clacualters are teh gr33t
  • CoT Emeritus
  • LV15 Omnimagician (Next: --)
  • *
  • Posts: 55943
  • Rating: +3154/-232
  • CodeWalrus founder & retired Omnimaga founder
    • View Profile
    • Dream of Omnimaga Music
Re: Nspire Raycaster
« Reply #223 on: April 16, 2010, 05:03:28 pm »
You may want to post the source in case certain people may be able to help. SirCmpwn did stuff with C-like languages before and Critor did the java raycaster

Offline bwang

  • LV7 Elite (Next: 700)
  • *******
  • Posts: 634
  • Rating: +30/-11
    • View Profile
Re: Nspire Raycaster
« Reply #224 on: April 16, 2010, 05:43:29 pm »
Source:
Code: [Select]
#include <os.h>
#include "utils.h"
#include "raymath.h"
#include "sort.h"
#include "sprites.h"
#include "config.h"

asm(".string \"PRG\"\n");

int main(void)
{
  *(volatile unsigned*) 0x900B0000 = 0x00000002;
  *(volatile unsigned*) 0x900B000C = 4;

  struct Sprite sprite[numSprites] =
  {
    {12, 12},
    {13, 12},
    {14, 12},
    {15, 12},
  };

  int map[mapWidth][mapHeight]=
  {
  {0,0,0,0,0,0,0,0,0,0,0,0,0,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,0},
  {0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0},
  {0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0},
  {0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0},
  {0,1,0,0,0,0,0,1,1,1,1,1,0,0,0,0,2,2,2,2,2,0,0,0,1,0},
  {0,1,0,0,0,0,0,1,0,0,0,1,0,0,0,0,2,0,0,0,2,0,0,0,1,0},
  {0,1,0,0,0,0,0,1,0,0,0,1,0,0,0,0,2,0,0,0,0,0,0,0,1,0},
  {0,1,0,0,0,0,0,1,0,0,0,1,0,0,0,0,2,0,0,0,2,0,0,0,1,0},
  {0,1,0,0,0,0,0,1,1,0,1,1,0,0,0,0,2,2,2,2,2,0,0,0,1,0},
  {0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0},
  {0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0},
  {0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0},
  {0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0},
  {0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0},
  {0,1,0,0,0,0,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0},
  {0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0},
  {0,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0},
  {0,1,1,0,1,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0},
  {0,1,1,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0},
  {0,1,1,0,1,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0},
  {0,1,1,0,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0},
  {0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0},
  {0,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,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,0},
  {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
  };

  float height[mapWidth][mapHeight]=
  {
  {0,0,0,0,0,0,0,0,0,0,0,0,0,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,0},
  {0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0},
  {0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0},
  {0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0},
  {0,1,0,0,0,0,0,1,1,1,1,1,0,0,0,0,2,1,2,1,2,0,0,0,1,0},
  {0,1,0,0,0,0,0,1,0,0,0,1,0,0,0,0,1,0,0,0,1,0,0,0,1,0},
  {0,1,0,0,0,0,0,1,0,0,0,1,0,0,0,0,2,0,0,0,0,0,0,0,1,0},
  {0,1,0,0,0,0,0,1,0,0,0,1,0,0,0,0,1,0,0,0,1,0,0,0,1,0},
  {0,1,0,0,0,0,0,1,1,0,1,1,0,0,0,0,2,1,2,1,2,0,0,0,1,0},
  {0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0},
  {0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0},
  {0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0},
  {0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0},
  {0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0},
  {0,1,0,0,0,0,1,0.8,0.6,0.4,0.2,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0},
  {0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0},
  {0,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0},
  {0,1,1,0,1,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0},
  {0,1,1,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0},
  {0,1,1,0,1,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0},
  {0,1,1,0,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0},
  {0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0},
  {0,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,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,0},
  {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
  };

  char* scrbuf = (char*) malloc(SCREEN_BYTES_SIZE);
  int tex[texW][texH]; 
  int u, v;
  for (u = 0; u < texW; u++) {
    for (v = 0; v < texH; v++) {
      tex[u][v] = 6;
    }
  }

  for (u = 0; u < texW; u++) {
    for (v = 0; v < texH; v++) {
      if (u % 4 == 3 || (v % 8 == 2 && ((u / 4) % 2) == 0) ||
         (v % 8 == 6 && ((u / 4) % 2) == 1)) {
        tex[u][v] = 15;
      }
    }
  }

  int tex2[texW][texH];
  for (u = 0; u < texW; u++) {
    for (v = 0; v < texH; v++) {
      tex2[u][v] = (u ^ v) / 2;
    }
  }

  int texSpr[texW][texH];
  for (u = 0; u < texW; u++) {
    for (v = 0; v < texH; v++) {
      int dsq = (u - 16) * (u - 16) + (v - 16) * (v - 16);
      if (dsq > 196 && dsq < 256) {
        texSpr[u][v] = 0;
      } else if (dsq < 196 && dsq > 64) {
        texSpr[u][v] = 8;
      } else if (dsq < 64 && dsq > 49) {
        texSpr[u][v] = 0;
      } else {
        texSpr[u][v] = -1;
      }
    }
  }
 
  float posX = 12, posY = 12; 
  float dirX = -1, dirY = 0;
  float planeX = 0, planeY = 0.66;
  int w = 320, h = 240;
  int res = 1;
  int x, y;
  float zbuf[w][h];
  int spriteOrder[numSprites];
  float spriteDistance[numSprites];
  int redraw;
  float heye = 0.5;

  while (!isKeyPressed(KEY_NSPIRE_ESC)) {
    redraw = 0;
    if (isKeyPressed(KEY_NSPIRE_LEFT)) {
      float oldDirX = dirX;
      dirX = dirX * cos1 - dirY * sin1;
      dirY = oldDirX * sin1 + dirY * cos1;
      float oldPlaneX = planeX;
      planeX = planeX * cos1 - planeY * sin1;
      planeY = oldPlaneX * sin1 + planeY * cos1;
      redraw = 1;
    }
    if (isKeyPressed(KEY_NSPIRE_RIGHT)) {
      float oldDirX = dirX;
      dirX = dirX * cos1 + dirY * sin1;
      dirY = -oldDirX * sin1 + dirY * cos1;
      float oldPlaneX = planeX;
      planeX = planeX * cos1 + planeY * sin1;
      planeY = -oldPlaneX * sin1 + planeY * cos1;
      redraw = 1;
    }
    if (isKeyPressed(KEY_NSPIRE_UP)) {
      if (map[(int) (posX + stpsize * dirX)][(int) (posY)] == 0)
        posX += stpsize * dirX;
      if (map[(int) (posX)][(int) (posY + stpsize * dirY)] == 0)
        posY += stpsize * dirY;
      if (posX < 0) posX = 0;
      if (posX > 24) posX = 24;
      if (posY < 0) posY = 0;
      if (posY > 24) posY = 24;
      redraw = true;
    }
    if (isKeyPressed(KEY_NSPIRE_DOWN)) {
      if (map[(int) (posX - stpsize * dirX)][(int) (posY)] == 0)
        posX -= stpsize * dirX;
      if (map[(int) (posX)][(int) (posY - stpsize * dirY)] == 0)
        posY -= stpsize * dirY;
      if (posX < 0) posX = 0;
      if (posX > 24) posX = 24;
      if (posY < 0) posY = 0;
      if (posY > 24) posY = 24;
      redraw = true;
    }
    if (isKeyPressed(KEY_NSPIRE_MINUS)) {
      heye -= 0.1;
      redraw = true;
    }
    if (isKeyPressed(KEY_NSPIRE_PLUS)) {
      heye += 0.1;
      redraw = true;
    }
    if (isKeyPressed(KEY_NSPIRE_DIVIDE)) {
      w = (int) (w * 1.1);
      if (w > 320) w = 320;
      h = (int) (h * 1.1);
      if (h > 240) h = 240;
      redraw = true;
    }
    if (isKeyPressed(KEY_NSPIRE_MULTIPLY)) {
      w = (int) (w * 0.9);
      h = (int) (h * 0.9);
      redraw = true;
    }
    if (isKeyPressed(KEY_NSPIRE_H)) {
      res = 1;
      redraw = true;
    }
    if (isKeyPressed(KEY_NSPIRE_L)) {
      res = 2;
      redraw = true;
    }
    if (redraw) {
      memset(scrbuf, 0x00, SCREEN_BYTES_SIZE);
      for(x = 0; x < w; x += res) {
        float rayPosX = posX;
        float rayPosY = posY;
        int mapX = (int) rayPosX;
        int mapY = (int) rayPosY;
        float cameraX = 2 * x / (float) (w) - 1;
        float raydx = dirX + planeX * cameraX;
        float raydy = dirY + planeY * cameraX;       
        float sideDistX;
        float sideDistY;
        float raydx2 = raydx * raydx, raydy2 = raydy * raydy;
        float deltaDistX = sqrt(1 + raydy2 / raydx2);
        float deltaDistY = sqrt(1 + raydx2 / raydy2);
        float perpWallDist;
           
        int stepX;
        int stepY;
        int side;
        float curH = 0;
        int scrtop = h - 1;
     
        if (raydx < 0) {
          stepX = -1;
          sideDistX = (rayPosX - mapX) * deltaDistX;
        } else {
          stepX = 1;
          sideDistX = (mapX + 1.0 - rayPosX) * deltaDistX;
        }
        if (raydy < 0) {
          stepY = -1;
          sideDistY = (rayPosY - mapY) * deltaDistY;
        } else {
          stepY = 1;
          sideDistY = (mapY + 1.0 - rayPosY) * deltaDistY;
        }
        int first = true;
        while (mapX > 0 && mapX < mapWidth && mapY > 0 && mapY < mapHeight ) {
          if (sideDistX < sideDistY) {
            sideDistX += deltaDistX;
            mapX += stepX;
            side = 0;
          } else {
            sideDistY += deltaDistY;
            mapY += stepY;
            side = 1;
          }
          if (!map[mapX][mapY]) continue;
          if (scrtop < 0) break;
          float hwall = height[mapX][mapY];

          if (side == 0) {
            perpWallDist = fabs((mapX - rayPosX + (1 - stepX) / 2) / raydx);
          } else {
            perpWallDist = fabs((mapY - rayPosY + (1 - stepY) / 2) / raydy);
          }

          int nextX;
          int nextY;
          int nextSide;
          if (sideDistX < sideDistY) {
            nextX = mapX + stepX;
            nextSide = 0;
          } else {
            nextY = mapY + stepY;
            nextSide = 1;
          }
           
          float nextD;
          if (nextSide == 0) {
            nextD = fabs((nextX - rayPosX + (1 - stepX) / 2) / raydx);
          } else {
            nextD = fabs((nextY - rayPosY + (1 - stepY) / 2) / raydy);
          }

          int lo = (int) (h * (0.5 - heye / perpWallDist));
          int lw = (int) (h * hwall / perpWallDist);
         
          int nlo = (int) (h * (0.5 - heye / nextD));
          int nlw = (int) (h * hwall / nextD);

          int fstart = h - nlo - nlw;
          int fend = h - lo - lw - 1;
          int wstart = h - lo - lw;
          int wend = h - lo - 1;

          float wallX;
          if (side == 1) {
            wallX = rayPosX + ((mapY - rayPosY + (1 - stepY) / 2) / raydy) * raydx;
          } else {       
            wallX = rayPosY + ((mapX - rayPosX + (1 - stepX) / 2) / raydx) * raydy;
          }
          wallX -= (int) wallX;
          int texX = (int) (wallX * (float) (texW));
          if(side == 0 && raydx > 0) texX = texW - texX - 1;
          if(side == 1 && raydy < 0) texX = texW - texX - 1;
         
          //floor loop
          int y;
          int sfstart = max(fstart, 0);
          int sfend = min(fend, scrtop);
          for (y = sfstart; y <= sfend; y++) {
            setPixelBuf(scrbuf, x, y, 8);
            //very large value since sprites always cover floors
            zbuf[x][y] = 1000;
            if (res == 2 && x < w - 1) {
              setPixelBuf(scrbuf, x + 1, y, 8);
              zbuf[x + 1][y] = 1000;
            }
          }
          //wall loop
          float texY = 0;
          float texD = ((float)texH) / ((float)lw);
          int swstart = max(wstart, 0);
          if (swstart == 0) texY += (-wstart) * texD;
          int swend = min(wend, scrtop);
          for (y = swstart; y <= swend; y++) {
            int color = tex[(int) texY][texX];
            if (side == 1) color = color / 2;
            setPixelBuf(scrbuf, x, y, color);
            //set the zbuffer
            zbuf[x][y] = perpWallDist;
            if (res == 2 && x < w - 1) {
              setPixelBuf(scrbuf, x + 1, y, color);
              zbuf[x + 1][y] = perpWallDist;
            }
            texY += texD;
          }
          int oldtop = scrtop;
          scrtop = fstart - 1;
          if (scrtop > wstart - 1) scrtop = wstart;
          if (scrtop > oldtop) scrtop = oldtop;
          curH = hwall;
        }
      }
      int i;
      for(i = 0; i < numSprites; i++)
      {
        spriteOrder[i] = i;
        spriteDistance[i] = ((posX - sprite[i].x) * (posX - sprite[i].x)
                           + (posY - sprite[i].y) * (posY - sprite[i].y));
      }
      combSort(spriteOrder, spriteDistance, numSprites);
       
      for(i = 0; i < numSprites; i++)
      {
        float spriteX = sprite[spriteOrder[i]].x - posX;
        float spriteY = sprite[spriteOrder[i]].y - posY;
           
        float invDet = 1.0 / (planeX * dirY - dirX * planeY);
       
        float transformX = invDet * (dirY * spriteX - dirX * spriteY);
        float transformY = invDet * (-planeY * spriteX + planeX * spriteY);       
             
        int spriteScreenX = (int) ((w / 2) * (1 + transformX / transformY));
       
        #define uDiv 1
        #define vDiv 1
        #define vMove 0.0

        int vMoveScreen = (int) (vMove / transformY);
       
        int spriteHeight = abs((int) (h / transformY)) / vDiv;
        int lo = (int) (h * (0.5 - heye / transformY));
        int drawStartY = h - lo - spriteHeight;
        int drawEndY = h - lo;
        if (drawEndY > h - 1) drawEndY = h - 1;
        int texOff = 0;
        if (drawStartY < 0) {
          texOff = -drawStartY;
          drawStartY = 0;
        }

        int spriteWidth = abs((int) (h / (transformY))) / uDiv;
        int drawStartX = -spriteWidth / 2 + spriteScreenX;
        if(drawStartX < 0) drawStartX = 0;
        int drawEndX = spriteWidth / 2 + spriteScreenX;
        if(drawEndX >= w) drawEndX = w - 1;
       
        int stripe;
        float texD = ((float)texH) / ((float)spriteHeight);
        texOff *= texD;
        for(stripe = drawStartX; stripe < drawEndX; stripe += res)
        {
          int texX = (int) (256 * (stripe - (-spriteWidth / 2 + spriteScreenX)) * texW / spriteWidth) / 256;
          float texY = texOff;
          if(transformY > 0 && stripe > 0 && stripe < w) {
            for(y = drawStartY; y < drawEndY; y++)
            {
              if (y >= 0 && y <= h - 1) {
                if (transformY >= zbuf[stripe][y]) continue;
                int color = texSpr[(int)texY][texX];
                if (color != -1) {
                  setPixelBuf(scrbuf, stripe, y, color);
                  if (res == 2 && stripe < w - 1) {
                    setPixelBuf(scrbuf, stripe + 1, y, color);
                  }
                }
              }
              texY += texD;
            }
          }
        }
      }
      refresh(scrbuf);
    }
  }

  *(volatile unsigned*) 0x900B0000 = 0x00141002;
  *(volatile unsigned*) 0x900B000C = 4;
  return 0;
}