This example uses sprites Alpha+ Circle Alpha Addition effects.
Updated Jan 2024, corrected a few small differences for modern PlayBASIC
[pbcode]
OpenSCreen 640,480,32,1
; CReate an FX copy of the display
FXScreen=NewFXImage(GetScreenWidth(),GetScreenHeight())
; Projection constants
constant ProjectionX#=400
constant ProjectionY#=400
; View Z depth
Zdepth#=1000
; Size of Alpha Texture
Size=32
MaxImages=64
Dim ParticleIMage(MaxImages)
Col=$ffffff
For lp=0 to MaxImages
ParticleIMage(lp)=Newimage(Size,Size)
rendertoimage ParticleIMage(lp)
Preparefximage ParticleIMage(lp)
RenderPhongImage ParticleIMage(lp),Size/2,Size/2,col,255,260/(size/2)
Col=rndrgb()
next
rendertoscreen
ParticleSize=GetImageWidth(ParticleIMage(0))
NumberOfParticles=400
acset =1
Constant Particle_Xpos=ac(1)
Constant Particle_Ypos=ac(1)
Constant Particle_Zpos=ac(1)
Constant Particle_Size=ac(1)
Constant Particle_Sprite=ac(1)
Constant Particle_StructSize=ac(1)
Dim VertexList#(NumberOfParticles,Particle_StructSize)
Dim RotatedVertexList#(NumberOfParticles,Particle_StructSize)
Size=300
For lp=0 to NumberOfParticles
VertexList#(lp,Particle_Xpos)=rndrange(-size,size)
VertexList#(lp,Particle_Ypos)=rndrange(-size,size)
VertexList#(lp,Particle_Zpos)=rndrange(-size,size)
VertexList#(lp,Particle_Size)=rndrange(20,50)
ThisSprite=NewSprite(0,0,ParticleIMage(rnd(MaxImages)))
SpriteDRawmode ThisSprite,2+16
VertexList#(lp,Particle_Sprite)=ThisSprite
next
RenderMode=1
rendertoimage fxscreen
Createcamera 1
cameracls 1,off
RenderToSCreen
Do
rendertoimage fxscreen
RotateVerts(VertexList#(),RotatedVertexList#(),NumberOFParticles,Tilt#,turn#,roll#,Zdepth#)
if RenderMode=0
CaptureToscene
ClsScene
inkmode 1+64
For lp=0 to NumberOFParticles
z#=RotatedVertexList#(lp,Particle_Zpos)
if z#>1
capturedepth z#
Size#=(VertexList#(lp,Particle_Size)*ProjectionX#)/z#
Circlec RotatedVertexList#(lp,Particle_Xpos),RotatedVertexList#(lp,Particle_Ypos),Size#,1,$80808
endif
next
DrawCamera 1
endif
if RenderMode=1
For lp=0 to NumberOFParticles
z#=RotatedVertexList#(lp,Particle_Zpos)
if z#>1
ThisSprite=VertexList#(lp,Particle_Sprite)
SizeX#=VertexList#(lp,Particle_Size)
POsitionSpritexyz ThisSprite,RotatedVertexList#(lp,Particle_Xpos),RotatedVertexList#(lp,Particle_Ypos),z#
ScaleSprite ThisSprite,(SizeX#*ProjectionX#)/z#/ParticleSize
else
POsitionSpritex ThisSprite,-100
endif
next
drawAllsprites
endif
inkmode 1
rendertoscreen
if RenderMode=1 then BlitImageClr(FXScreen,0,0,rgb(20,30,40))
if RenderMode=0 then BlitImageALphaSub(FXScreen,X#,y#,Rgb(23,34,22))
; BlitImageALphaAdd(FXScreen,X#,y#,Rgb(11,11,11))
; BlitImageALphaSub(FXScreen,X#,y#,Rgb(13,14,12))
; drawimage FXScreen,0,0,0
Rem animate the tilt, turn And roll values
tilt# = tilt#+1.31
turn# = turn#+0.42
roll# = roll#+0.53
ShowSubDivides = enterkey()
if upkey() and zdepth#>400 then Zdepth#=Zdepth#-20
if downkey() then Zdepth#=Zdepth#+20
If SpaceKey() and Released=false
RenderMode=1-rendermode
released=true
endif
if Scancode()=0
released=false
endif
if RenderMode=0 then ModeName$="Alpha Circles"
if RenderMode=1 then ModeName$="Alpha Sprites"
setcursor 0,0
print "Effect Mode: "+Modename$
print fps()
Sync
loop
Function RotateVerts(Pts#(),RotatedPts#(),NumbOfVerts,Tilt#,turn#,roll#,ObjectDistance#)
cx=getscreenwidth()/2
cy=getscreenheight()/2
Rem prepare the rotation matrix
A#=Cos(tilt#) : B#=Sin(tilt#)
C#=Cos(turn#) : D#=Sin(turn#)
E#=Cos(roll#) : F#=Sin(roll#)
AD#=A#*D#
BD#=B#*D#
; Calc Rotation Matrix
m11#=C#*E#
m21#=-1*C#*F#
m31#=D#
m12#=BD#*E#+A#*F#
m22#=-1*BD#*F#+A#*E#
m32#=-1*B#*C#
m13#=-1*AD#*E#+B#*F#
m23#=AD#*F#+B#*E#
m33#=A#*C#
Rem rotate all the points using the matrix
For p=0 To NumbOfVerts
pointx#=pts#(p,1)
pointy#=pts#(p,2)
pointz#=pts#(p,3)
rotatedPts#(p,1) = (m11# * pointx#) + (m12# * pointy#) + (m13# * pointz#)
rotatedPts#(p,2) = (m21# * pointx#) + (m22# * pointy#) + (m23# * pointz#)
rotatedPts#(p,3) = (m31# * pointx#) + (m32# * pointy#) + (m33# * pointz#)
Rem Now Do the perspective calculation
z# = rotatedPts#(p,3) + ObjectDistance#
rotatedPts#(p,1) = cx+ ((rotatedPts#(p,1)*ProjectionX# )/ z#)
rotatedPts#(p,2) = cy+ ((rotatedpts#(p,2)*ProjectionY# )/ z#)
RotatedPts#(p,3)=z#
Next p
Endfunction
Function SizeVerts(Pts#(),x_scale#,y_scale#,z_scale#)
Rem rotate all the points using the matrix
For p=1 To getArrayelements(pts#(),1)
pts#(p,1)=pts#(p,1)* x_scale#
pts#(p,2)=pts#(p,2)* Y_scale#
pts#(p,3)=pts#(p,3)* Z_scale#
Next p
Endfunction
Function BlitImageClr(ThisImage,X#,y#,ClrRGB)
BlitMode=1
blitimage ThisIMage,x#,y#,transparentflag,Blitmode,ClrRGB,0,0
EndFunction
Function BlitImageALphaAdd(ThisImage,X#,y#,AddRGB)
select GetSCreenDepth()
case 32
BlitMode=64
blitimage ThisImage,x#,y#,transparentflag,Blitmode,AddRGB,0,0
default
; Create an Equivalent Alpha effect using a box alpha masked over the src image
OldSurface=getsurface()
w=getimagewidth(Thisimage)
h=getimageHeight(Thisimage)
oldinkmode=getinkmode()
rendertoimage thisimage
inkmode 1+64
boxc 0,0,w,h,1,AddRgb
inkmode oldinkmode
rendertoimage OldSurface
drawimage Thisimage,X#,y#,0
endselect
EndFunction
Function BlitImageALphaSub(ThisImage,X#,y#,SubRGB)
select GetSCreenDepth()
; BlitImage only supports 32bit in PBV1.089 and bellow
case 32
BlitMode=128
blitimage ThisImage,x#,y#,transparentflag,Blitmode,SubRGB,0,0
default
; Create an Equivalent Alpha effect using a box alpha masked over the src image
OldSurface=getsurface()
w=getimagewidth(Thisimage)
h=getimageHeight(Thisimage)
oldinkmode=getinkmode()
rendertoimage thisimage
inkmode 1+128
boxc 0,0,w,h,1,SubRgb
inkmode oldinkmode
rendertoimage OldSurface
drawimage Thisimage,X#,y#,0
endselect
EndFunction
[/pbcode]
Original Code:
[pbcode]
; PROJECT : AlphaParticles
; AUTHOR : Kevin Picone (c) Underware Design 2005
; CREATED : 13/08/2004
; EDITED : 8/01/2006
OpenSCreen 640,480,16,2
; CReate an FX copy of the display
FXScreen=NewImage(GetScreenWidth(),GetScreenHeight())
PrepareFXimage FXscreen
; Projection constants
constant ProjectionX#=400
constant ProjectionY#=400
; View Z depth
Zdepth#=1000
; Size of Alpha Texture
Size=32
MaxImages=64
Dim ParticleIMage(MaxImages)
Col=$ffffff
For lp=0 to MaxImages
ParticleIMage(lp)=Newimage(Size,Size)
rendertoimage ParticleIMage(lp)
Preparefximage ParticleIMage(lp)
RenderPhongImage ParticleIMage(lp),Size/2,Size/2,col,255,260/(size/2)
Col=rndrgb()
next
rendertoscreen
ParticleSize=GetImageWidth(ParticleIMage(0))
NumberOfParticles=400
acset =1
Constant Particle_Xpos=ac(1)
Constant Particle_Ypos=ac(1)
Constant Particle_Zpos=ac(1)
Constant Particle_Size=ac(1)
Constant Particle_Sprite=ac(1)
Constant Particle_StructSize=ac(1)
Dim VertexList#(NumberOfParticles,Particle_StructSize)
Dim RotatedVertexList#(NumberOfParticles,Particle_StructSize)
Size=300
For lp=0 to NumberOfParticles
VertexList#(lp,Particle_Xpos)=rndrange(-size,size)
VertexList#(lp,Particle_Ypos)=rndrange(-size,size)
VertexList#(lp,Particle_Zpos)=rndrange(-size,size)
VertexList#(lp,Particle_Size)=rndrange(20,50)
ThisSprite=NewSprite(0,0,ParticleIMage(rnd(MaxImages)))
SpriteDRawmode ThisSprite,2+16
VertexList#(lp,Particle_Sprite)=ThisSprite
next
RenderMode=1
rendertoimage fxscreen
Createcamera 1
cameracls 1,off
RenderToSCreen
Do
rendertoimage fxscreen
RotateVerts(VertexList#(),RotatedVertexList#(),NumberOFParticles,Tilt#,turn#,roll#,Zdepth#)
if RenderMode=0
CaptureToscene
ClsScene
inkmode 1+64
For lp=0 to NumberOFParticles
z#=RotatedVertexList#(lp,Particle_Zpos)
if z#>1
capturedepth z#
Size#=(VertexList#(lp,Particle_Size)*ProjectionX#)/z#
Circlec RotatedVertexList#(lp,Particle_Xpos),RotatedVertexList#(lp,Particle_Ypos),Size#,1,$80808
endif
next
DrawCamera 1
endif
if RenderMode=1
For lp=0 to NumberOFParticles
z#=RotatedVertexList#(lp,Particle_Zpos)
if z#>1
ThisSprite=VertexList#(lp,Particle_Sprite)
SizeX#=VertexList#(lp,Particle_Size)
POsitionSpritexyz ThisSprite,RotatedVertexList#(lp,Particle_Xpos),RotatedVertexList#(lp,Particle_Ypos),z#
ScaleSprite ThisSprite,(SizeX#*ProjectionX#)/z#/ParticleSize
else
POsitionSpritex ThisSprite,-100
endif
next
drawAllsprites
endif
inkmode 1
rendertoscreen
if RenderMode=1 then BlitImageClr(FXScreen,0,0,rgb(20,30,40))
if RenderMode=0 then BlitImageALphaSub(FXScreen,X#,y#,Rgb(23,34,22))
; BlitImageALphaAdd(FXScreen,X#,y#,Rgb(11,11,11))
; BlitImageALphaSub(FXScreen,X#,y#,Rgb(13,14,12))
; drawimage FXScreen,0,0,0
Rem animate the tilt, turn And roll values
tilt# = tilt#+1.31
turn# = turn#+0.42
roll# = roll#+0.53
ShowSubDivides = enterkey()
if upkey() and zdepth#>400 then Zdepth#=Zdepth#-20
if downkey() then Zdepth#=Zdepth#+20
If SpaceKey() and Released=false
RenderMode=1-rendermode
released=true
endif
if Scancode()=0
released=false
endif
if RenderMode=0 then ModeName$="Alpha Circles"
if RenderMode=1 then ModeName$="Alpha Sprites"
setcursor 0,0
print "Effect Mode: "+Modename$
print fps()
Sync
loop
Function RotateVerts(Pts#(),RotatedPts#(),NumbOfVerts,Tilt#,turn#,roll#,ObjectDistance#)
cx=getscreenwidth()/2
cy=getscreenheight()/2
Rem prepare the rotation matrix
A#=Cos(tilt#) : B#=Sin(tilt#)
C#=Cos(turn#) : D#=Sin(turn#)
E#=Cos(roll#) : F#=Sin(roll#)
AD#=A#*D#
BD#=B#*D#
; Calc Rotation Matrix
m11#=C#*E#
m21#=-1*C#*F#
m31#=D#
m12#=BD#*E#+A#*F#
m22#=-1*BD#*F#+A#*E#
m32#=-1*B#*C#
m13#=-1*AD#*E#+B#*F#
m23#=AD#*F#+B#*E#
m33#=A#*C#
Rem rotate all the points using the matrix
For p=0 To NumbOfVerts
pointx#=pts#(p,1)
pointy#=pts#(p,2)
pointz#=pts#(p,3)
rotatedPts#(p,1) = (m11# * pointx#) + (m12# * pointy#) + (m13# * pointz#)
rotatedPts#(p,2) = (m21# * pointx#) + (m22# * pointy#) + (m23# * pointz#)
rotatedPts#(p,3) = (m31# * pointx#) + (m32# * pointy#) + (m33# * pointz#)
Rem Now Do the perspective calculation
z# = rotatedPts#(p,3) + ObjectDistance#
rotatedPts#(p,1) = cx+ ((rotatedPts#(p,1)*ProjectionX# )/ z#)
rotatedPts#(p,2) = cy+ ((rotatedpts#(p,2)*ProjectionY# )/ z#)
RotatedPts#(p,3)=z#
Next p
Endfunction
Function SizeVerts(Pts#(),x_scale#,y_scale#,z_scale#)
Rem rotate all the points using the matrix
For p=1 To getArrayelements(pts#(),1)
pts#(p,1)=pts#(p,1)* x_scale#
pts#(p,2)=pts#(p,2)* Y_scale#
pts#(p,3)=pts#(p,3)* Z_scale#
Next p
Endfunction
Function BlitImageClr(ThisImage,X#,y#,ClrRGB)
BlitMode=1
blitimage ThisIMage,x#,y#,transparentflag,Blitmode,ClrRGB,0,0
EndFunction
Function BlitImageALphaAdd(ThisImage,X#,y#,AddRGB)
select GetSCreenDepth()
case 32
BlitMode=64
blitimage ThisImage,x#,y#,transparentflag,Blitmode,AddRGB,0,0
default
; Create an Equivalent Alpha effect using a box alpha masked over the src image
OldSurface=getsurface()
w=getimagewidth(Thisimage)
h=getimageHeight(Thisimage)
oldinkmode=getinkmode()
rendertoimage thisimage
inkmode 1+64
boxc 0,0,w,h,1,AddRgb
inkmode oldinkmode
rendertoimage OldSurface
drawimage Thisimage,X#,y#,0
endselect
EndFunction
Function BlitImageALphaSub(ThisImage,X#,y#,SubRGB)
select GetSCreenDepth()
; BlitImage only supports 32bit in PBV1.089 and bellow
case 32
BlitMode=128
blitimage ThisImage,x#,y#,transparentflag,Blitmode,SubRGB,0,0
default
; Create an Equivalent Alpha effect using a box alpha masked over the src image
OldSurface=getsurface()
w=getimagewidth(Thisimage)
h=getimageHeight(Thisimage)
oldinkmode=getinkmode()
rendertoimage thisimage
inkmode 1+128
boxc 0,0,w,h,1,SubRgb
inkmode oldinkmode
rendertoimage OldSurface
drawimage Thisimage,X#,y#,0
endselect
EndFunction
[/pbcode]
Pretty cool! Not very fast on my machine but looks great. Will fiddle around with it ;)
Thanks for the sample code,
Tommy
Yeah, I can't say i'm terribly surprised, it won't ever be 'fast'. Whatever that means. There's a fair slab of rotation overhead on the VM handling 400 points, and the actual shift it's to video memory, is at the mercy of your video bus. But none of the polygon renders (which draw sprites) are MMX optimized, which is where most of the time is spent.
It clocks about 24/25fps in32bit and about 30fps in 16bit on my Duron 800 + GF2 combo.
In between baby sitting yesterday, and fixing the clipping and some render errors in Blit Image, i've been experimenting with a few other modes. This one takes two source images and performs a 50/50 alpha blend between the two and dumps tot eh output surface. If both surfaces are FX surfaces this avoid the reading from memory bottleneck
The blend image is secondary though, so unfortunately you can't offset the two during the merge. And the second image can't be smaller than source. It's not express at full screen (640*480*32) on the Duron 800/gf2 combo, but should be useful when used in smaller doses.
This picture is the above demo modified to merge the sprite image with a shadebox + image on a second image. The Blit mode doesn't effect either, it just merges them and write to where ever the current surface is.
This image is the product of another BlitImage combination effect. This mode performs an alpha subtraction between two images during the output. This means that you can use a second image as sort of light map (well subtraction map). It's creates a pretty interest effect in the particle demo, the particles turn into these sort of morphing blobs. Very odd, but cool :)
I guess should explain how it works.
Image A = Your normal image
Image B = Subtraction Map
Outputted pixels are read from Pixel A and then have the corresponding pixel from image B subtracted from it then it's drawn to the ouput surface.
So white pixels will make an area blacked out, and Black pixels will have no effect. Alternatively if you fill Image with Rgb(0,0,255) this would screen out Blue in the render image... You can make a flash light by using gouraud shaded triangles. Projected from (or around) the viewer.