3D City Scape
This example is of a 3D city scape that's generated in code. The example uses the Sprite Entity feature of PB1.7x combined and Draw Perspective Sprite command to render a 3D scene.
Screen Shots
See Screen Shots (http://www.underwaredesign.com/forums/index.php?topic=1807.msg14836#msg14836)
Code
This code will work in PBV1.70 revisions (with perspective errors) but was written using PB1.71f.
; Open a 800*600*32 screen in FullScreen with Z buffer support.
OpenScreen 800,600,32,PBscreenmode_FullScreenZ
screenvsync on
; Make the Random Grass texture
Grass=MakeGrass(1024,56)
Wall=MakeWall(256,512,56)
; The Size (along edges) of the Terrain Mesh
TerrainSize=96
; Create the a Sprite With a terrain like mesh
terrain= MakeTerrainSprite(Grass,rnd(0),rnd(0),TerrainSize)
; Set the Sprite Rotation Mode to 3D (XYZ mode()
SpriteRotationMode terrain,0
; Position the Sprite in 3D space
positionspritexyz terrain,0,0000,00
; Enable Back faces culling so Polygons facing away from the camera won't be drawn
Spritebackface Terrain,On
Dim Buildings(3500)
For lp=1 to getarrayelements(Buildings(),1)
Buildings(lp)=MakeBuildingSprite(Wall,rndrange(-50000,50000),0000,rndrange(-50000,50000),rndrange(250,1000),rndrange(1000,10000),rndrange(250,1000))
Next
; Dim PB Camera type
Dim Camera as PBCamera Pointer
Camera = new PBCamera
; Camera Position
Camera.pos.X=0.0
Camera.pos.y=5500.0
Camera.pos.z=-10000.0
; RotationMode 0= 2d With Perspective down Z axis (z rotation supported Camera.Angle.Z)
; RotationMode 1= 3D ZYX rotation
Camera.RotationMode=1
// Set Camera Projection t
Camera.Projection.x=((GetScreenWidth()/2.0)*1000)/(400)
Camera.Projection.y=((GetScreenHeight()/2.0)*1000)/(400)
// Middle of Camera
Camera.ViewPoint.X=GetScreenWidth()/2.0
Camera.ViewPoint.y=GetScreenHeight()/2.0
MillisecondsPerFrame=1000/60
NextFrame=timer()+MillisecondsPerFrame
// Start of Program Main Loop
Do
; Clear the Screen
; Cls rgb(130,140,140)
Cls rgb(30,40,40)
ix=mod(ix+1,getimagewidth(1))
; DRaw the Sprite List from the Camera Viewer Perspectively
DrawPerspectiveSprites Camera
if Timer()>NextFrame
framespast=(timer()-NextFrame)/MillisecondsPerFrame
for lp=1 to framesPast
MouseLookCamera(camera,100)
next
NextFrame=NextFrame+(FramesPast*MillisecondsPerFrame)
endif
; setcursor 0,0
; print fps()
; print framespast
; print "Camera Position"
; print Camera.pos.x
; print Camera.pos.y
; print Camera.pos.z
; print "Camera Projection"
; print Camera.Projection.x
; print Camera.Projection.y
; print "Camera Rotation"
;; print Camera.Angle.x
; print Camera.Angle.y
; print Camera.Angle.z
; print Camera.RotationMode
; drawimage grass,100,100,true
; drawimage wall,00,00,true
Sync
loop
Function MakeBuildingSprite(ThisIMage,Xpos#,Ypos#,Zpos#,Width#,Height#,Depth#)
; Create the sprite
ThisSprite=NewSprite(Xpos#,Ypos#,ThisIMage)
SpriteDrawMode ThisSprite, 2
PositionSpriteXyz thisSprite,xpos#,ypos#,zpos#
SpriteTransparent ThisSprite,off
TurnSpriteXYZ thissprite,00,00,00
SpriteBackFace ThisSprite,on
SpriteRotationMode ThisSprite,0
; Set Terrain Sprites number of Vertex and Faces
SpriteVertexQuantity thisSprite,8
SpriteFaceQuantity thisSprite,20
HandleX#=Width#/-2
HandleY#=Height#
HandleZ#=Depth#/2
y#=0
for Ylp=0 to 1
X1#=Handlex#
Z1#=Handlez#
X2#=x1#+Width#
Z2#=z1#
X3#=x2#
Z3#=z2#-Depth#
X4#=x1#
Z4#=z3#
PokeSpriteVertex ThisSprite,ThisVertex,X1#,y#,z1#
inc ThisVertex
PokeSpriteVertex ThisSprite,ThisVertex,X2#,y#,z2#
inc ThisVertex
PokeSpriteVertex ThisSprite,ThisVertex,X3#,y#,z3#
inc ThisVertex
PokeSpriteVertex ThisSprite,ThisVertex,X4#,y#,z4#
inc ThisVertex
y#=y#+Height#
next
ThisColour2=rgb(0,0,255)
ThisColour=rndrgb()
;rgb(255,255,255)
; init faces (UV points U=X in texture space, V = Y in texture space)
u1#=0
v1#=0
u2#=1
v2#=0
u3#=1
v3#=1
u4#=0
v4#=1
For Vert=0 to 3
PokeSpriteFaceVerts ThisSprite,ThisFace,4
NextVertTop=Vert+5
NextVertBot=Vert+1
if Vert=3
NextVertTop=NextVertTop-4
NextVertBot=NextVertBot-4
endif
PokeSpriteface ThisSprite,ThisFace,0,Vert+4 ,u1#,v1#,ThisColour
PokeSpriteface ThisSprite,ThisFace,1,NextVertTop ,u2#,v2#,ThisColour
PokeSpriteface ThisSprite,ThisFace,2,NextVertBot ,u3#,v3#,ThisColour
PokeSpriteface ThisSprite,ThisFace,3,Vert+0 ,u4#,v4#,ThisColour
inc ThisFace
next
; Set top face
PokeSpriteFaceVerts ThisSprite,ThisFace,4
PokeSpriteface ThisSprite,ThisFace,0,7 ,u1#,v1#,ThisColour
PokeSpriteface ThisSprite,ThisFace,1,6 ,u1#,v1#,ThisColour
PokeSpriteface ThisSprite,ThisFace,2,5 ,u1#,v1#,ThisColour
PokeSpriteface ThisSprite,ThisFace,3,4 ,u1#,v1#,ThisColour
inc ThisFace
;Set the numbner of faces this sprite has
SpriteFaceQuantity thisSprite,ThisFace+1
EndFUnction ThisSPrite
;*=---------------------------------------------------------------------------=*
; >> Make A Terrain Sprite<<
;*=---------------------------------------------------------------------------=*
Function MakeTerrainSprite(ThisIMage,Xpos#,Ypos#,points)
; Create the sprite
ThisSprite=NewSprite(Xpos#,Ypos#,ThisIMage)
SpriteDrawMode ThisSprite, 2
SpriteTransparent ThisSprite,off
turnspritexyz thissprite,00,00,00
Points2=(points+1)
; Dim an Array to Hold the terraion height map
Dim Matrix#(POints2,points2)
; randomize the heights of each point in the height map
RadomizeHeightMap(Matrix#(),2000)
; smooth out the height map be averageing the points
SmoothHeightMap(Matrix#(),2)
; Set Terrain Sprites number of Vertex and Faces
SpriteVertexQuantity thisSprite,(Points2*POints2)
SpriteFaceQuantity thisSprite,(Points2*Points2)*2
Size=1000
HandleX#=-(((points+1.0)/2.0)*size)
HandleZ#=-(((points+1.0)/2.0)*size)
; init vertex
for Ylp=0 to Points
Zpos#=ylp*Size
for Xlp=0 to Points
Xpos#=Xlp*Size
ThisVertex=(Ylp*(points+1))+xlp
ypos#=Matrix#(xlp,ylp)
PokeSpriteVertex ThisSprite,ThisVertex,Xpos#+HandleX#,Ypos#,Zpos#+HandleZ#
next
next
ThisColour=rgb(255,255,255)
; init faces (UV points U=X in texture space, V = Y in texture space)
TUstep#=1.0/(points+1)
Tvstep#=1.0/(points+1)
For Ylp=0 to points-1
For Xlp=0 to points-1
CurrentVertexRow =(Ylp*(points+1))+Xlp
NextVertexRow =CurrentVertexRow+(points+1)
PokeSpriteFaceVerts ThisSprite,ThisFace,3
u1#=Xlp*TUstep#
v1#=Ylp*TVstep#
u2#=(Xlp+1)*TUstep#
v2#=Ylp*TVstep#
u3#=(Xlp+1)*TUstep#
v3#=(Ylp+1)*TVstep#
u4#=(Xlp+0)*TUstep#
v4#=(Ylp+1)*TVstep#
PokeSpriteface ThisSprite,ThisFace,0,CurrentVertexRow+0 ,u1#,v1#,ThisColour
PokeSpriteface ThisSprite,ThisFace,1,CurrentVertexRow+1 ,u2#,v2#,ThisColour
PokeSpriteface ThisSprite,ThisFace,2,NextVertexRow+1 ,u3#,v3#,ThisColour
inc ThisFace
PokeSpriteFaceVerts ThisSprite,ThisFace,3
PokeSpriteface ThisSprite,ThisFace,0,CurrentVertexRow+0 ,u1#,v1#,ThisColour
PokeSpriteface ThisSprite,ThisFace,1,NextVertexRow+1 ,u3#,v3#,ThisColour
PokeSpriteface ThisSprite,ThisFace,2,NextVertexRow ,u4#,v4#,ThisColour
inc ThisFace
Next
Next
;Set the numbner of faces this sprite has
SpriteFaceQuantity thisSprite,ThisFace+1
EndFUnction ThisSPrite
` *=----------------------------------------------------------------=*
` >> Randomize HeightMap Array <<
` *=----------------------------------------------------------------=*
Function RadomizeHeightMap(Matrix#(),Height)
Width=getarrayelements(Matrix#(),1)
Depth=getarrayelements(Matrix#(),2)
For Zlp=1 to Depth-2
For Xlp=1 to Width-2
Matrix#(xlp,zlp)=rnd#(Height)
next xlp
next zlp
Endfunction
` *=----------------------------------------------------------------=*
` >> Average Matrix Height <<
` *=----------------------------------------------------------------=*
Function SmoothHeightMap(Matrix#(),passes)
Width=getarrayelements(Matrix#(),1)
Depth=getarrayelements(Matrix#(),2)
Dim Temp_HeightMap_Yvalues#(Width,Depth)
For ThisPass=0 To Passes
CopyArray Matrix#(),Temp_HeightMap_Yvalues#()
For Zlp=1 to Depth-1
ZlpF=zlp-1
ZlpB=zlp+1
Width2=Width-1
For Xlp=1 to Width2
xlpL=Xlp-1
xlpR=Xlp+1
yb#=Temp_HeightMap_Yvalues#(xlp,zlp)
yt#=Temp_HeightMap_Yvalues#(xlp,zlpF)
yb#=Temp_HeightMap_Yvalues#(xlp,zlpB)
yl#=Temp_HeightMap_Yvalues#(xlpL,zlp)
yr#=Temp_HeightMap_Yvalues#(xlpR,zlp)
ytl#=Temp_HeightMap_Yvalues#(xlpL,zlpF)
ytr#=Temp_HeightMap_Yvalues#(xlpR,zlpF)
ybl#=Temp_HeightMap_Yvalues#(xlpL,zlpB)
ybr#=Temp_HeightMap_Yvalues#(xlpR,zlpB)
matrix#(xlp,zlp)=(yb#+yt#+yb#+yL#+yr#+ytl#+ytr#+ybl#+ybr#)/9
next xlp
next zlp
next ThisPass
unDim Temp_HeightMap_Yvalues#()
endfunction
Function MouseLookCamera(camera as PBcamera pointer,camspeed#)
static acamx#,acamy#
W=GetScreenWidth()
H=GetScreenHeight()
x1=GetScreenXpos()
Y1=GetScreenYpos()
x2=x1+w
y2=y1+h
MMX#=MouseMoveX()
MMY#=MouseMoveY()
x#=x1+MouseX()
y#=y1+MouseY()
flag=0
If x#=<(x1+75) Then x#=x1+(w/2): flag=1
If x#=>(x2-75) And flag=0 Then x#=x1+(w/2): flag=1
If y#=<(y1+75) Then y#=y1+(h/2): flag=1
If y#=>(y2-75) Then y#=y1+(h/2): flag=1
If flag=1
SetMouse x#,y#
MMX2#=MouseMoveX()
MMY2#=MouseMoveY()
if pbdebugmode=0
SetMouse x#,y#
endif
EndIf
camf#=0
acamx#=WrapAngle(acamx#,mmy#*0.7)
acamy#=WrapAngle(acamy#,mmx#*0.7)
Camera.Angle.x#=CurveAngle(acamx#,Camera.Angle.x#,6.2)
Camera.Angle.y#=CurveAngle(acamy#,Camera.Angle.y#,6.2)
If KeyState(200 ) Then camf#=camf#+camspeed#
If KeyState(208) Then camf#=camf#-camspeed#
If camf#<>0
movementx#,movementy#,movementz#=CalcMovement(acamx#,acamy#,0.0,Camf#)
x#=Camera.pos.x#+movementx#
y#=Camera.pos.y#+movementy#
z#=Camera.pos.z#+movementz#
; clip the camera to 20 on y axis
; If y#<100 Then y#=100
Camera.pos.x=x#
Camera.pos.y=y#
Camera.pos.z=z#
EndIf
EndFunction
Function CalcMovement(Anglex#,angley#,anglez#,Speed#)
` invert angles
anglex#=WrapAngle(360,-anglex#)
angley#=WrapAngle(90,-angley#)
anglez#=WrapAngle(360,-anglez#)
` precalc cos+sin for rotation
cx#=Cos(anglex#)
sx#=Sin(anglex#)
cy#=Cos(angley#)
sy#=Sin(angley#)
Zpos#=Speed#
z#=(cx#*Zpos#)
y#=(sx#*zpos#)
` Around Y axis
x2#=(cY#*Z#)-(sy#*X#)
z#=((cy#*X#)+(sy#*Z#))
EndFunction x2#,y#,z#
Function MakeGrass(Size,Seed)
Randomize Seed
Me=New3DImage(Size,Size)
if Me
oldsurface=getsurface()
rendertoimage me
lockbuffer
GrassColour=$304050
For ylp=0 to size-1
For xlp=0 to size-1
IntensityColour=rnd(155)
C=(IntensityColour+OLDIntensityColour)/2
c=rgbalphamult(GrassColour,Rgb(c,c,c))
dotc xlp,ylp,c
OldIntensityColour= IntensityColour
next
next
unlockbuffer
blurimage me,8.1
inkmode 1+16
For ylp=0 to size-1 step 32
For xlp=0 to size-1 step 32
boxc xlp,0,xlp+8,size,true,rgb(90,20,20)
boxc 0,ylp,size,ylp+8,true,rgb(20,20,90)
next
next
inkmode 1
rendertoimage oldsurface
endif
EndFunction me
Function MakeWall(Width,Height,Seed)
Randomize Seed
Me=New3DImage(Width,Height)
if Me
oldsurface=getsurface()
rendertoimage me
lockbuffer
c1=rgb(10,10,40)
c2=rndrgb()
Shadebox 0,0,Width,Height,c1,c1,c2,c2
WindowWidth=16
WindowHeight=8
inkmode 1
for ylp=10 to Height-20 step (windowHeight+4)
for xlp=10 to Width-10 step (windowWidth+4)
v=rnd(1)
boxc xlp,ylp,xlp+windowWidth,ylp+windowHeight,true,v*rgb(200,200,200)
boxc xlp,ylp,xlp+windowWidth,ylp+windowHeight,false,v*rgb(255,255,255)
next
next
inkmode 1
unlockbuffer
rendertoimage oldsurface
blurimage me,8.1
endif
EndFunction me