Sprite Surface Thingy

Blitz3D SDK Forums/Blitz3D SDK Programming/Sprite Surface Thingy

Ok, I found this sprite surface while browsing the code archives, and ported it from B3D to BMax/B3DSDK. Along with a few optimizations. Thought I'd share it. :)

Import Blitz3D.Blitz3DSDK
Import BRL.Retro

Rem

Ported to BlitzMax / Blitz3D SDK and optimised for BlitzMax by ninjarat
RCD Software (TM)

Main code by Syntax Error

Modified by Barliesque
 - Re-wrote CopyImage3D() to create an independant copy of a sprite
 - Modified texture application technique in various functions to allow
      sprite copies to use different frames of the same animated texture
 - Fixed an error in ImageToSprite(), so that parent parameter defaults correctly


Thanks go to:
-------------
SkidRacer        - Pixel-Perfect sprites (pixies.bb)
Rob Cummings     - Information on using a 2nd sprite camera (now defunct)
FredBorg         - HandleImage3D() and CopyImage3D()
EricZann         - Idea for ModifyText3D
Ghislain Schoofs - Suggestion for optional 'Parent' flag to give more flexibility
BlitzSupport     - MaskImage3D - modified LoadMaskedTexture
Bot Builder      - MidHandle3D - modified Mid3DHandle from code archives

End Rem


Global spritepivot,spritecamera


' Create a complete sprite display with camera/pivot
Function Init3DSpriteSurface(pivotdist#=1.0)
	bbSetBuffer bbBackBuffer()
	spritecamera=bbCreateCamera()
	spritepivot=CreateSpritePivot(spritecamera,pivotdist)
End Function

' Create a single 'sprite pivot' and scale to screen resolution
Function CreateSpritePivot(parentcam=0,dist#=1.0)

	Local gw=bbGraphicsWidth()
	Local gh=bbGraphicsHeight()
	spritepivot=bbCreatePivot(parentcam)
	Local aspect#=Float(gh)/Float(gw)
	Local scale#=2.0/gw
	bbPositionEntity spritepivot,-1,aspect,dist
	bbScaleEntity spritepivot,scale,scale,scale
	bbNameEntity spritepivot,"SpriteControl Pivot"
	Return spritepivot

End Function

' Use this command to zoom the main camera and sprite pivot
' NOTE: Only positive values if 1 and greater can be used
Function CameraZoom3D(cam,z#)

	If z<1.0 z=1.0
	If bbEntityClass(cam)="Camera" bbCameraZoom cam,z
	Local numchilds=bbCountChildren(cam)
	If numchilds>0
		For c=1 To numchilds
			Local child=bbGetChild(cam,c)
			If bbEntityName(child)="SpriteControl Pivot"
				bbPositionEntity child,bbEntityX(child),bbEntityY(child),z+0.001
				Exit
			EndIf
		Next
	EndIf

End Function

' Create a blank quad sprite
Function CreateImage3D(w=1,h=1,par=0,u2#=1.0,v2#=1.0)

	If par=0 par=spritepivot
	Local sprite=bbCreateMesh(par)
	Local s=bbCreateSurface(sprite)
	bbAddVertex s,0,0,0 ,0,0
	bbAddVertex s,2,0,0 , u2,0
	bbAddVertex s,0,-2,0 ,0,v2
	bbAddVertex s,2,-2,0 , u2,v2
	bbAddTriangle s,0,1,2
	bbAddTriangle s,3,2,1
	bbScaleEntity sprite,Float(w)/2,Float(h)/2,1
	bbEntityFX sprite,1+16+32
	bbEntityOrder sprite,-100
	bbPositionEntity sprite,-10000,-10000,0
	Return sprite

End Function

' Load an image into a texture and attach it to a quad sprite
Function LoadImage3D(file$,texflags=4,par=0)

	If par=0 par=spritepivot
	Local tmpimage=bbLoadImage(file$)
	sprite=ImageToSprite(tmpimage,texflags)
	bbFreeImage tmpimg
	Return sprite

End Function

' Load and create a quad sprite with animation frames
Function LoadAnimImage3D(file$,framesX=1,framesY=1,texflags=4,par=0)

	If par=0 par=spritepivot
	If frames<2 Then frames=2
	Local tmpimage=bbLoadImage(file)
	Local iw=bbImageWidth(tmpimage)
	Local ih=bbImageHeight(tmpimage)
	Local tw=2 Shl (Len(Int(Bin(iw-1)))-1)
	Local th=2 Shl (Len(Int(Bin(ih-1)))-1)
	bbFreeImage tmpimg
	sprite=ImageToSprite(tmpimage,texflags,framesX,framesY)
	Local ename$="W"+Float(tw)/(Float(iw)/Float(framesX))
	ename:+"H"+Float(th)/(Float(ih)/Float(framesY))
	ename:+"X"+String(framesX)
	ename:+"Y"+String(framesY)+"E"
	bbNameEntity sprite,ename
	Return sprite

End Function

' Grab an area of the current drawing buffer into a quad sprite
Function GrabImage3D(x,y,iw,ih,par=0)

	If par=0 par=spritepivot
	Local tmpimage=bbCreateImage(iw,ih)
	bbGrabImage tmpimage,x,y
	sprite=ImageToSprite(tmpimage)
	bbFreeImage tmpimage
	Return sprite

End Function

' Duplicate an existing quad sprite
Function CopyImage3D(sprite,Alpha#=1.0,Blend=1)

	Local i,x,y
	Local vx#,vy#,vz#,u#,v#
	Local Surf = bbGetSurface(sprite,1)
   Local par = bbGetParent(sprite)
	Local NewSprite = bbCreateMesh(par)
	Local NewSurf = bbCreateSurface(NewSprite)

	i=0
   For y=0 To -2 Step -2
      For x=0 To 2 Step 2
			u  = bbVertexU(Surf,i)
			v  = bbVertexV(Surf,i)
			bbAddVertex NewSurf, x,y,0, u,v
	   Next
	Next
	bbAddTriangle NewSurf,0,1,2
	bbAddTriangle NewSurf,3,2,1

	bbPositionEntity NewSprite, bbEntityX(sprite),bbEntityY(sprite),bbEntityZ(sprite)
	ResizeImage3D NewSprite, ImageWidth3D(sprite),ImageHeight3D(sprite)
	
	bbEntityFX    NewSprite, 17
	bbEntityOrder NewSprite, -100
	bbEntityBlend NewSprite, Blend
	bbEntityAlpha NewSprite, Alpha
	bbNameEntity  NewSprite, bbEntityName(sprite)
	bbEntityTexture NewSprite, GetSpriteTexture(sprite)
	Return NewSprite

End Function

' Create a quad sprite from a string of text
Function Text3D(xpos,ypos,txt$,flags=5,par=0)

	If par=0 par=spritepivot
	If t$="" Then t$="?"
	Local gbuffer=bbGraphicsBuffer()
	Local tmpimage=bbCreateImage(bbStringWidth(txt$),bbFontHeight())
	bbSetBuffer bbImageBuffer(tmpimage)
	bbCls ;	bbText 0,0,txt$ ;	bbSetBuffer gbuffer
	textsprite=ImageToSprite(tmpimage,flags)
	bbFreeImage tmpimage
	DrawImage3D textsprite,xpos,ypos
	Return textsprite

End Function

' Modify existing text in quad sprite
Function ModifyText3D(sprite,xpos,ypos,t$,flags=5)

	If t$="" Then t$="?"
	Local par=bbGetParent(sprite)
	FreeImage3D sprite
	Local newstextsprite=Text3D(xpos,ypos,t$,flags,par)
	Return newsprite
	
End Function

' Position quad sprite at 2D screen coordinates
Function DrawImage3D(sprite,x,y,frame=-99999,z#=0)

	bbPositionEntity sprite,x+0.5,-y+0.5,z
	If frame<>-99999
		Local en$=bbEntityName(sprite)
		Local fw#=Float(Mid$(en$,Instr(en$,"W")+1,Instr(en$,"H")-Instr(en$,"W")))
		Local fh#=Float(Mid$(en$,Instr(en$,"H")+1,Instr(en$,"X")-Instr(en$,"H")))
		Local framesH=Int(Mid$(en$,Instr(en$,"X")+1,Instr(en$,"Y")-Instr(en$,"X")))
		Local framesV=Int(Mid$(en$,Instr(en$,"Y")+1,Instr(en$,"E")-Instr(en$,"Y")))
		Local numframes=framesH*framesV
		If frame>numframes frame=(frame Mod numframes)
		If frame<1 frame=numframes-(Abs(frame) Mod numframes)
		frame=frame-1

		Local frameX = frame Mod framesH
		Local frameY = Int(frame/framesH)

		Local u1#=Float(frameX)/Float(framesH)
		Local v1#=Float(frameY)/Float(framesV)
	   Local u2#=u1 + (1.0/FramesH)
	   Local v2#=v1 + (1.0/FramesV)

		Local s=bbGetSurface(sprite,1)
		bbVertexTexCoords s,0,u1,v1	
		bbVertexTexCoords s,1,u2,v1	
		bbVertexTexCoords s,2,u1,v2	
		bbVertexTexCoords s,3,u2,v2	
	EndIf
		
End Function

' Scale a quad sprite to equivalent 2d pixel width/height values
Function ResizeImage3D(sprite,w,h)

	bbScaleEntity sprite,Float(w)/2,Float(h)/2,1

End Function

' Flip the quad sprite horizontally or vertically
Function FlipImage3D(sprite,flipX=1,flipY=0)

	Local s=bbGetSurface(sprite,1)
	Local vx#,vy#,vz#
	If flipX
		vx#=bbVertexX(s,1) ; vy#=bbVertexY(s,1) ; vz#=bbVertexZ(s,1)
		bbVertexCoords s,1,bbVertexX(s,0),bbVertexY(s,0),bbVertexZ(s,0)
		bbVertexCoords s,0,vx,vy,vz
		vx#=bbVertexX(s,3) ; vy#=bbVertexY(s,3) ; vz#=bbVertexZ(s,3)
		bbVertexCoords s,3,bbVertexX(s,2),bbVertexY(s,2),bbVertexZ(s,2)
		bbVertexCoords s,2,vx,vy,vz
	EndIf
	If flipY
		vx#=bbVertexX(s,2) ; vy#=bbVertexY(s,2) ; vz#=bbVertexZ(s,2)
		bbVertexCoords s,2,bbVertexX(s,0),bbVertexY(s,0),bbVertexZ(s,0)
		bbVertexCoords s,0,vx,vy,vz
		vx#=bbVertexX(s,3) ; vy#=bbVertexY(s,3) ; vz#=bbVertexZ(s,3)
		bbVertexCoords s,3,bbVertexX(s,1),bbVertexY(s,1),bbVertexZ(s,1)
		bbVertexCoords s,1,vx,vy,vz
	EndIf

End Function

' Rotate a quad sprite by ang# angle
'       0 = normal
'      90 = clockwise by 90 degrees
'     180 = upside down
'     270 = anti-clockwise by 90 degrees
Function RotateImage3D(sprite,angle#)

	bbRotateEntity sprite,0,0,-angle

End Function

' Set the handle/axis of a quad sprite (courtesy of FredBorg)
'   handleX ->   -1 is left   0 is centre   1 is right
'   handleY ->   -1 is top    0 is centre   1 is bottom
Function HandleImage3D(sprite,handleX#,handleY#)

	MidHandle3D sprite
	Local s=bbGetSurface(sprite,1)
	bbVertexCoords s,0,bbVertexX(s,0)-handleX,bbVertexY(s,0)+handleY,bbVertexZ(s,0)
	bbVertexCoords s,1,bbVertexX(s,1)-handleX,bbVertexY(s,1)+handleY,bbVertexZ(s,1)
	bbVertexCoords s,2,bbVertexX(s,2)-handleX,bbVertexY(s,2)+handleY,bbVertexZ(s,2)
	bbVertexCoords s,3,bbVertexX(s,3)-handleX,bbVertexY(s,3)+handleY,bbVertexZ(s,3)

End Function

' Move the quad sprites handle/axis to the center (courtesy of bot builder)
Function MidHandle3D(sprite)

	Local ux#=-100000 , uy#=-100000 , uz#=-100000
	Local lx#=100000 , ly#=100000 , lz#=100000
	Local s=bbGetSurface(sprite,1)
	For v=0 To 3
		vx#=bbVertexX#(s,v) ; vy#=bbVertexY#(s,v) ; vz#=bbVertexZ#(s,v)
		If vx#<lx# Then lx#=vx#
		If vx#>ux# Then ux#=vx#
		If vy#<ly# Then ly#=vy#
		If vy#>uy# Then uy#=vy#
		If vz#<lz# Then lz#=vz#
		If vz#>uz# Then uz#=vz#
	Next
	ax#=(ux#+lx#)/2 ; ay#=(uy#+ly#)/2 ; az#=(uz#+lz#)/2
	bbPositionMesh sprite,-ax#,-ay#,-az#

End Function

' Return the X handle/axis position (-1 = Left  0 = Centre  +1 = Right)
Function ImageXHandle3D#(sprite)

	Return -bbVertexX(bbGetSurface(sprite,1),1)+1

End Function

' Return the Y handle/axis position (-1= Top  0 = Centre  +1 = Bottom)
Function ImageYHandle3D#(sprite)

	Return bbVertexY(bbGetSurface(sprite,1),0)-1

End Function

' Return width of a quad sprite in screen pixel dimensions
Function ImageWidth3D(sprite)

	Local x#=bbGetMatElement(sprite,0,0)
	Local y#=bbGetMatElement(sprite,0,1)
	Local z#=bbGetMatElement(sprite,0,2)
	Return Sqr(x*x+y*y+z*z) * bbGraphicsWidth()

End Function

' Return height of a quad sprite in screen pixel dimensions
Function ImageHeight3D(sprite)

	Local x#=bbGetMatElement(sprite,1,0)
	Local y#=bbGetMatElement(sprite,1,1)
	Local z#=bbGetMatElement(sprite,1,2)
	Return Sqr(x*x+y*y+z*z) * bbGraphicsWidth()

End Function

' Apply a color mask to the sprites texture (thanks to BlitzSupport)
Function MaskImage3D(sprite,r,g,b,numframes=1)
	Local temp=bbCreateTexture(1,1,flags)
	bbWritePixel 0,0,((r Shl 16)+(g Shl 8)+b) & $00FFFFFF,bbTextureBuffer(temp)
	Local trgb=bbReadPixel(0,0,bbTextureBuffer(temp))
	r=trgb Shr 16 & $ff ; g=trgb Shr 8 & $ff ; b=trgb & $ff
	bbFreeTexture temp
	Local gb=bbGraphicsBuffer()
	Local tex=GetSpriteTexture(sprite)
	For frm=1 To numframes
		bbSetBuffer bbTextureBuffer(tex,frm-1)
		bbLockBuffer bbGraphicsBuffer()
		For x=0 To bbTextureWidth(tex)-1
			For y=0 To bbTextureHeight(tex)- 1
				Local rgb=bbReadPixelFast(x,y) & $00FFFFFF
				If rgb=((r Shl 16)+(g Shl 8)+b)
					bbWritePixelFast x,y,$00000000
				Else
					bbWritePixelFast x,y,rgb | $FF000000
				EndIf
			Next
		Next
		bbUnlockBuffer bbGraphicsBuffer()
	Next
	bbSetBuffer gb
End Function

' Return TRUE if two sprite images are overlapping
Function ImagesOverlap3D(sprite1,sprite2,ovl1=0,ovl2=0)

	Local iw1=ImageWidth3D(sprite1) , ih1=ImageHeight3D(sprite1)
	Local iw2=ImageWidth3D(sprite2) , ih2=ImageHeight3D(sprite2)
	Local fX=1,fY=1 , s=bbGetSurface(sprite1,1)
	If bbVertexX(s,0)>bbVertexX(s,1) fX=-1
	If bbVertexY(s,0)<bbVertexY(s,2) fY=3
	Local ix1#=Abs(bbEntityX(sprite1))-((ImageXHandle3D(sprite1)+fX)/2)*iw1+0.5
	Local iy1#=Abs(bbEntityY(sprite1))-((ImageYHandle3D(sprite1)+fY)/2)*ih1+0.5
	fX=1 ; fY=1 ;	s=bbGetSurface(sprite2,1)
	If bbVertexX(s,0)>bbVertexX(s,1) fX=-1
	If bbVertexY(s,0)<bbVertexY(s,2) fY=3
	Local ix2#=Abs(bbEntityX(sprite2))-((ImageXHandle3D(sprite2)+fX)/2)*iw2+0.5
	Local iy2#=Abs(bbEntityY(sprite2))-((ImageYHandle3D(sprite2)+fY)/2)*ih2+0.5
	Return bbRectsOverlap(ix1+ovl1,iy1+ovl1,iw1-ovl1,ih1-ovl1 , ix2+ovl2,iy2+ovl2,iw2-ovl2,ih2-ovl2)
	
End Function

' Convert an existing 2D image to quad sprite
Function ImageToSprite(img,texflags=5,numframesX=1,numframesY=1,par=0)

	If par=0 Then par=spritepivot
	Local iw=bbImageWidth(img)
	Local ih=bbImageHeight(img)
	Local tw=2 Shl (Bin(iw-1).Replace("0"," ").Trim().length-1)
	Local th=2 Shl (Bin(iw-1).Replace("0"," ").Trim().length-1)
	Local tex=bbCreateTexture(tw,th,texflags)
	Local ib=bbImageBuffer(img) ; bbLockBuffer ib
	Local tb=bbTextureBuffer(tex) ; bbLockBuffer tb
	Local x,y
	For x=0 To iw-1
		For y=0 To ih-1
			rgbc=bbReadPixelFast(x,y,ib) & $00ffffff
			If rgbc=((r Shl 16)+(g Shl 8)+b)
				bbWritePixelFast x,y,($00000000),tb
			Else
				bbWritePixelFast x,y,(rgbc | $ff000000),tb
			EndIf
		Next
	Next
	bbUnlockBuffer ib ; bbUnlockBuffer tb
	
	Local sprite=CreateImage3D(iw,ih,par,1.0/Float(numframesX),1.0/Float(numframesY))
	bbEntityTexture sprite,tex
	bbEntityFX sprite,16+1
	bbEntityOrder sprite,-100
	bbScaleEntity sprite,Float(iw/numframesX)/2.0,Float(ih/numframesY)/2.0,1.0
	Return sprite
	
End Function

' Flush out an existing quad sprite and it's texture
Function FreeImage3D(sprite)

	Local brush=bbGetEntityBrush(sprite)
	Local tex=bbGetBrushTexture(brush)
	If brush<>0 bbFreeBrush brush
	If tex<>0 bbFreeTexture tex
	If sprite<>0 bbFreeEntity sprite

End Function

' Return the texture handle of a quad sprite
Function GetSpriteTexture(sprite)

	Local brush=bbGetEntityBrush(sprite)
	Local tex=bbGetBrushTexture(brush)
	bbFreeBrush brush
	Return tex

End Function


EDIT: Little fix added, anyone's already tried it use this instead. Parent default was -1 when in B3DSDK should be 0.