Last active
August 29, 2015 14:05
-
-
Save momorprods/7cb1739e8f8444b1b0e3 to your computer and use it in GitHub Desktop.
Raycasting renderer for Pythonista
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# RayCast | |
# Simple raycasting based renderer | |
# Touch the left part to rotate, and touch the right part to move forward | |
# Coded in a few hours during my holidays, July 2014, straight from my iPhone - thanks Pythonista !!! | |
# Feel free to upgrade ! | |
# Enjoy ! | |
# Emmanuel ICART | |
# eicart@momorprods.com | |
from scene import * | |
from math import * | |
# rendering step - 1=best(slower) | |
RENDERING_STEP=2 | |
# level data | |
# 1 = wall, 0 = empty | |
level = [[1,1,1,1,1,1,1,1], | |
[1,1,0,0,0,0,0,1], | |
[1,0,0,0,0,1,0,1], | |
[1,0,1,0,0,0,0,1], | |
[1,1,0,0,0,1,0,1], | |
[1,0,0,1,1,0,0,1], | |
[1,0,0,0,0,0,0,1], | |
[1,1,1,1,1,1,1,1]] | |
LX=len(level[0]) | |
LZ=len(level) | |
CellSize=128 | |
scan=10 | |
# player | |
xo=CellSize*LX/2 | |
zo=CellSize*LZ/2 | |
angle=45.0 | |
fov=80 | |
class RayCastScene (Scene): | |
def setup(self): | |
# preload the texture | |
self.texture='PC_Chest_Closed' | |
self.screenWidth=int(self.size.w) | |
self.screenHeight=int(self.size.h) | |
self.xTouchStart=0 | |
self.yTouchStart=0 | |
load_image(self.texture) | |
pass | |
def draw(self): | |
global angle | |
global xo | |
global zo | |
# This will be called for every frame (typically 60 times per second). | |
# clear background | |
background(0, 0, 0) | |
focale=0.5*self.screenWidth/tan(radians(fov/2)) | |
# compute each screen column | |
for column in xrange(0,self.screenWidth,RENDERING_STEP): | |
scan_angle=angle+((float(column)-self.screenWidth/2)*fov)/self.screenWidth | |
c=cos(radians(scan_angle)) | |
s=sin(radians(scan_angle)) | |
if abs(c)<0.001: | |
if c>0: | |
c=0.001 | |
else: | |
c=-0.001 | |
if abs(s)<0.001: | |
if s>0: | |
s=0.001 | |
else: | |
s=-0.001 | |
t2=s/c | |
t1=c/s | |
ok1=True | |
ok2=True | |
#Initialization of ray casting | |
pz1=t2*CellSize | |
if c>0: | |
px1=CellSize | |
ini=0 | |
else: | |
px1=-CellSize | |
pz1=-pz1 | |
ini=CellSize-1 | |
xp1=ini+(((int)(xo/CellSize))*CellSize) | |
zp1=zo+((xp1-xo)*pz1)/px1 | |
px2=t1*CellSize | |
if s>0: | |
pz2=CellSize | |
ini=0 | |
else: | |
pz2=-CellSize | |
px2=-px2 | |
ini=CellSize-1 | |
zp2=ini+(((int)(zo/CellSize))*CellSize) | |
xp2=xo+((zp2-zo)*px2)/pz2 | |
#****** cast a ray for z walls ****** | |
compteur=0 | |
while True: | |
xp1+=px1 | |
zp1+=pz1 | |
compteur+=1 | |
xd=(int)(xp1/CellSize) % LX | |
zd=(int)(zp1/CellSize) % LZ | |
if (xd<0): xd=0 | |
if (zd<0): zd=0 | |
if level[xd][zd]!=0 or compteur>=scan: break | |
if (compteur==scan):ok1=False | |
distance1=(xp1-xo)/c | |
col1=(zp1 % CellSize) | |
if (px1<=0): col1=CellSize-1-col1 | |
#****** cast a ray for x walls ****** | |
compteur=0 | |
while True: | |
xp2+=px2 | |
zp2+=pz2 | |
compteur+=1 | |
xd=(int)(xp2/CellSize) % LX | |
zd=(int)(zp2/CellSize) % LZ | |
if (xd<0):xd=0 | |
if (zd<0):zd=0 | |
if level[xd][zd]!=0 or compteur>=scan: break | |
if (compteur==scan): ok2=False | |
distance2=(zp2-zo)/s | |
col2=(xp2 % CellSize) | |
if (pz2>=0):col2=CellSize-1-col2 | |
#Choose the nearest wall (x or z) | |
if (distance1<distance2): | |
distance=1+(distance1) | |
colonne=col1 | |
else: | |
distance=1+(distance2) | |
colonne=col2 | |
if ok1 or ok2: | |
# fix the fishbowl effect | |
distance=distance*cos(radians(angle-scan_angle)) | |
#compute the wall screen height | |
hauteur = ((CellSize*focale)/distance) | |
# draw the column | |
ximage=(colonne*128)/CellSize # 101 x 171 tile | |
image(self.texture,column,(self.screenHeight-hauteur)/2,RENDERING_STEP,hauteur,ximage,0,RENDERING_STEP,171) | |
# display fingers | |
fill(1, 0, 0) | |
for touch in self.touches.values(): | |
ellipse(touch.location.x - 50, touch.location.y - 50, 100, 100) | |
# rotation control | |
if (touch.location.x<self.screenWidth/2): | |
angle += 0.04*(touch.location.x-self.xTouchStart) | |
else: | |
# displacement control | |
speed=(touch.location.y-self.yTouchStart)*0.2 | |
dx=speed*cos(radians(angle)) | |
dz=speed*sin(radians(angle)) | |
if level[int((xo+dx)/CellSize)][int(zo/CellSize)]!=0:dx=0 | |
if level[int(xo/CellSize)][int((zo+dz)/CellSize)]!=0:dz=0 | |
xo+=dx | |
zo+=dz | |
def touch_began(self, touch): | |
global angle | |
self.angleStart=angle | |
if touch.location.x<self.screenWidth/2:self.xTouchStart=touch.location.x | |
else: | |
self.yTouchStart=touch.location.y | |
pass | |
def touch_moved(self, touch): | |
pass | |
def touch_ended(self, touch): | |
pass | |
run(RayCastScene(),LANDSCAPE) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
If you copy this code to a Repo then I would be interested to make a few pull requests. It would be cool to try to add the
motion
module to this so that the scene would change without touching the screen as you change the yaw, pitch, and roll of your device. Your thoughts?