0

I am currently using the pygame raycasting engine, pyray (https://github.com/oscr/PyRay/blob/master/pyray.py), and there are some questions I would like to ask:

1) How do you add a sprite which isn't an a cell, like the enemies in Wolfenstein 3D?

2) How do you make the walls images, instead of blocks of colour?

  • I think it is engine only to see how raycasting works, not engine to create game - it has no funtions to put sprites and to draw images. You would have to add this functionality on your own. – furas Nov 18 '17 at 08:11
  • I finished editing of my answer. – Spektre Nov 28 '17 at 15:55

1 Answers1

0
  1. Textures

    first see Ray Casting with different height size

    what you need is to change the scan line rendering of your engine to use textures. The C++ code in above link is just for starters (look for // render scan line in the code) and will do for the walls but ceiling and floor will have distortions present. To remedy that you need to apply perspective correct texture mapping.

    This is how it look like (after I implement it to the former code I linked)

    void Doom3D::draw_scanline(int sx,int sy0,int sy1,int sz0,int sz1,int symin,int tx0,int ty0,int tx1,int ty1,DWORD li)
        {
        //
        union { DWORD dd; BYTE db[4]; } cc;
        int Txs=txs*tn;
        if (sz0==sz1)   // affine texture mapping (front side of walls)
            {
            int sy,tx,ty,ktx,kty,dtx,dty,ctx,cty,dsy;
                   dsy=sy1-sy0; if (dsy<0)                        dsy=-dsy;
            ktx=0; dtx=tx1-tx0; if (dtx>0) ktx=+1; else { ktx=-1; dtx=-dtx; } tx=tx0; ctx=0;
            kty=0; dty=ty1-ty0; if (dty>0) kty=+1; else { kty=-1; dty=-dty; } ty=ty0; cty=0;
            if (dsy) for (sy=sy0;sy>=sy1;sy--)
                {
                if ((sy>=0)&&(sy<sys)&&(sy<=symin))
                 if ((tx>=0)&&(tx<Txs)&&(ty>=0)&&(ty<tys))
                    {
                    cc.dd=ptxr[ty][tx];
                    cc.db[0]=DWORD((DWORD(cc.db[0])*li)>>8);
                    cc.db[1]=DWORD((DWORD(cc.db[1])*li)>>8);
                    cc.db[2]=DWORD((DWORD(cc.db[2])*li)>>8);
                    pscr[sy][sx]=cc.dd;
                    }
                for (ctx+=dtx;ctx>=dsy;) { ctx-=dsy; tx+=ktx; }
                for (cty+=dty;cty>=dsy;) { cty-=dsy; ty+=kty; }
                }
            }
        else{           // perspective correct mapping (floor, top side of walls, ceiling)
            int sy,tx,ty,dsy,dtx,dty,_n,n,dn;
            int a,b,_z0,_z1;
            const int acc=15;
            dsy=sy1-sy0; n=abs(dsy); dn=n;
            dtx=tx1-tx0; n=abs(dtx); if (dn<n) dn=n;
            dty=ty1-ty0; n=abs(dty); if (dn<n) dn=n;
            if (sz0==0) return; _z0=(1<<acc)/sz0;
            if (sz1==0) return; _z1=(1<<acc)/sz1;
            if (dn) for (n=0;n<=dn;n++)
                {
                sy=sy0+((n*dsy)/dn);
                tx=tx0+((n*dtx)/dn);
                ty=ty0+((n*dty)/dn);
                // perspective correction (https://en.wikipedia.org/wiki/Texture_mapping)
                _n=dn-n;
                a=(_n*tx0*_z0) + (n*tx1*_z1);
                b=(_n    *_z0) + (n    *_z1); tx=a/b;
                a=(_n*ty0*_z0) + (n*ty1*_z1);
                b=(_n    *_z0) + (n    *_z1); ty=a/b;
                if ((sy>=0)&&(sy<sys)&&(sy<=symin))
                 if ((tx>=0)&&(tx<Txs)&&(ty>=0)&&(ty<tys))
                    {
                    cc.dd=ptxr[ty][tx];
                    cc.db[0]=DWORD((DWORD(cc.db[0])*li)>>8);
                    cc.db[1]=DWORD((DWORD(cc.db[1])*li)>>8);
                    cc.db[2]=DWORD((DWORD(cc.db[2])*li)>>8);
                    pscr[sy][sx]=cc.dd;
                    }
                }
            }
        }
    

    The sx,sy0,sy1 are screen coordinated of the scan line tx0,ty0,tx1,ty1 are the texture coordinates and l0,l1 are distances from camera. li is just light intensity. The texture coordinates are the fractional part of each grid cell hit coordinates. The texture used is atlas so the x coordinate encodes also which texture is used from it.

    On top of this you should also add mip maps (use texture resolution close to projected cell size) that will avoid the shimmering of pixels on distant walls while movement or turn around ...

  2. Sprites

    For each object compute its polar position relative to player. So you will have angle (relative to view direction) and distance. You can compute screen xdirectly from angle and screen y0,y1 from the distance. From that just render each scan line +/- the sprite width around computed position. You do not need perspective correct texture mapping for sprites as they are always perpendicular to player ...

    The computations needed are here: Theory behind Wolfenstein-style 3D rendering

Spektre
  • 49,595
  • 11
  • 110
  • 380