A fun thing I made.
A magical portal, yesterday.
Video
Please find project file with code, executable and media attatched as a self extracting zip file. Here is the code for quick reference:
Rem Project: spherical portal thingy
Rem Created: 29/03/2010 20:36:16
Rem ***** Main Source File *****
`#constant level_obj 1
#constant ref1_obj 2
#constant ref2_obj 3
#constant ball1_obj 4
#constant ball2_obj 5
#constant box1_obj 6
#constant box2_obj 7
#constant level_obj1 8
#constant level_obj2 9
#constant level_obj3 10
#constant level_obj4 11
#constant ref1u_obj 13
#constant ref1d_obj 14
#constant ref1l_obj 15
#constant ref1r_obj 16
#constant level1_tex 1
#constant level2_tex 2
#constant level1_lm 3
#constant level2_lm 4
#constant ref1_tex 5
#constant ref2_tex 6
#constant bg1_tex 7
#constant bg2_tex 8
#constant ref1u_tex 6
#constant ref1d_tex 7
#constant ref1l_tex 8
#constant ref1r_tex 9
set display mode 800,600,32
`set display mode 640,480,32 `good for fraps FOR YOUTUBE VID CAPTURE
sync on
`sync rate 0
sync rate 100 `?? not sure what sense has with many cameras. presumably 2 sync calls (one multicam) means actual framerate halved?
set camera aspect 1.667
`set camera aspect 1.333 `FOR YOUTUBE VID CAPTURE
hide mouse
#constant portal_radius 0.2
#constant portal_radius_b 0.2025
` a fudge. get teleport to happen a tiny bit before get to sphere surface.
`to get around near camera plane clipping problem
dim portalpos#(2,3)
portalpos#(1,1) =-10
portalpos#(1,2) =1
portalpos#(1,3) =0
portalpos#(2,1) =10
portalpos#(2,2) =1
portalpos#(2,3) =0
`CARTOGRAPHY SHOP STUFF!!
load object "wierd cylinder/wierd_cylinder.x",level_obj1
load object "wierd cylinder/wierd_cylinder_lm.x",level_obj2 `a
ghost object on level_obj2,1
`load object "wierd cylinder/wierd_cylinder_snow.x",level_obj3 `as above, just different textures.
`load object "wierd cylinder/wierd_cylinder_lmx.x",level_obj4 `""
clone object level_obj3,level_obj1
clone object level_obj4,level_obj2
ghost object on level_obj4,1
set ambient light 100
load image "wierd cylinder/AlgaeBricks.jpg",level1_tex :texture object level_obj1,level1_tex
load image "wierd cylinder/SnowBricks.jpg",level2_tex :texture object level_obj3,level2_tex
load image "wierd cylinder/wierd_cylinder_1a.jpg",level1_lm :texture object level_obj2,level1_lm
load image "wierd cylinder/wierd_cylinder_1x.jpg",level2_lm :texture object level_obj4,level2_lm `mixed up #s
sf#=0.2
scale object level_obj1,sf#,sf#,sf#
scale object level_obj2,sf#,sf#,sf#
scale object level_obj3,sf#,sf#,sf#
scale object level_obj4,sf#,sf#,sf#
position object level_obj1,portalpos#(1,1),portalpos#(1,2),portalpos#(1,3)
position object level_obj2,portalpos#(1,1),portalpos#(1,2),portalpos#(1,3)
position object level_obj3,portalpos#(2,1),portalpos#(2,2),portalpos#(2,3)
position object level_obj4,portalpos#(2,1),portalpos#(2,2),portalpos#(2,3)
yrotate object level_obj3,90
yrotate object level_obj4,90
`put spheres at portal points.
`in ms3d, portals are at x=+/- 10
make object sphere ball1_obj,portal_radius*2.0 * 0.96 `0.96 is to make smaller than covering projected cam image
make object sphere ball2_obj,portal_radius*2.0 * 0.96
load object "8by8.x",ref1_obj
`clone object ref2_obj,ref1_obj
clone object ref1u_obj,ref1_obj
clone object ref1d_obj,ref1_obj
clone object ref1l_obj,ref1_obj
clone object ref1r_obj,ref1_obj
position object ball1_obj,portalpos#(1,1),portalpos#(1,2),portalpos#(1,3)
position object ball2_obj,portalpos#(2,1),portalpos#(2,2),portalpos#(2,3)
color object ball1_obj,rgb(255,0,250)
color object ball2_obj,rgb(0,255,250)
position object ref1_obj,portalpos#(1,1),portalpos#(1,2),portalpos#(1,3)
`position object ref2_obj,portalpos#(2,1),portalpos#(2,2),portalpos#(2,3)
position object ref1u_obj,portalpos#(1,1),portalpos#(1,2),portalpos#(1,3)
position object ref1d_obj,portalpos#(1,1),portalpos#(1,2),portalpos#(1,3)
position object ref1l_obj,portalpos#(1,1),portalpos#(1,2),portalpos#(1,3)
position object ref1r_obj,portalpos#(1,1),portalpos#(1,2),portalpos#(1,3)
`test have loaded it right!
position camera 0,10,-20
point camera 0,0,0
`make some more cameras
make camera 1
`make camera 2
make camera 3 `up camera
make camera 4 `down
make camera 5 `left
make camera 6 `right
`set camera to image 1,ref1_tex,256,256
set camera to image 1,ref1_tex,512,512
`set camera to image 2,ref2_tex,512,512
set camera to image 3,ref1u_tex,256,256
set camera to image 4,ref1d_tex,256,256
set camera to image 5,ref1l_tex,256,256
set camera to image 6,ref1r_tex,256,256
remstart
set camera to image 3,ref1u_tex,128,128
set camera to image 4,ref1d_tex,128,128
set camera to image 5,ref1l_tex,128,128
set camera to image 6,ref1r_tex,128,128
remend
remstart
set camera to image 3,ref1u_tex,64,64
set camera to image 4,ref1d_tex,64,64
set camera to image 5,ref1l_tex,64,64
set camera to image 6,ref1r_tex,64,64
remend
set camera aspect 1,1 `think should be default anyway
`set camera aspect 2,1
set camera aspect 3,1
set camera aspect 4,1
set camera aspect 5,1
set camera aspect 6,1
set camera fov 1,90
`set camera fov 2,90
set camera fov 3,90
set camera fov 4,90
set camera fov 5,90
set camera fov 6,90
texture object ref1_obj,ref1_tex
`texture object ref2_obj,ref2_tex
texture object ref1u_obj,ref1u_tex
texture object ref1d_obj,ref1d_tex
texture object ref1l_obj,ref1l_tex
texture object ref1r_obj,ref1r_tex
set object texture ref1_obj,2,0
`set object texture ref2_obj,2,0
set object texture ref1u_obj,2,0
set object texture ref1d_obj,2,0
set object texture ref1l_obj,2,0
set object texture ref1r_obj,2,0
sf#=portal_radius*100.0
`sf#=portal_radius*98.0 `fudge to avoid near camrange clipping. better if changed tecture mapping also, and/or squished
`mesh, so appears as should do.
scale object ref1_obj,sf#,sf#,sf#
`scale object ref2_obj,sf#,sf#,sf#
scale object ref1u_obj,sf#,sf#,sf#
scale object ref1d_obj,sf#,sf#,sf#
scale object ref1l_obj,sf#,sf#,sf#
scale object ref1r_obj,sf#,sf#,sf#
set object light ref1_obj,0
`set object light ref2_obj,0
set object light ref1u_obj,0
set object light ref1d_obj,0
set object light ref1l_obj,0
set object light ref1r_obj,0
set camera range 0,0.002,40.0
set camera range 1,0.05,40.0
`set camera range 2,0.05,50.0
set camera range 3,0.05,40.0
set camera range 4,0.05,40.0
set camera range 5,0.05,40.0
set camera range 6,0.05,40.0
`set camera fov 1,90 `as default i think. also aspect should be 1 as expect.
dim vertxpos#(100)
dim vertypos#(100)
lock vertexdata for limb ref1_obj,2,1 `bizzarely, limb 2 has the vertices! should remesh.
n=GET VERTEXDATA VERTEX COUNT()
for i=0 to n `not sure whether is 0 to 80 or 1 to 81
vertxpos#(i)=get vertexdata position x(i)
vertypos#(i)=get vertexdata position y(i)
next i
unlock vertexdata
fmodel=1
`for cshop new levels.
`should use a skybox or something.
backdrop on 1
`backdrop on 2
backdrop on 3
backdrop on 4
backdrop on 5
backdrop on 6
skyc=rgb(160,160,160)
color backdrop 1,skyc
`color backdrop 2,skyc
color backdrop 3,skyc `rgb(0,255,0) `up cam. test
color backdrop 4,skyc `rgb(0,255,0)
color backdrop 5,skyc
color backdrop 6,skyc
color backdrop 0,skyc
`start pos near one portal
position camera 0,-10.5,2.5,-1.0
yr#=20.0
xr#=40.0
do
mc=mouseclick()
cv#=0.999*cv#+0.0001*( 2*(mc&&1 ) - (mc&&2) )
if mc=0 then cv#=0.85*cv#
move camera cv#
yr#=wrapvalue(yr#+0.15*mousemovex())
xr#=xr#+0.15*mousemovey()
if xr#>85.0 then xr#=85.0
if xr#<-85.0 then xr#=-85.0
rotate camera xr#,yr#,0.0
cx#=camera position x(0)
cy#=camera position y(0)
cz#=camera position z(0)
`actual doing the portal.
rsq#=cz#*cz#+(cy#-1.0)^2.0
IF cx#<0.0
totsq#=rsq#+(cx#+10.0)^2.0
if totsq# < portal_radius_b*portal_radius_b
test1=1
ratio#=(portal_radius_b*portal_radius_b)/totsq#
cz#=-cz#*ratio#
cy#=-(cy#-1.0)*ratio# +1.0
cx#=-(cx#+10.0)*ratio# + 10.0
endif
position camera 0,cx#,cy#,cz#
ELSE
totsq#=rsq#+(cx#-10.0)^2.0
if totsq# < portal_radius_b*portal_radius_b
test2=1
ratio#=(portal_radius_b*portal_radius_b)/totsq#
cz#=-cz#*ratio#
cy#=-(cy#-1.0)*ratio# +1.0
cx#=-(cx#-10.0)*ratio# - 10.0
endif
position camera 0,cx#,cy#,cz#
ENDIF
`reposition things, because now putting portal view objects on NEAREST portal.
`a better system would be to have levels of detail for different resolutions, and to refresh distant portal cam
`views once every few frames. for now make do with more limited system
if cx#<0.0
in_portal=1
out_portal=2
else
in_portal=2
out_portal=1
endif
position object ref1_obj,portalpos#(in_portal,1),portalpos#(in_portal,2),portalpos#(in_portal,3)
`position object ref2_obj,portalpos#(2,1),portalpos#(2,2),portalpos#(2,3)
position object ref1u_obj,portalpos#(in_portal,1),portalpos#(in_portal,2),portalpos#(in_portal,3)
position object ref1d_obj,portalpos#(in_portal,1),portalpos#(in_portal,2),portalpos#(in_portal,3)
position object ref1l_obj,portalpos#(in_portal,1),portalpos#(in_portal,2),portalpos#(in_portal,3)
position object ref1r_obj,portalpos#(in_portal,1),portalpos#(in_portal,2),portalpos#(in_portal,3)
`position camera 2,portalpos#(1,1),portalpos#(1,2),portalpos#(1,3) `SWITCHED CAM POSITIONS TO MAKE PORTAL
position camera 1,portalpos#(out_portal,1),portalpos#(out_portal,2),portalpos#(out_portal,3)`point camera 1,cx#,cy#,cz#
position camera 3,portalpos#(out_portal,1),portalpos#(out_portal,2),portalpos#(out_portal,3) `up
position camera 4,portalpos#(out_portal,1),portalpos#(out_portal,2),portalpos#(out_portal,3) `up
position camera 5,portalpos#(out_portal,1),portalpos#(out_portal,2),portalpos#(out_portal,3) `up
position camera 6,portalpos#(out_portal,1),portalpos#(out_portal,2),portalpos#(out_portal,3) `up
`point camera 2,cx#,cy#,cz#
point object ref1_obj,cx#,cy#,cz#
point object ref1u_obj,cx#,cy#,cz# `same orientation. - recalc prob unnecessary. maybe quicker to use rotate x,y,z
point object ref1d_obj,cx#,cy#,cz# `same orientation.
point object ref1l_obj,cx#,cy#,cz#
point object ref1r_obj,cx#,cy#,cz#
`point object ref2_obj,cx#,cy#,cz#
turn object right ref1_obj,180
`turn object right ref2_obj,180
turn object right ref1u_obj,180
turn object right ref1d_obj,180
turn object right ref1l_obj,180
turn object right ref1r_obj,180
set camera to object orientation 1,ref1_obj `:turn camera right 1,180 `SHOULD MOVE THIS -
`set camera to object orientation 2,ref2_obj `:turn camera right 2,180 `OR RENDER OTHER CAMS BEFORE 0
set camera to object orientation 3,ref1u_obj `same orientation as ref1_obj
set camera to object orientation 4,ref1d_obj `same orientation as ref1_obj
set camera to object orientation 5,ref1d_obj
set camera to object orientation 6,ref1d_obj
dx#=portalpos#(in_portal,1) - cx#
dy#=portalpos#(in_portal,2) - cy#
dz#=portalpos#(in_portal,3) - cz#
v1#=sqrt(dx#*dx#+dy#*dy#+dz#*dz#)
`dx#=portalpos#(2,1) - cx#
`dy#=portalpos#(2,2) - cy#
`dz#=portalpos#(2,3) - cz#
`v2#=sqrt(dx#*dx#+dy#*dy#+dz#*dz#)
vs1#=v1#/portal_radius
`vs2#=v2#/portal_radius
if keystate(2) then fmodel=1
if keystate(3) then fmodel=2
if fmodel=1
f1#=vs1#/(2.0*vs1#-1.0)
`f2#=vs2#/(2.0*vs2#-1.0)
else
f1#=1.0/vs1# `simple model. better is v/(2v-1)
`f2#=1.0/vs2# `simple model. better is v/(2v-1)
endif
if spacekey()
else
move camera 1,portal_radius*f1#
move camera 3,portal_radius*f1#
move camera 4,portal_radius*f1#
move camera 5,portal_radius*f1#
move camera 6,portal_radius*f1#
`move camera 2,portal_radius*f2#
endif
pitch camera up 3,90
pitch camera down 4,90 `:turn camera left 4,180
turn camera left 5,90
turn camera right 6,90
if controlkey()
f1#=0.0
` f2#=0.0
endif
`set camera to object orientation
`set object to camera orientation ref1_tex,1 `only works for cam 0. rubbish.
text 0,0,str$(screen fps())
`test vertexdata stuffs.
`lock vertexdata for limb ref1_obj,2,1 `bizzarely, limb 2 has the vertices! should remesh.
`i=GET VERTEXDATA VERTEX COUNT()
`unlock vertexdata
`text 0,0,str$(i)
lock vertexdata for limb ref1_obj,2,1 `bizzarely, limb 2 has the vertices! should remesh.
n=GET VERTEXDATA VERTEX COUNT()-1 `81-1
for i=0 to n
`simple projection onto sphere to test.
x#=vertxpos#(i)
y#=vertypos#(i)
`z#=-1.0-f1#
`r#=sqrt(x#*x#+y#*y#+z#*z#)
`x#=x#/r#
`y#=y#/r#
`z#=z#/r#
`z#=1.0
rsq#=x#*x#+y#*y#
lsq#=rsq#+1.0 `+zsq#
brak#=(1.0-f1#*f1#*rsq#/lsq#)/lsq#
q#=-f1#/lsq# + sqrt(brak#)
x#=x#*q#
y#=y#*q#
z#=-f1#-q#
set vertexdata position i,x#,y#,z#
next n
unlock vertexdata
REMSTART
lock vertexdata for limb ref2_obj,2,1 `bizzarely, limb 2 has the vertices! should remesh.
n=GET VERTEXDATA VERTEX COUNT()-1 `81-1
for i=0 to n
`simple projection onto sphere to test.
x#=vertxpos#(i)
y#=vertypos#(i)
rsq#=x#*x#+y#*y#
lsq#=rsq#+1.0 `+zsq#
brak#=(1.0-f2#*f2#*rsq#/lsq#)/lsq#
q#=-f2#/lsq# + sqrt(brak#)
x#=x#*q#
y#=y#*q#
z#=-f2#-q#
set vertexdata position i,x#,y#,z#
next n
unlock vertexdata
REMEND
`REMSTART
`UP CAMERA
lock vertexdata for limb ref1u_obj,2,1 `bizzarely, limb 2 has the vertices! should remesh.
n=GET VERTEXDATA VERTEX COUNT()-1 `81-1
for i=0 to n
`simple projection onto sphere to test.
x#=vertxpos#(i)
z#=-vertypos#(i) `up so y always 1 ?!
y#=1.0
rsq#=x#*x# + 1.0 `+y#*y#
lsq#=rsq#+z#*z#
brak#=(1.0-f1#*f1#*rsq#/lsq#)/lsq#
q#=-f1#*z#/lsq# + sqrt(brak#)
x#=x#*q#
y#=y#*q#
z#=-f1#-z#*q#
set vertexdata position i,x#,y#,z#
next n
unlock vertexdata
`DOWN CAMERA
lock vertexdata for limb ref1d_obj,2,1 `bizzarely, limb 2 has the vertices! should remesh.
n=GET VERTEXDATA VERTEX COUNT()-1 `81-1
for i=0 to n
`simple projection onto sphere to test.
x#=vertxpos#(i)
z#=vertypos#(i)
y#=-1.0
rsq#=x#*x# + 1.0 `+y#*y#
lsq#=rsq#+z#*z#
brak#=(1.0-f1#*f1#*rsq#/lsq#)/lsq#
q#=-f1#*z#/lsq# + sqrt(brak#)
x#=x#*q#
y#=y#*q#
z#=-f1#-z#*q#
set vertexdata position i,x#,y#,z#
next n
unlock vertexdata
`LEFT
lock vertexdata for limb ref1l_obj,2,1 `bizzarely, limb 2 has the vertices! should remesh.
n=GET VERTEXDATA VERTEX COUNT()-1 `81-1
for i=0 to n
`simple projection onto sphere to test.
x#=-1.0
z#=vertxpos#(i)
y#=vertypos#(i)
rsq#=y#*y# + 1.0 `+y#*y#
lsq#=rsq#+z#*z#
brak#=(1.0-f1#*f1#*rsq#/lsq#)/lsq#
q#=-f1#*z#/lsq# + sqrt(brak#)
x#=x#*q#
y#=y#*q#
z#=-f1#-z#*q#
set vertexdata position i,x#,y#,z#
next n
unlock vertexdata
`RIGHT
lock vertexdata for limb ref1r_obj,2,1 `bizzarely, limb 2 has the vertices! should remesh.
n=GET VERTEXDATA VERTEX COUNT()-1 `81-1
for i=0 to n
`simple projection onto sphere to test.
x#=1.0
z#=-vertxpos#(i)
y#=vertypos#(i)
rsq#=y#*y# + 1.0 `+y#*y#
lsq#=rsq#+z#*z#
brak#=(1.0-f1#*f1#*rsq#/lsq#)/lsq#
q#=-f1#*z#/lsq# + sqrt(brak#)
x#=x#*q#
y#=y#*q#
z#=-f1#-z#*q#
set vertexdata position i,x#,y#,z#
next n
unlock vertexdata
REMSTART
text 0,50,str$(f1#)
text 0,60,str$(f2#)
REMEND
`cosmetic thing for fun. spin one of the thingys (not spinning portal though! although could do this easily-
`just a set to portal object orientation!
`would also need to apply rotation to position co-ords
turn object left level_obj3,0.5
turn object left level_obj4,0.5 `"light map"
docam=1 `default
`docam=1-docam `for fraps -capturing at 25 fps instead of 50 FOR YOUTUBE VID CAPTURE
IF docam
sync mask 126:sync `add nonexistent cam 2- maybe will fix problem with not rendering cam 3. DBP BUG?
sync mask 1:sync `render 0
ENDIF
loop
end
function single_pixel_image(imagenum,red,green,blue,alpha)
temp_memblocknum=1
make memblock temp_memblocknum,16
write memblock dword temp_memblocknum,0,1
write memblock dword temp_memblocknum,4,1
write memblock dword temp_memblocknum,8,32
write memblock dword temp_memblocknum,12,rgba(red,green,blue,alpha)
make image from memblock imagenum,1
delete memblock temp_memblocknum
endfunction
`RGBA function by Aaron Miller
function rgba(r,g,b,a)
c = (a and 0xff) << 24 or ((r and 0xff) << 16) or ((g and 0xff) << 8) or (b and 0xff)
endfunction c
What it is
Two spherical portals. You look into one and see out of the other. If you fly into one, you fly out of the other.
From a distance, the view through a portal is distorted. Get close and the perspective becomes flatter, such that as you pass through the portal, your view does not "jump". Also, if you see something else go through the portal, it also does not "jump", but is seen to move continuously.
Instructions
Use mouse to look, and mouseclick to fly forwards (left click) and back (right click). 1 and 2 number keys switch between two portal "versions", changing where the viewpoint in the out portal is.
(version 1 (default) makes the line of sight better approximate a straight path going through the portal. Version 2 means only a hemisphere of view is ever seen through the portal. Both versions converge as the portal is approached.)
Improvements
There are a many improvements that can be made to this. The camera resolution could be made dependent on distance from portals, and whether portals are visible. Also, very close to the portal, only the "front" camera view is seen. Much of the view seen by the 4 "sideways" portal cameras is not required at any time. For distant portals, cameras could be rendered, and vertexdata for the portal object set every few frames.
Currently all 5 portal cameras are rendered at fixed resolution, every frame, and for only the portal closest the player. Without improving this system, it would be required to make the two portals not visible in the same frame.
It would also be fun to play around with moving and rotating portals, and incorporating them into a demo with collsion, gravity etc. I've been wanting to try out 3rd party collision detection stuff, and playing more with Cartography Shop so might give this a bash.
Models
Models were made in Cartography Shop. I used CSG with convex objects (it advises you don't ever do this!) and made a cool mess.
Anyway I hope someone finds this useful or interesting. Please make something with it if you feel so inclined.