Author Topic: [Bmax] Software render framework  (Read 21629 times)

0 Members and 1 Guest are viewing this topic.

Offline zawran

  • Sponsor
  • Pentium
  • *******
  • Posts: 909
  • Karma: 67
    • View Profile
[Bmax] Software render framework
« on: July 10, 2009 »
I was bored so I got the idea of making the start of a software render framework that uses the build-in pixmap feature of Blitzmax to render the screen buffer. Blitzmax has two ways of storing images. One is the regular image format, the other is pixmaps. Regular image format does not allow for direct manipulation of the image data, pixmaps does. So what I did was setup a type to handle the image buffers and some methods to manipulate these. So far its more or less just code to make these, load images into them, and simple scrolling and copying. I will when time permits add more manipulation methods, but everyone is free to assist and add to this if they like. The code is public domain and you may use it as you like, no strings attached, no warranties provided.

The framework type itself:
Code: [Select]
' The following code is public domain, use it as you like, in parts or as it is. Feel free to modify as you see fit.
' No warranties are provided for the code.
' All code written by Johnny Bremer (aka. Zawran), unless otherwise mentioned

Type zImageBuffer

Global zImageBufferList:TList = CreateList()

Field link:TLink ' link pointer
Field buffer:TPixmap ' buffer where pixmap is stored
Field width:Int ' width of image
Field height:Int ' height of image
Field pitch:Int ' pitch of image
Field bcount:Int ' byte count of each pixel 3/4 depending on alpha
Field mask:Int[3] ' masking color array

' create a new image buffer
Function make:zImageBuffer(width:Int,height:Int,maskrgb:Int=$000000,fillrgb:Int=$FF000000)
Local tmp:zImageBuffer = New zImageBuffer
tmp.buffer = CreatePixmap(width,height,PF_RGBA8888)
tmp.width = tmp.buffer.width
tmp.height = tmp.buffer.height
tmp.pitch = PixmapPitch(tmp.buffer)
tmp.bcount = tmp.pitch / tmp.width
tmp.mask[0] = (maskrgb Shr 16) & 255
tmp.mask[1] = (maskrgb Shr 8) & 255
tmp.mask[2] = maskrgb & 255
ClearPixels(tmp.buffer,fillrgb)
tmp.link = ListAddLast(zImageBufferList,tmp)
Return tmp
End Function

' load an image and create an image buffer from that
Function loadfile:zImageBuffer(path:String,maskrgb:Int=$000000)
Local tmp:zImageBuffer = New zImageBuffer
tmp.buffer = LoadPixmap(path)
tmp.width = tmp.buffer.width
tmp.height = tmp.buffer.height
tmp.pitch = PixmapPitch(tmp.buffer)
tmp.bcount = tmp.pitch / tmp.width
tmp.mask[0] = (maskrgb Shr 16) & 255
tmp.mask[1] = (maskrgb Shr 8) & 255
tmp.mask[2] = maskrgb & 255
tmp.link = ListAddLast(zImageBufferList,tmp)
Return tmp
End Function

' copy an already existing image buffer
Function copy:zImageBuffer(from:zImageBuffer)
Local tmp:zImageBuffer = New zImageBuffer
tmp.buffer = CopyPixmap(from.buffer)
tmp.width = tmp.buffer.width
tmp.height = tmp.buffer.height
tmp.pitch = PixmapPitch(tmp.buffer)
tmp.bcount = tmp.pitch / tmp.width
tmp.mask = from.mask
tmp.link = ListAddLast(zImageBufferList,tmp)
Return tmp
End Function

Function rotatecopy:zImageBuffer(from:zImageBuffer,angle:Int)
Local tmp:zImagebuffer = zImageBuffer.copy(from)
tmp.clear()
Local r:Byte,g:Byte,b:Byte,a:Byte
Local wh:Int = (tmp.width/2)-1
Local hh:Int = (tmp.height/2)-1
For Local y:Int = 0 Until tmp.height
For Local x:Int = 0 Until tmp.width
Local tx:Int = (x-wh) * Cos(angle) - (y-hh) * Sin(angle) + wh
Local ty:Int = (y-hh) * Cos(angle) + (x-wh) * Sin(angle) + hh
If tx > -1 And tx < tmp.width And ty > -1 And ty < tmp.height Then
from.getpixel(tx,ty,r,g,b,a)
tmp.setpixel(x,y,r,g,b,a)
End If
Next
Next
Return tmp
End Function

Method setmask(maskrgb:Int=$000000)
Self.mask[0] = (maskrgb Shr 16) & 255
Self.mask[1] = (maskrgb Shr 8) & 255
Self.mask[2] = maskrgb & 255
End Method

' copy a rectangle from one image buffer to another as a block without masking
Method copyrectblock(from:zImageBuffer,x:Int,y:Int,w:Int,h:Int,tx:Int,ty:Int)
Local pixTargetPtr:Byte Ptr = PixmapPixelPtr(Self.buffer)
Local pixSourcePtr:Byte Ptr = PixmapPixelPtr(from.buffer)
For Local yy:Int = 0 Until h
For Local xx:Int = 0 Until w
' is target pixel within the target buffer
If tx+xx > -1 And ty+yy > -1 And tx+xx < Self.width And ty+yy < Self.height Then
' is source pixel within the source buffer
If x+xx > -1 And y+yy > -1 And x+xx < from.width And y+yy < from.height Then
pixTargetPtr[(ty+yy)*Self.pitch+(tx+xx)*Self.bcount] = pixSourcePtr[(y+yy)*from.pitch+(x+xx)*from.bcount]
pixTargetPtr[(ty+yy)*Self.pitch+(tx+xx)*Self.bcount+1] = pixSourcePtr[(y+yy)*from.pitch+(x+xx)*from.bcount+1]
pixTargetPtr[(ty+yy)*Self.pitch+(tx+xx)*Self.bcount+2] = pixSourcePtr[(y+yy)*from.pitch+(x+xx)*from.bcount+2]
' check to see if both images include alpha value that needs to be copied as well
If Self.bcount = 4 And from.bcount = 4 Then pixTargetPtr[(ty+yy)*Self.pitch+(tx+xx)*Self.bcount+3] = pixSourcePtr[(y+yy)*from.pitch+(x+xx)*from.bcount+3]
End If
End If
Next
Next
End Method

' copy a rectangle from one image buffer to another using masking
Method copyrect(from:zImageBuffer,x:Int,y:Int,w:Int,h:Int,tx:Int,ty:Int)
Local pixTargetPtr:Byte Ptr = PixmapPixelPtr(Self.buffer)
Local pixSourcePtr:Byte Ptr = PixmapPixelPtr(from.buffer)
For Local yy:Int = 0 Until h
For Local xx:Int = 0 Until w
' is target pixel within the target buffer
If tx+xx > -1 And ty+yy > -1 And tx+xx < Self.width And ty+yy < Self.height Then
' is source pixel within the source buffer
If x+xx > -1 And y+yy > -1 And x+xx < from.width And y+yy < from.height Then
If pixSourcePtr[(y+yy)*from.pitch+(x+xx)*from.bcount] <> from.mask[0] Or pixSourcePtr[(y+yy)*from.pitch+(x+xx)*from.bcount+1] <> from.mask[1] Or pixSourcePtr[(y+yy)*from.pitch+(x+xx)*from.bcount+2] <> from.mask[2] Then
pixTargetPtr[(ty+yy)*Self.pitch+(tx+xx)*Self.bcount] = pixSourcePtr[(y+yy)*from.pitch+(x+xx)*from.bcount]
pixTargetPtr[(ty+yy)*Self.pitch+(tx+xx)*Self.bcount+1] = pixSourcePtr[(y+yy)*from.pitch+(x+xx)*from.bcount+1]
pixTargetPtr[(ty+yy)*Self.pitch+(tx+xx)*Self.bcount+2] = pixSourcePtr[(y+yy)*from.pitch+(x+xx)*from.bcount+2]
' check to see if both images include alpha value that needs to be copied as well
If Self.bcount = 4 And from.bcount = 4 Then pixTargetPtr[(ty+yy)*Self.pitch+(tx+xx)*Self.bcount+3] = pixSourcePtr[(y+yy)*from.pitch+(x+xx)*from.bcount+3]
End If
End If
End If
Next
Next
End Method

Method setpixel(x:Int,y:Int,r:Byte,g:Byte,b:Byte,a:Byte=255)
Local pixPtr:Byte Ptr = PixmapPixelPtr(Self.buffer)
If x > -1 And x < Self.width And y > -1 And y < Self.height Then
pixPtr[y*Self.pitch+x*Self.bcount] = r
pixPtr[y*Self.pitch+x*Self.bcount+1] = g
pixPtr[y*Self.pitch+x*Self.bcount+2] = b
If Self.bcount = 4 Then pixPtr[y*Self.pitch+x*Self.bcount+3] = a
End If
End Method

Method getpixel(x:Int,y:Int,r:Byte Var,g:Byte Var,b:Byte Var,a:Byte Var)
Local pixPtr:Byte Ptr = PixmapPixelPtr(Self.buffer)
If x > -1 And x < Self.width And y > -1 And y < Self.height Then
r = pixPtr[y*Self.pitch+x*Self.bcount]
g = pixPtr[y*Self.pitch+x*Self.bcount+1]
b = pixPtr[y*Self.pitch+x*Self.bcount+2]
If Self.bcount = 4 Then a = pixPtr[y*Self.pitch+x*Self.bcount+3]
End If
End Method

Method noise(x:Int,y:Int,width:Int,height:Int,noiselevel:Int)
Local pixPtr:Byte Ptr = PixmapPixelPtr(Self.buffer)
For Local ty:Int = y To y+height-1
For Local tx:Int = x To x+width-1
If tx > -1 And tx < Self.width And ty > -1 And ty < Self.height Then
Local offset:Int = ty*Self.pitch+tx*Self.bcount
Local n:Int = Rnd(-noiselevel,noiselevel)
If pixPtr[offset] + n < 0 Then
pixPtr[offset] = 0
Else
If pixPtr[offset] + n > 255 Then
pixPtr[offset] = 255
Else
pixPtr[offset] :+ n
End If
End If
If pixPtr[offset+1] + n < 0 Then
pixPtr[offset+1] = 0
Else
If pixPtr[offset+1] + n > 255 Then
pixPtr[offset+1] = 255
Else
pixPtr[offset+1] :+ n
End If
End If
If pixPtr[offset+2] + n < 0 Then
pixPtr[offset+2] = 0
Else
If pixPtr[offset+2] + n > 255 Then
pixPtr[offset+2] = 255
Else
pixPtr[offset+2] :+ n
End If
End If
End If
Next
Next
End Method

Method colorSubtract(x:Int,y:Int,width:Int,height:Int,r:Byte,g:Byte,b:Byte,a:Byte)
Local pixPtr:Byte Ptr = PixmapPixelPtr(Self.buffer)
For Local ty:Int = y To y+height-1
For Local tx:Int = x To x+width-1
If tx > -1 And tx < Self.width And ty > -1 And ty < Self.height Then
Local offset:Int = ty*Self.pitch+tx*Self.bcount
If pixPtr[offset] - r < 0 Then
pixPtr[offset] = 0
Else
pixPtr[offset] = pixPtr[offset]-r
End If
If pixPtr[offset+1] - g < 0 Then
pixPtr[offset+1] = 0
Else
pixPtr[offset+1] = pixPtr[offset+1]-g
End If
If pixPtr[offset+2] - b < 0 Then
pixPtr[offset+2] = 0
Else
pixPtr[offset+2] = pixPtr[offset+2]-b
End If
If Self.bcount = 4 Then
If pixPtr[offset+3] - a < 0 Then
pixPtr[offset+3] = 0
Else
pixPtr[offset+3] = pixPtr[offset+3]-a
End If
End If
End If
Next
Next
End Method

Method colorAddition(x:Int,y:Int,width:Int,height:Int,r:Byte,g:Byte,b:Byte,a:Byte)
Local pixPtr:Byte Ptr = PixmapPixelPtr(Self.buffer)
For Local ty:Int = y To y+height-1
For Local tx:Int = x To x+width-1
If tx > -1 And tx < Self.width And ty > -1 And ty < Self.height Then
Local offset:Int = ty*Self.pitch+tx*Self.bcount
If r + pixPtr[offset] > 255 Then
pixPtr[offset] = 255
Else
pixPtr[offset] = pixPtr[offset]+r
End If
If g + pixPtr[offset+1] > 255 Then
pixPtr[offset+1] = 255
Else
pixPtr[offset+1] = pixPtr[offset+1]+g
End If
If b + pixPtr[offset+2] > 255 Then
pixPtr[offset+2] = 255
Else
pixPtr[offset+2] = pixPtr[offset+2]+b
End If
If Self.bcount = 4 Then
If a + pixPtr[offset+3] > 255 Then
pixPtr[offset+3] = 255
Else
pixPtr[offset+3] = pixPtr[offset+3]+a
End If
End If
End If
Next
Next
End Method

Method drawBlock(toImg:zImagebuffer,x:Int,y:Int)
Local pixSourcePtr:Byte Ptr = PixmapPixelPtr(Self.buffer)
Local pixDestPtr:Byte Ptr = PixmapPixelPtr(toImg.buffer)
For Local ty:Int = 0 Until Self.height
For Local tx:Int = 0 Until Self.width
If tx+x > -1 And tx+x < toImg.width And ty+y > -1 And ty+y < toImg.height Then
Local offsetS:Int = ty*Self.pitch+tx*Self.bcount
Local offsetD:Int = (ty+y)*toImg.pitch+(tx+x)*toImg.bcount
pixDestPtr[offsetD] = pixSourcePtr[offsetS]
pixDestPtr[offsetD+1] = pixSourcePtr[offsetS+1]
pixDestPtr[offsetD+2] = pixSourcePtr[offsetS+2]
If Self.bcount = 4 And toImg.bcount = 4 Then pixDestPtr[offsetD+3] = pixSourcePtr[offsetS+3]
End If
Next
Next
End Method

Method drawMasked(toImg:zImageBuffer,x:Int,y:Int)
Local pixSourcePtr:Byte Ptr = PixmapPixelPtr(Self.buffer)
Local pixDestPtr:Byte Ptr = PixmapPixelPtr(toImg.buffer)
For Local ty:Int = 0 Until Self.height
For Local tx:Int = 0 Until Self.width
If tx+x > -1 And tx+x < toImg.width And ty+y > -1 And ty+y < toImg.height Then
If pixSourcePtr[ty*Self.pitch+tx*Self.bcount] <> Self.mask[0] Or pixSourcePtr[ty*Self.pitch+tx*Self.bcount+1] <> Self.mask[1] Or pixSourcePtr[ty*Self.pitch+tx*Self.bcount+2] <> Self.mask[2] Then
Local offsetS:Int = ty*Self.pitch+tx*Self.bcount
Local offsetD:Int = (ty+y)*toImg.pitch+(tx+x)*toImg.bcount
pixDestPtr[offsetD] = pixSourcePtr[offsetS]
pixDestPtr[offsetD+1] = pixSourcePtr[offsetS+1]
pixDestPtr[offsetD+2] = pixSourcePtr[offsetS+2]
If Self.bcount = 4 And toImg.bcount = 4 Then pixDestPtr[offsetD+3] = pixSourcePtr[offsetS+3]
End If
End If
Next
Next
End Method

Method drawScaledBlock(toImg:zImageBuffer,x:Int,y:Int,scaleX:Float,scaleY:Float)
Local pixSourcePtr:Byte Ptr = PixmapPixelPtr(Self.buffer)
Local pixDestPtr:Byte Ptr = PixmapPixelPtr(toImg.buffer)
Local sw:Int = Self.width * scaleX
Local sh:Int = Self.height * scaleY
Local deltaX:Float = Self.width / Float(sw)
Local deltaY:Float = Self.height / Float(sh)
Local cx:Float=0.0,cy:Float=0.0
For Local ty:Int = 0 Until sh
cx = 0.0
For Local tx:Int = 0 Until sw
If tx+x > -1 And tx+x < toImg.width And ty+y > -1 And ty+y < toImg.height Then
Local offsetS:Int = Int(cy)*Self.pitch+Int(cx)*Self.bcount
Local offsetD:Int = (ty+y)*toImg.pitch+(tx+x)*toImg.bcount
pixDestPtr[offsetD] = pixSourcePtr[offsetS]
pixDestPtr[offsetD+1] = pixSourcePtr[offsetS+1]
pixDestPtr[offsetD+2] = pixSourcePtr[offsetS+2]
If Self.bcount = 4 And toImg.bcount = 4 Then pixDestPtr[offsetD+3] = pixSourcePtr[offsetS+3]
End If
cx :+ deltaX
Next
cy :+ deltaY
Next
End Method

Method drawScaledMasked(toImg:zImageBuffer,x:Int,y:Int,scaleX:Float,scaleY:Float)
Local pixSourcePtr:Byte Ptr = PixmapPixelPtr(Self.buffer)
Local pixDestPtr:Byte Ptr = PixmapPixelPtr(toImg.buffer)
Local sw:Int = Self.width * scaleX
Local sh:Int = Self.height * scaleY
Local deltaX:Float = Self.width / Float(sw)
Local deltaY:Float = Self.height / Float(sh)
Local cx:Float=0.0,cy:Float=0.0
For Local ty:Int = 0 Until sh
cx = 0.0
For Local tx:Int = 0 Until sw
If tx+x > -1 And tx+x < toImg.width And ty+y > -1 And ty+y < toImg.height Then
If pixSourcePtr[Int(cy)*Self.pitch+Int(cx)*Self.bcount] <> Self.mask[0] Or pixSourcePtr[Int(cy)*Self.pitch+Int(cx)*Self.bcount+1] <> Self.mask[1] Or pixSourcePtr[Int(cy)*Self.pitch+Int(cx)*Self.bcount+2] <> Self.mask[2] Then
Local offsetS:Int = Int(cy)*Self.pitch+Int(cx)*Self.bcount
Local offsetD:Int = (ty+y)*toImg.pitch+(tx+x)*toImg.bcount
pixDestPtr[offsetD] = pixSourcePtr[offsetS]
pixDestPtr[offsetD+1] = pixSourcePtr[offsetS+1]
pixDestPtr[offsetD+2] = pixSourcePtr[offsetS+2]
If Self.bcount = 4 And toImg.bcount = 4 Then pixDestPtr[offsetD+3] = pixSourcePtr[offsetS+3]
End If
End If
cx :+ deltaX
Next
cy :+ deltaY
Next
End Method

' save an image buffer to file
Method savefile(path:String="image.png",comp:Int=5)
SavePixmapPNG(Self.buffer,path,comp)
End Method

' clear an image buffer with a set RGBA color
Method clear(rgba:Int=$FF000000)
ClearPixels(Self.buffer,rgba)
End Method

' draw an image buffer to screen
Method blitToScreen(x:Int=0,y:Int=0)
DrawPixmap(Self.buffer,x,y)
End Method

' remove an image buffer
Method remove()
Self.buffer = Null
RemoveLink(Self.link)
End Method

Method line(x1:Float,y1:Float,x2:Float,y2:Float,r:Byte,g:Byte,b:Byte)
Local pixPtr:Byte Ptr = PixmapPixelPtr(Self.buffer)
Local steps:Float,xI:Float,i:Int

x2 :- x1
y2 :- y1

If Abs(x2) > Abs(y2) Then
steps = Abs(x2)
Else
steps = Abs(y2)
End If

xI = Float(x2 / steps)
y2 = Float(y2 / steps)

For x2 = 0 To steps
If Int(y1) > -1 And Int(y1) < Self.height Then
If Int(x1) > -1 And Int(x1) < Self.width Then
Local offset:Int = Int(y1)*Self.pitch+Int(x1)*Self.bcount
pixPtr[offset] = r
pixPtr[offset+1] = g
pixPtr[offset+2] = b
If Self.bcount = 4 Then pixPtr[offset+3] = 255
End If
End If
x1 :+ xI
y1 :+ y2
Next
End Method

Method rect(x1:Int,y1:Int,x2:Int,y2:Int,r:Byte,g:Byte,b:Byte)
For Local i:Int = y1 To y2
Self.line(x1,i,x2,i,r,g,b)
Next
End Method

Method mirror(direction:Int)
Local pixPtr:Byte Ptr = PixmapPixelPtr(Self.buffer)
Local tmp:Byte,x:Int,y:Int

Select direction
Case 0 ' horizontal
For x = 0 To (Self.width/2)-1
For y = 0 Until Self.height
tmp = pixPtr[y*Self.pitch+x*Self.bcount]
pixPtr[y*Self.pitch+x*Self.bcount] = pixPtr[y*Self.pitch+(Self.width-x-1)*Self.bcount]
pixPtr[y*Self.pitch+(Self.width-x-1)*Self.bcount] = tmp
tmp = pixPtr[y*Self.pitch+x*Self.bcount+1]
pixPtr[y*Self.pitch+x*Self.bcount+1] = pixPtr[y*Self.pitch+(Self.width-x-1)*Self.bcount+1]
pixPtr[y*Self.pitch+(Self.width-x-1)*Self.bcount+1] = tmp
tmp = pixPtr[y*Self.pitch+x*Self.bcount+2]
pixPtr[y*Self.pitch+x*Self.bcount+2] = pixPtr[y*Self.pitch+(Self.width-x-1)*Self.bcount+2]
pixPtr[y*Self.pitch+(Self.width-x-1)*Self.bcount+2] = tmp
If Self.bcount = 4 Then
tmp = pixPtr[y*Self.pitch+x*Self.bcount+3]
pixPtr[y*Self.pitch+x*Self.bcount+3] = pixPtr[y*Self.pitch+(Self.width-x-1)*Self.bcount+3]
pixPtr[y*Self.pitch+(Self.width-x-1)*Self.bcount+3] = tmp
End If
Next
Next
Case 1 ' vertical
For y = 0 To (Self.height/2)-1
For x = 0 Until Self.width
tmp = pixPtr[y*Self.pitch+x*Self.bcount]
pixPtr[y*Self.pitch+x*Self.bcount] = pixPtr[(Self.height-y-1)*Self.pitch+x*Self.bcount]
pixPtr[(Self.height-y-1)*Self.pitch+x*Self.bcount] = tmp
tmp = pixPtr[y*Self.pitch+x*Self.bcount+1]
pixPtr[y*Self.pitch+x*Self.bcount+1] = pixPtr[(Self.height-y-1)*Self.pitch+x*Self.bcount+1]
pixPtr[(Self.height-y-1)*Self.pitch+x*Self.bcount+1] = tmp
tmp = pixPtr[y*Self.pitch+x*Self.bcount+2]
pixPtr[y*Self.pitch+x*Self.bcount+2] = pixPtr[(Self.height-y-1)*Self.pitch+x*Self.bcount+2]
pixPtr[(Self.height-y-1)*Self.pitch+x*Self.bcount+2] = tmp
If Self.bcount = 4 Then
tmp = pixPtr[y*Self.pitch+x*Self.bcount+3]
pixPtr[y*Self.pitch+x*Self.bcount+3] = pixPtr[(Self.height-y-1)*Self.pitch+x*Self.bcount+3]
pixPtr[(Self.height-y-1)*Self.pitch+x*Self.bcount+3] = tmp
End If
Next
Next
Case 2 ' horizontal + vertical
Self.mirror(0)
Self.mirror(1)
End Select
End Method

' shift the content of an image buffer in one of four directions: 0=left, 1=right, 2=up, 3=down
Method shift(direction:Int,pixels:Int)
Local pixPtr:Byte Ptr = PixmapPixelPtr(Self.buffer)

Select direction
Case 0 ' left
Local moveSize:Int = (Self.width-pixels)*Self.bcount
Local offset:Int = pixels*Self.bcount
For Local y:Int = 0 Until Self.height
MemCopy(pixPtr,pixPtr+offset,moveSize)
pixPtr :+ pitch
Next
Case 1 ' right
Local pixSize:Int = pitch/Self.width
Local moveSize:Int = (Self.width-pixels)*Self.bcount
Local offset:Int = pixels*Self.bcount
For Local y:Int = 0 Until Self.height
MemCopy(pixPtr+offset,pixPtr,moveSize)
pixPtr :+ pitch
Next
Case 2 ' up
Local pixSize:Int = pitch/Self.width
For Local y:Int = 0 To Self.height-1-pixels
MemCopy(pixPtr,pixPtr+pitch*pixels,pitch)
pixPtr :+ pitch
Next
Case 3 ' down
Local pixSize:Int = pitch/Self.width
pixPtr :+ ((Self.width*Self.bcount)*(Self.height-1))
For Local y:Int = 0 To Self.height-1-pixels
MemCopy(pixPtr,pixPtr-(pitch*pixels),pitch)
pixPtr :- pitch
Next
End Select
End Method

End Type

Type zSpriteSheet

Global zSpriteSheetList:TList = CreateList()

Field link:TLink ' link pointer
Field image:zImageBuffer ' image buffer where sprites pixmap is stored
Field spriteCount:Int ' number of sprites on sheet
Field spriteX:Int[] ' x position of top left pixel in sprite
Field spriteY:Int[] ' y position of top left pixel in sprite
Field spriteW:Int[] ' width of sprite
Field spriteH:Int[] ' height of sprite

' create a new image buffer
Function loadfontsheet:zSpriteSheet(path:String,maskrgb:Int=$000000)
Local tmp:zSpriteSheet = New zSpriteSheet
tmp.image = zImageBuffer.loadfile(path,maskrgb)
Local f:String = Left(path,Len(path)-3)+"fxb"
Local filein:TStream = OpenFile(f)
Local t:Int = ReadInt(filein) ' font height not used
tmp.spriteCount = ReadInt(filein)
tmp.spriteX = New Int[tmp.spriteCount]
tmp.spriteY = New Int[tmp.spriteCount]
tmp.spriteW = New Int[tmp.spriteCount]
tmp.spriteH = New Int[tmp.spriteCount]
For Local i:Int = 0 Until tmp.spriteCount
t = ReadInt(filein) ' ascii code not used
tmp.spriteX[i] = ReadInt(filein)
tmp.spriteY[i] = ReadInt(filein)
tmp.spriteW[i] = ReadInt(filein)
tmp.spriteH[i] = ReadInt(filein)
Next
tmp.link = ListAddLast(zSpriteSheetList,tmp)
Return tmp
End Function

Method draw(toImg:zImageBuffer,x:Int,y:Int,sprite:Int)
toImg.copyrect(Self.image,Self.spriteX[sprite],Self.spriteY[sprite],Self.spriteW[sprite],Self.spriteH[sprite],x,y)
End Method

' remove sprite sheet
Method remove()
Self.image.remove() ' remove sprite image
RemoveLink(Self.link) ' remove sprite sheet
End Method

End Type

Quick short test code.

Code: [Select]
SuperStrict

Framework BRL.GLMax2D
Import BRL.Pixmap
Import BRL.PNGLoader

SetGraphicsDriver GLMax2DDriver()

Include "zImageBuffer.bmx"

Graphics 640,480

Global timerUpd:Int = MilliSecs()
Global timerFrq:Int = 1000/30 ' 30 fps

' create an image buffer to store the screen in
Global screenBuffer:zImageBuffer = zImageBuffer.make(640,480)
screenBuffer.clear($FF000000)
' load an image into a new image buffer
Global tmpo:zImageBuffer = zImageBuffer.loadfile("background.png")
' copy half of the loaded image into the screen buffer image
screenBuffer.copyRect(tmpo,320,0,320,480,0,0)

While Not KeyHit(KEY_ESCAPE)
Cls
screenBuffer.draw()
Flip

While MilliSecs() < timerUpd+timerFrq
Wend
timerUpd = MilliSecs()

screenBuffer.shift(3,1)

Wend
End

Compiled example and source code can be found here: http://zac-interactive.dk/temp/srf.zip
« Last Edit: August 08, 2009 by zawran »

Offline Shockwave

  • good/evil
  • Founder Member
  • DBF Aficionado
  • ********
  • Posts: 17409
  • Karma: 498
  • evil/good
    • View Profile
    • My Homepage
Re: [Bmax] Software render framework
« Reply #1 on: July 10, 2009 »
You always pick the nicest pics to use in your things :)

K+ for sharing this nice program.

Shockwave ^ Codigos
Challenge Trophies Won:

Offline zawran

  • Sponsor
  • Pentium
  • *******
  • Posts: 909
  • Karma: 67
    • View Profile
Re: [Bmax] Software render framework
« Reply #2 on: July 10, 2009 »
Quote
You always pick the nicest pics to use in your things
 Hehe, thanks :)

[edit] I added a couple of things. There is a new method for the framework, which will mirror the image buffer horizontal, vertical or both. I also added masking color to the image buffer type so that a masking color can be set for the buffers. This also ment that I had to make an additional copyrect method which will use the masking colors when copying the rect from one image buffer to another. I therefore renamed the old copyrect method to copyrectblock. I also added a method to allow for changing the masking color seperately.

Code: [Select]
' The following code is public domain, use it as you like, in parts or as it is. Feel free to modify as you see fit.
' No warranties are provided for the code, if it works then fine, if not then ah well, I tried :)

Type zImageBuffer

Global zImageBufferList:TList = CreateList()

Field link:TLink ' link pointer
Field buffer:TPixmap ' buffer where pixmap is stored
Field width:Int ' width of image
Field height:Int ' height of image
Field pitch:Int ' pitch of image
Field bcount:Int ' byte count of each pixel 3/4 depending on alpha
Field mask:Int[3] ' masking color array

' create a new image buffer
Function make:zImageBuffer(width:Int,height:Int,maskrgb:Int=$000000)
Local tmp:zImageBuffer = New zImageBuffer
tmp.buffer = CreatePixmap(width,height,PF_RGBA8888)
tmp.width = tmp.buffer.width
tmp.height = tmp.buffer.height
tmp.pitch = PixmapPitch(tmp.buffer)
tmp.bcount = tmp.pitch / tmp.width
tmp.mask[0] = (maskrgb Shr 16) & 255
tmp.mask[1] = (maskrgb Shr 8) & 255
tmp.mask[2] = maskrgb & 255
tmp.link = ListAddLast(zImageBufferList,tmp)
Return tmp
End Function

' load an image and create an image buffer from that
Function loadfile:zImageBuffer(path:String,maskrgb:Int=$000000)
Local tmp:zImageBuffer = New zImageBuffer
tmp.buffer = LoadPixmap(path)
tmp.width = tmp.buffer.width
tmp.height = tmp.buffer.height
tmp.pitch = PixmapPitch(tmp.buffer)
tmp.bcount = tmp.pitch / tmp.width
tmp.mask[0] = (maskrgb Shr 16) & 255
tmp.mask[1] = (maskrgb Shr 8) & 255
tmp.mask[2] = maskrgb & 255
tmp.link = ListAddLast(zImageBufferList,tmp)
Return tmp
End Function

' copy an already existing image buffer
Function copy:zImageBuffer(from:zImageBuffer)
Local tmp:zImageBuffer = New zImageBuffer
tmp.buffer = CopyPixmap(from.buffer)
tmp.width = tmp.buffer.width
tmp.height = tmp.buffer.height
tmp.pitch = PixmapPitch(tmp.buffer)
tmp.bcount = tmp.pitch / tmp.width
tmp.mask = from.mask
tmp.link = ListAddLast(zImageBufferList,tmp)
Return tmp
End Function

Method setmask(maskrgb:Int=$000000)
Self.mask[0] = (maskrgb Shr 16) & 255
Self.mask[1] = (maskrgb Shr 8) & 255
Self.mask[2] = maskrgb & 255
End Method

' copy a rectangle from one image buffer to another as a block without masking
Method copyrectblock(from:zImageBuffer,x:Int,y:Int,w:Int,h:Int,tx:Int,ty:Int)
Local pixTargetPtr:Byte Ptr = PixmapPixelPtr(Self.buffer)
Local pixSourcePtr:Byte Ptr = PixmapPixelPtr(from.buffer)
For Local yy:Int = 0 Until h
For Local xx:Int = 0 Until w
' is target pixel within the target buffer
If tx+xx > -1 And ty+yy > -1 And tx+xx < Self.width And ty+yy < Self.height Then
' is source pixel within the source buffer
If x+xx > -1 And y+yy > -1 And x+xx < from.width And y+yy < from.height Then
pixTargetPtr[(ty+yy)*Self.pitch+(tx+xx)*Self.bcount] = pixSourcePtr[(y+yy)*from.pitch+(x+xx)*from.bcount]
pixTargetPtr[(ty+yy)*Self.pitch+(tx+xx)*Self.bcount+1] = pixSourcePtr[(y+yy)*from.pitch+(x+xx)*from.bcount+1]
pixTargetPtr[(ty+yy)*Self.pitch+(tx+xx)*Self.bcount+2] = pixSourcePtr[(y+yy)*from.pitch+(x+xx)*from.bcount+2]
' check to see if both images include alpha value that needs to be copied as well
If Self.bcount = 4 And from.bcount = 4 Then pixTargetPtr[(ty+yy)*Self.pitch+(tx+xx)*Self.bcount+3] = pixSourcePtr[(y+yy)*from.pitch+(x+xx)*from.bcount+3]
End If
End If
Next
Next
End Method

' copy a rectangle from one image buffer to another using masking
Method copyrect(from:zImageBuffer,x:Int,y:Int,w:Int,h:Int,tx:Int,ty:Int)
Local pixTargetPtr:Byte Ptr = PixmapPixelPtr(Self.buffer)
Local pixSourcePtr:Byte Ptr = PixmapPixelPtr(from.buffer)
For Local yy:Int = 0 Until h
For Local xx:Int = 0 Until w
' is target pixel within the target buffer
If tx+xx > -1 And ty+yy > -1 And tx+xx < Self.width And ty+yy < Self.height Then
' is source pixel within the source buffer
If x+xx > -1 And y+yy > -1 And x+xx < from.width And y+yy < from.height Then
If pixSourcePtr[(y+yy)*from.pitch+(x+xx)*from.bcount] <> from.mask[0] Or pixSourcePtr[(y+yy)*from.pitch+(x+xx)*from.bcount+1] <> from.mask[1] Or pixSourcePtr[(y+yy)*from.pitch+(x+xx)*from.bcount+2] <> from.mask[2] Then
pixTargetPtr[(ty+yy)*Self.pitch+(tx+xx)*Self.bcount] = pixSourcePtr[(y+yy)*from.pitch+(x+xx)*from.bcount]
pixTargetPtr[(ty+yy)*Self.pitch+(tx+xx)*Self.bcount+1] = pixSourcePtr[(y+yy)*from.pitch+(x+xx)*from.bcount+1]
pixTargetPtr[(ty+yy)*Self.pitch+(tx+xx)*Self.bcount+2] = pixSourcePtr[(y+yy)*from.pitch+(x+xx)*from.bcount+2]
' check to see if both images include alpha value that needs to be copied as well
If Self.bcount = 4 And from.bcount = 4 Then pixTargetPtr[(ty+yy)*Self.pitch+(tx+xx)*Self.bcount+3] = pixSourcePtr[(y+yy)*from.pitch+(x+xx)*from.bcount+3]
End If
End If
End If
Next
Next
End Method

' save an image buffer to file
Method savefile(path:String="image.png",comp:Int=5)
SavePixmapPNG(Self.buffer,path,comp)
End Method

' clear an image buffer with a set RGBA color
Method clear(rgba:Int=$FF000000)
ClearPixels(Self.buffer,rgba)
End Method

' draw an image buffer to screen
Method draw(x:Int=0,y:Int=0)
DrawPixmap(Self.buffer,x,y)
End Method

' remove an image buffer
Method remove()
Self.buffer = Null
RemoveLink(Self.link)
End Method

Method line(x1:Float,y1:Float,x2:Float,y2:Float,r:Byte,g:Byte,b:Byte)
Local pixPtr:Byte Ptr = PixmapPixelPtr(Self.buffer)
Local steps:Int,xI:Float,i:Int

x2 :- x1
y2 :- y1

If Abs(x2) > Abs(y2) Then
steps = Abs(x2)
Else
steps = Abs(y2)
End If

xI = Float(x2 / steps)
y2 = Float(y2 / steps)

For i = 1 To steps
If Int(y1) > -1 And Int(y1) < Self.height Then
If Int(x1) > -1 And Int(x1) < Self.width Then
pixPtr[Int(y1)*Self.pitch+Int(x1)*Self.bcount] = r
pixPtr[Int(y1)*Self.pitch+Int(x1)*Self.bcount+1] = g
pixPtr[Int(y1)*Self.pitch+Int(x1)*Self.bcount+2] = b
If Self.bcount = 4 Then pixPtr[Int(y1)*Self.pitch+Int(x1)*Self.bcount+3] = 255
End If
End If
x1 :+ xI
y1 :+ y2
Next
End Method

Method rect(x1:Int,y1:Int,x2:Int,y2:Int,r:Byte,g:Byte,b:Byte)
For Local i:Int = y1 To y2
Self.line(x1,i,x2,i,r,g,b)
Next
End Method

Method mirror(direction:Int)
Local pixPtr:Byte Ptr = PixmapPixelPtr(Self.buffer)
Local tmp:Byte,x:Int,y:Int

Select direction
Case 0 ' horizontal
For x = 0 To (Self.width/2)-1
For y = 0 Until Self.height
tmp = pixPtr[y*Self.pitch+x*Self.bcount]
pixPtr[y*Self.pitch+x*Self.bcount] = pixPtr[y*Self.pitch+(Self.width-x-1)*Self.bcount]
pixPtr[y*Self.pitch+(Self.width-x-1)*Self.bcount] = tmp
tmp = pixPtr[y*Self.pitch+x*Self.bcount+1]
pixPtr[y*Self.pitch+x*Self.bcount+1] = pixPtr[y*Self.pitch+(Self.width-x-1)*Self.bcount+1]
pixPtr[y*Self.pitch+(Self.width-x-1)*Self.bcount+1] = tmp
tmp = pixPtr[y*Self.pitch+x*Self.bcount+2]
pixPtr[y*Self.pitch+x*Self.bcount+2] = pixPtr[y*Self.pitch+(Self.width-x-1)*Self.bcount+2]
pixPtr[y*Self.pitch+(Self.width-x-1)*Self.bcount+2] = tmp
If Self.bcount = 4 Then
tmp = pixPtr[y*Self.pitch+x*Self.bcount+3]
pixPtr[y*Self.pitch+x*Self.bcount+3] = pixPtr[y*Self.pitch+(Self.width-x-1)*Self.bcount+3]
pixPtr[y*Self.pitch+(Self.width-x-1)*Self.bcount+3] = tmp
End If
Next
Next
Case 1 ' vertical
For y = 0 To (Self.height/2)-1
For x = 0 Until Self.width
tmp = pixPtr[y*Self.pitch+x*Self.bcount]
pixPtr[y*Self.pitch+x*Self.bcount] = pixPtr[(Self.height-y-1)*Self.pitch+x*Self.bcount]
pixPtr[(Self.height-y-1)*Self.pitch+x*Self.bcount] = tmp
tmp = pixPtr[y*Self.pitch+x*Self.bcount+1]
pixPtr[y*Self.pitch+x*Self.bcount+1] = pixPtr[(Self.height-y-1)*Self.pitch+x*Self.bcount+1]
pixPtr[(Self.height-y-1)*Self.pitch+x*Self.bcount+1] = tmp
tmp = pixPtr[y*Self.pitch+x*Self.bcount+2]
pixPtr[y*Self.pitch+x*Self.bcount+2] = pixPtr[(Self.height-y-1)*Self.pitch+x*Self.bcount+2]
pixPtr[(Self.height-y-1)*Self.pitch+x*Self.bcount+2] = tmp
If Self.bcount = 4 Then
tmp = pixPtr[y*Self.pitch+x*Self.bcount+3]
pixPtr[y*Self.pitch+x*Self.bcount+3] = pixPtr[(Self.height-y-1)*Self.pitch+x*Self.bcount+3]
pixPtr[(Self.height-y-1)*Self.pitch+x*Self.bcount+3] = tmp
End If
Next
Next
Case 2 ' horizontal + vertical
Self.mirror(0)
Self.mirror(1)
End Select
End Method

' shift the content of an image buffer in one of four directions: 0=left, 1=right, 2=up, 3=down
Method shift(direction:Int,pixels:Int)
Local pixPtr:Byte Ptr = PixmapPixelPtr(Self.buffer)

Select direction
Case 0 ' left
Local moveSize:Int = (Self.width-pixels)*Self.bcount
Local offset:Int = pixels*Self.bcount
For Local y:Int = 0 Until Self.height
MemCopy(pixPtr,pixPtr+offset,moveSize)
pixPtr :+ pitch
Next
Case 1 ' right
Local pixSize:Int = pitch/Self.width
Local moveSize:Int = (Self.width-pixels)*Self.bcount
Local offset:Int = pixels*Self.bcount
For Local y:Int = 0 Until Self.height
MemCopy(pixPtr+offset,pixPtr,moveSize)
pixPtr :+ pitch
Next
Case 2 ' up
Local pixSize:Int = pitch/Self.width
For Local y:Int = 0 To Self.height-1-pixels
MemCopy(pixPtr,pixPtr+pitch*pixels,pitch)
pixPtr :+ pitch
Next
Case 3 ' down
Local pixSize:Int = pitch/Self.width
pixPtr :+ ((Self.width*Self.bcount)*(Self.height-1))
For Local y:Int = 0 To Self.height-1-pixels
MemCopy(pixPtr,pixPtr-(pitch*pixels),pitch)
pixPtr :- pitch
Next
End Select
End Method

End Type
« Last Edit: July 11, 2009 by zawran »

Offline Pixel_Outlaw

  • Pentium
  • *****
  • Posts: 1382
  • Karma: 83
    • View Profile
Re: [Bmax] Software render framework
« Reply #3 on: July 11, 2009 »
Awesome, just awesome dude.
Have some karma.
Challenge Trophies Won:

Offline TinDragon

  • Pentium
  • *****
  • Posts: 644
  • Karma: 24
    • View Profile
    • J2K's blog
Re: [Bmax] Software render framework
« Reply #4 on: July 11, 2009 »
Looking good :)

I made a start on a small pixmap lib to allow me to draw to a pixmap as if it were to the screen awhile back, will have  look maybe the line drawing code will be useful, not sure what else i had added to it.

[Edit] Well I only had line and rectangle drawing coded, i will post the whole thing including example here and then people can adapt the code into zawrans lib or use it as is if they want.

Code: [Select]
SuperStrict
'SetGraphicsDriver GLMax2DDriver()
SetGraphicsDriver D3D7Max2DDriver()

Const XRES:Int=640
Const YRES:Int=480

Graphics XRES,YRES,0

'-------------------------------------------------------------------
' Extension of TPixmap with extra drawing commands
'-------------------------------------------------------------------
Type TTDpixmap Extends TPixmap
'-------------------------------------------------------------------
' NAME : Create()
' PURPOSE : This Function Creates a custom pixmap
' INPUTS : width:Int,height:Int,format:Int,align:Int
' RETURNS : TTDpixmap.
'-------------------------------------------------------------------
Function Create:TTDPixmap( width:Int,height:Int,format:Int,align:Int=4 )
Local pitch:Int=width*BytesPerPixel[format]
pitch=(pitch+(align-1))/align*align
Local capacity:Int=pitch*height
Local pixmap:TTDpixmap=New TTDpixmap
pixmap.pixels=MemAlloc( capacity )
pixmap.width=width
pixmap.height=height
pixmap.pitch=pitch
pixmap.format=format
pixmap.capacity=capacity
Return pixmap
End Function
'-----------------------------------------------------------------------
' NAME : Line()
' PURPOSE : This Function draws a line
' INPUTS : x1:Float,y1:Float,x2:Float,y2:Float,r:Int,g:Int,b:Int
' RETURNS : Nothing.
' NOTES : multi format, could incrase speed by setting fixed
'-----------------------------------------------------------------------
Method Line(x1:Float,y1:Float,x2:Float,y2:Float,r:Int,g:Int,b:Int)
Local argb:Int=r Shl 16 + g Shl 8 + b
Local xI:Float, steps:Int
Local p:Byte Ptr
'---
x2 = x2-x1
y2 = y2-y1
If Abs(x2)>Abs(y2)
steps=Abs(x2)
Else
steps=Abs(y2)
EndIf
xI = x2 / steps
y2 = y2 / steps
For x2 = 0 To steps
If x1>=0 And x1<width And y1>=0 And y1<height
' Could maybe speed up by ensuring line is inside pixmap rather than each pixel
' Also maybe just pass r,g,b rather than split back out from argb for some lol
p=PixelPtr(x1,y1)
Select format
Case PF_A8
p[0]=argb Shr 24
Case PF_I8
p[0]=( (argb Shr 16 & $ff)+(argb Shr 8 & $ff)+(argb & $ff) )/3
Case PF_RGB888
p[0]=argb Shr 16 ; p[1]=argb Shr 8 ; p[2]=argb
Case PF_BGR888
p[0]=argb ; p[1]=argb Shr 8 ; p[2]=argb Shr 16
Case PF_RGBA8888
p[0]=argb Shr 16 ; p[1]=argb Shr 8 ; p[2]=argb ; p[3]=argb Shr 24
Case PF_BGRA8888
p[0]=argb ; p[1]=argb Shr 8 ; p[2]=argb Shr 16 ; p[3]=argb Shr 24
End Select
x1=x1+xI
y1=y1+y2
EndIf
Next
End Method
'---------------------------------------------------------------------------
' NAME : Rect()
' PURPOSE : This Function draws a rectangle
' INPUTS : x1:Int,y1:Int,width:Int,height:Int,r:Int,g:Int,b:Int,filled:Int
' RETURNS : Nothing.
' NOTES : Uses Line drawing atm, could increase speed by directly drawing
'---------------------------------------------------------------------------
Method Rect(x1:Int,y1:Int,width:Int,height:Int,r:Int,g:Int,b:Int,filled:Int=True)
Local lopy:Int
If filled=True
For lopy=y1 To (y1+height)
line(x1,lopy,x1+width,lopy,r,g,b)
Next
Else
line(x1,y1,x1+width,y1,r,g,b) 'top
line(x1,y1+height,x1+width,y1+height,r,g,b) 'bottom
line(x1,y1,x1,y1+height,r,g,b) 'left
line(x1+width,y1,x1+width,y1+height,r,g,b) 'right
EndIf
End Method
EndType

'Local testmap:TTDPixmap=TTDPixmap.Create(XRES,YRES/2,PF_RGBA8888) ' Use for GL mode, slow/no show in DX
Local testmap:TTDPixmap=TTDPixmap.Create(XRES,YRES/2,PF_BGRA8888) ' Use for DX mode, slow GL (converts to RGBA)
testmap.ClearPixels(0)

Local iFramecounter_counter:Int
Local iFramecounter_time:Int
Local iFramecounter_framerate:Int

Repeat
Cls
'testmap.Line(Rand(0,XRES),Rand(0,YRES/2),Rand(0,XRES),Rand(0,YRES/2),Rand(0,255),Rand(0,255),Rand(0,255))
'testmap.rect(Rand(0,XRES),Rand(0,YRES/2),Rand(0,50),Rand(0,50),Rand(0,255),Rand(0,255),Rand(0,255),True)
testmap.rect(Rand(0,XRES),Rand(0,YRES/2),Rand(1,50),Rand(1,50),Rand(0,255),Rand(0,255),Rand(0,255),False)
DrawPixmap(testmap,0,50)
DrawText("FPS : "+iFramecounter_framerate,0,0)
Flip
If KeyHit(KEY_SPACE)
testmap.ClearPixels(0)
EndIf
'Framecounter--------------------------------------------
iFramecounter_counter=iFramecounter_counter+1
If iFramecounter_time=0 Then iFramecounter_time=MilliSecs()
If iFramecounter_time+1001 <MilliSecs() Then
iFramecounter_framerate=iFramecounter_counter
iFramecounter_counter=0
iFramecounter_time=MilliSecs()
EndIf
'Framecounter--------------------------------------------
Until KeyDown(KEY_ESCAPE)
End

If I get a chance I will look into some other drawing features, but it shouldnt be to hard to get the drawing functions to work in zawrans code base.

Cheers
Jon

« Last Edit: July 11, 2009 by TinDragon »

Offline Shockwave

  • good/evil
  • Founder Member
  • DBF Aficionado
  • ********
  • Posts: 17409
  • Karma: 498
  • evil/good
    • View Profile
    • My Homepage
Re: [Bmax] Software render framework
« Reply #5 on: July 11, 2009 »
have some karma :)
Shockwave ^ Codigos
Challenge Trophies Won:

Offline zawran

  • Sponsor
  • Pentium
  • *******
  • Posts: 909
  • Karma: 67
    • View Profile
Re: [Bmax] Software render framework
« Reply #6 on: July 11, 2009 »
Thanks guys.

Here are line and rect methods for the framework:

Code: [Select]
Method line(x1:Float,y1:Float,x2:Float,y2:Float,r:Byte,g:Byte,b:Byte)
Local pixPtr:Byte Ptr = PixmapPixelPtr(Self.buffer)
Local steps:Int,xI:Float,i:Int

x2 :- x1
y2 :- y1

If Abs(x2) > Abs(y2) Then
steps = Abs(x2)
Else
steps = Abs(y2)
End If

xI = Float(x2 / steps)
y2 = Float(y2 / steps)

For i = 1 To steps
If Int(y1) > -1 And Int(y1) < Self.height Then
If Int(x1) > -1 And Int(x1) < Self.width Then
pixPtr[Int(y1)*Self.pitch+Int(x1)*Self.bcount] = r
pixPtr[Int(y1)*Self.pitch+Int(x1)*Self.bcount+1] = g
pixPtr[Int(y1)*Self.pitch+Int(x1)*Self.bcount+2] = b
If Self.bcount = 4 Then pixPtr[Int(y1)*Self.pitch+Int(x1)*Self.bcount+3] = 255
End If
End If
x1 :+ xI
y1 :+ y2
Next
End Method

Method rect(x1:Int,y1:Int,x2:Int,y2:Int,r:Byte,g:Byte,b:Byte)
For Local i:Int = y1 To y2
Self.line(x1,i,x2,i,r,g,b)
Next
End Method

Offline zawran

  • Sponsor
  • Pentium
  • *******
  • Posts: 909
  • Karma: 67
    • View Profile
Re: [Bmax] Software render framework
« Reply #7 on: July 17, 2009 »
I made a smallish demo to show how the copy rect functions can be used to draw sprites and sinus a logo.

Code: [Select]
SuperStrict

Framework BRL.GLMax2D
Import BRL.Pixmap
Import BRL.PNGLoader

SetGraphicsDriver GLMax2DDriver()

Include "zImageBuffer.bmx"

Graphics 640,480

Global timerUpd:Int = MilliSecs()
Global timerFrq:Int = 1000/30 ' 30 fps

' create an image buffer to store the screen in
Global screenBuffer:zImageBuffer = zImageBuffer.make(640,480)
screenBuffer.clear($FF000000)
' load a logo image into a new image buffer
Global logoImg:zImageBuffer = zImageBuffer.loadfile("flames.png")
' create a temporary buffer where the logo is rendered to before rendering it to the screen buffer
Global tmpImg:zImageBuffer = zImageBuffer.make(360,256)
' load a sphere image into a new image buffer
Global sphereImg:zImageBuffer = zImageBuffer.loadfile("spheresmall.png")

Global angle1:Int = 0
Global angle2:Int = 0

While Not KeyHit(KEY_ESCAPE)
' clear screen buffer
screenBuffer.clear()
' clear temporary image buffer
tmpImg.clear()
' draw logo into temporary buffer offset with a horizontal sinus
sinusImageHorizontal(logoImg,tmpImg,0,32,angle1,2,15)

' draw temporary buffer into screenbuffer offset with a vertical sinus
sinusImageVertical(tmpImg,screenBuffer,140,0,angle2,1,64)

For Local i:Int = 0 To 7
screenBuffer.copyrect(sphereImg,0,0,128,128,288+Sin(angle1+i*45)*120,280+Cos(angle1+i*45)*120)
Next


' draw screenbuffer to the screen
screenBuffer.draw()
Flip

While MilliSecs() < timerUpd+timerFrq
Wend
timerUpd = MilliSecs()

angle1 :+ 4
angle2 :+ 8

Wend
End

Function sinusImageHorizontal(fromImg:zImageBuffer,toImg:zImageBuffer,x:Int,y:Int,angleStart:Int,angleJump:Int,freq:Int)
For Local lx:Int = 0 Until fromImg.width
toImg.copyrect(fromImg,lx,0,1,fromImg.height,x+lx,y+Sin(angleStart)*freq)
angleStart :+ angleJump
Next
End Function

Function sinusImageVertical(fromImg:zImageBuffer,toImg:zImageBuffer,x:Int,y:Int,angleStart:Int,angleJump:Int,freq:Int)
For Local ly:Int = 0 Until fromImg.height
toImg.copyrect(fromImg,0,ly,fromImg.width,1,x+Sin(angleStart)*freq,y+ly)
angleStart :+ angleJump
Next
End Function

You can get exe, source and image files from here: http://zac-interactive.dk/temp/srf_demo1.zip

[edit] forgot to mention that the flame logo is some random graphics I picked up using a google search, the same with the sphere. There were no mention on the page who had made them though.
« Last Edit: July 17, 2009 by zawran »

Offline Shockwave

  • good/evil
  • Founder Member
  • DBF Aficionado
  • ********
  • Posts: 17409
  • Karma: 498
  • evil/good
    • View Profile
    • My Homepage
Re: [Bmax] Software render framework
« Reply #8 on: July 17, 2009 »
Cool looking spheres :)

I will be acquiring a copy of Bmax soon (thanks hotshot) so I'll be able to have a play with your renderlib properly, I am looking forwad to it too.
Shockwave ^ Codigos
Challenge Trophies Won:

Offline TinDragon

  • Pentium
  • *****
  • Posts: 644
  • Karma: 24
    • View Profile
    • J2K's blog
Re: [Bmax] Software render framework
« Reply #9 on: July 17, 2009 »
Nice example, seems to be nice and smooth as well :)

One thing that I noticed looking thru the zImageBuffer type, with the added line and rectangle methods there isnt actually one to draw just single pixels, might be worth adding a wrapper method for it for ease of use, while it would likely add a little speed overhead if used alot it would give a few options for pixel manipulation of the buffers, perhaps have read and write methods ?


Offline zawran

  • Sponsor
  • Pentium
  • *******
  • Posts: 909
  • Karma: 67
    • View Profile
Re: [Bmax] Software render framework
« Reply #10 on: July 17, 2009 »
Quote
there isnt actually one to draw just single pixels

Good idea, so I added those:

Code: [Select]
Method setpixel(x:Int,y:Int,r:Byte,g:Byte,b:Byte,a:Byte=255)
Local pixPtr:Byte Ptr = PixmapPixelPtr(Self.buffer)
If x > -1 And x < Self.width And y > -1 And y < Self.height Then
pixPtr[y*Self.pitch+x*Self.bcount] = r
pixPtr[y*Self.pitch+x*Self.bcount+1] = g
pixPtr[y*Self.pitch+x*Self.bcount+2] = b
If Self.bcount = 4 Then pixPtr[y*Self.pitch+x*Self.bcount+3] = a
End If
End Method

Method getpixel(x:Int,y:Int,r:Byte Var,g:Byte Var,b:Byte Var,a:Byte Var)
Local pixPtr:Byte Ptr = PixmapPixelPtr(Self.buffer)
If x > -1 And x < Self.width And y > -1 And y < Self.height Then
r = pixPtr[y*Self.pitch+x*Self.bcount]
g = pixPtr[y*Self.pitch+x*Self.bcount+1]
b = pixPtr[y*Self.pitch+x*Self.bcount+2]
If Self.bcount = 4 Then a = pixPtr[y*Self.pitch+x*Self.bcount+3]
End If
End Method

I have updated the first post with the framework file as it is currently.

Offline benny!

  • Senior Member
  • DBF Aficionado
  • ********
  • Posts: 4384
  • Karma: 228
  • in this place forever!
    • View Profile
    • bennyschuetz.com - mycroBlog
Re: [Bmax] Software render framework
« Reply #11 on: July 17, 2009 »
Runs perfectly smooth here. Well done, mate!
 :clap:
[ mycroBLOG - POUET :: whatever keeps us longing - for another breath of air - is getting rare ]

Challenge Trophies Won:

Offline zawran

  • Sponsor
  • Pentium
  • *******
  • Posts: 909
  • Karma: 67
    • View Profile
Re: [Bmax] Software render framework
« Reply #12 on: July 17, 2009 »
Thanks :)  I am going to expand a little bit on the framework during this weekend since my saturday seems to be fairly open. Probably also an example with a bitmap scroller.

Offline Hotshot

  • DBF Aficionado
  • ******
  • Posts: 2114
  • Karma: 91
    • View Profile
Re: [Bmax] Software render framework
« Reply #13 on: July 17, 2009 »
The code is clean and simple
You have done very well with your code mr zawran  ;)

Offline zawran

  • Sponsor
  • Pentium
  • *******
  • Posts: 909
  • Karma: 67
    • View Profile
Re: [Bmax] Software render framework
« Reply #14 on: July 17, 2009 »
Thanks, I just added noise, color addition and subtraction methods:

Code: [Select]
Method noise(x:Int,y:Int,width:Int,height:Int,noiselevel:Int)
Local pixPtr:Byte Ptr = PixmapPixelPtr(Self.buffer)
For Local ty:Int = y To y+height-1
For Local tx:Int = x To x+width-1
If tx > -1 And tx < Self.width And ty > -1 And ty < Self.height Then
Local offset:Int = ty*Self.pitch+tx*Self.bcount
Local n:Int = Rnd(-noiselevel,noiselevel)
If pixPtr[offset] + n < 0 Then
pixPtr[offset] = 0
Else
If pixPtr[offset] + n > 255 Then
pixPtr[offset] = 255
Else
pixPtr[offset] :+ n
End If
End If
If pixPtr[offset+1] + n < 0 Then
pixPtr[offset+1] = 0
Else
If pixPtr[offset+1] + n > 255 Then
pixPtr[offset+1] = 255
Else
pixPtr[offset+1] :+ n
End If
End If
If pixPtr[offset+2] + n < 0 Then
pixPtr[offset+2] = 0
Else
If pixPtr[offset+2] + n > 255 Then
pixPtr[offset+2] = 255
Else
pixPtr[offset+2] :+ n
End If
End If
End If
Next
Next
End Method

Method colorSubtract(x:Int,y:Int,width:Int,height:Int,r:Byte,g:Byte,b:Byte,a:Byte)
Local pixPtr:Byte Ptr = PixmapPixelPtr(Self.buffer)
For Local ty:Int = y To y+height-1
For Local tx:Int = x To x+width-1
If tx > -1 And tx < Self.width And ty > -1 And ty < Self.height Then
Local offset:Int = ty*Self.pitch+tx*Self.bcount
If pixPtr[offset] - r < 0 Then
pixPtr[offset] = 0
Else
pixPtr[offset] = pixPtr[offset]-r
End If
If pixPtr[offset+1] - g < 0 Then
pixPtr[offset+1] = 0
Else
pixPtr[offset+1] = pixPtr[offset+1]-g
End If
If pixPtr[offset+2] - b < 0 Then
pixPtr[offset+2] = 0
Else
pixPtr[offset+2] = pixPtr[offset+2]-b
End If
If Self.bcount = 4 Then
If pixPtr[offset+3] - a < 0 Then
pixPtr[offset+3] = 0
Else
pixPtr[offset+3] = pixPtr[offset+3]-a
End If
End If
End If
Next
Next
End Method

Method colorAddition(x:Int,y:Int,width:Int,height:Int,r:Byte,g:Byte,b:Byte,a:Byte)
Local pixPtr:Byte Ptr = PixmapPixelPtr(Self.buffer)
For Local ty:Int = y To y+height-1
For Local tx:Int = x To x+width-1
If tx > -1 And tx < Self.width And ty > -1 And ty < Self.height Then
Local offset:Int = ty*Self.pitch+tx*Self.bcount
If r + pixPtr[offset] > 255 Then
pixPtr[offset] = 255
Else
pixPtr[offset] = pixPtr[offset]+r
End If
If g + pixPtr[offset+1] > 255 Then
pixPtr[offset+1] = 255
Else
pixPtr[offset+1] = pixPtr[offset+1]+g
End If
If b + pixPtr[offset+2] > 255 Then
pixPtr[offset+2] = 255
Else
pixPtr[offset+2] = pixPtr[offset+2]+b
End If
If Self.bcount = 4 Then
If a + pixPtr[offset+3] > 255 Then
pixPtr[offset+3] = 255
Else
pixPtr[offset+3] = pixPtr[offset+3]+a
End If
End If
End If
Next
Next
End Method

I probably won't be adding anymore tonight, its creeping towards midnight, and I'm a bit tired. But I will try and make more code for it tomorrow.

Offline Shockwave

  • good/evil
  • Founder Member
  • DBF Aficionado
  • ********
  • Posts: 17409
  • Karma: 498
  • evil/good
    • View Profile
    • My Homepage
Re: [Bmax] Software render framework
« Reply #15 on: July 17, 2009 »
Burning the midnight oil tonight eh Zawran? :)
Shockwave ^ Codigos
Challenge Trophies Won:

Offline zawran

  • Sponsor
  • Pentium
  • *******
  • Posts: 909
  • Karma: 67
    • View Profile
Re: [Bmax] Software render framework
« Reply #16 on: July 18, 2009 »
Quote
Burning the midnight oil tonight eh Zawran?

Yes I was up a bit late last night, but I got too tired to continue, so I have picked it up again this morning, and I have begun working on adding sprite sheets, which will make it easier to use bitmap fonts later on. I am not completely done yet, so for now its a little bit of a hack, but its working. I will fine tune things later today, probably sometime after lunch :)



http://zac-interactive.dk/temp/srf_demo2.zip

Offline Shockwave

  • good/evil
  • Founder Member
  • DBF Aficionado
  • ********
  • Posts: 17409
  • Karma: 498
  • evil/good
    • View Profile
    • My Homepage
Re: [Bmax] Software render framework
« Reply #17 on: July 18, 2009 »
Nice and shiny :)
I like the idea of using interference as a background for the eflection.
Shockwave ^ Codigos
Challenge Trophies Won:

Offline Rbz

  • Founder Member
  • DBF Aficionado
  • ********
  • Posts: 2757
  • Karma: 493
    • View Profile
    • https://www.rbraz.com/
Re: [Bmax] Software render framework
« Reply #18 on: July 18, 2009 »
Very nice library, works fine here
Challenge Trophies Won:

Offline benny!

  • Senior Member
  • DBF Aficionado
  • ********
  • Posts: 4384
  • Karma: 228
  • in this place forever!
    • View Profile
    • bennyschuetz.com - mycroBlog
Re: [Bmax] Software render framework
« Reply #19 on: July 18, 2009 »
Yeah, cool water mirror fx.
[ mycroBLOG - POUET :: whatever keeps us longing - for another breath of air - is getting rare ]

Challenge Trophies Won: