December 03, 2020, 08:19:47 PM

Author Topic: [bmx] Fast 2D Blobby Objects/Metaballs in Max2D with OpenGL extras by ImaginaryHuman [ 1+ years ago ]  (Read 487 times)

Offline BlitzBot

  • Jr. Member
  • **
  • Posts: 1
Title : Fast 2D Blobby Objects/Metaballs in Max2D with OpenGL extras
Author : ImaginaryHuman
Posted : 1+ years ago

Description : This program shows how to draw an `energy field` image using a special gradient curve, and then takes that image and renders it using LIGHTBLEND mode, to produce blobby objects. Several full-screen passes are then added using different blend modes and values to massage the visual data into something more useful. The end result is a gradient-shaded `band` which conforms to the contours of the blobby objects. This example uses some OpenGL calls but also still uses Max2D in a compatible way. This program could be used in a game or for some other application. It will be highly hardware accelerated if your OpenGL implementation supports using the GPU. It is fast!

Code :
Code: BlitzMax
  1. 'Blobby objects with BlitzMax using Max2D and some direct OpenGL
  2.  
  3. 'Some special numbers
  4. Local ballsize:Int=512
  5. Local ballsizehalf:Int=ballsize/2
  6.  
  7. 'Set up the display
  8. Graphics 800,600,0
  9. Cls
  10.  
  11. 'Work out what the dividers needs to be
  12. Local balldivider:Float
  13. If ballsize=128 Then balldivider=64 '8x8
  14. If ballsize=256 Then balldivider=256 '16x16
  15. If ballsize=512 Then balldivider=1024 '32x32
  16. Local lineardivider:Float
  17. If ballsize=128 Then lineardivider=0.5
  18. If ballsize=256 Then lineardivider=1
  19. If ballsize=512 Then lineardivider=2
  20.  
  21. 'Render the gradient image
  22. For Local r:Float=1 To ballsize-1 Step 0.5
  23.         Local level:Float=r
  24.         level:*level
  25.         level=level/balldivider
  26.         SetColor level,level,level 'For blobby gradient shape
  27.         'SetColor r/lineardivider,r/lineardivider,r/lineardivider 'For linear gradients
  28.         DrawOval r/2,r/2,ballsize-r,ballsize-r
  29. Next
  30.  
  31. 'Turn it into an image
  32. AutoMidHandle True
  33. Local img:TImage=CreateImage(ballsize,ballsize,1,FILTEREDIMAGE)
  34. GrabImage(img,0,0,0)
  35.  
  36. 'Set the blend mode
  37. SetBlend LIGHTBLEND
  38.  
  39. 'Keep drawing the image until you press Escape
  40. Repeat
  41.         Cls
  42.         glEnable(GL_BLEND)
  43.         glBlendFunc(GL_SRC_ALPHA,GL_ONE)
  44.         DrawImage img,400,300
  45.         DrawImage img,MouseX(),MouseY()
  46.         glBlendFunc(GL_SRC_ALPHA,GL_ONE)
  47.         glColor4b($40,$40,$40,$40)
  48.         DrawRect 0,0,800,600
  49.         glBlendFunc(GL_SRC_ALPHA_SATURATE,GL_DST_COLOR)
  50.         glColor4b(0,0,0,$FF)
  51.         Local Counter:Int
  52.         For Counter:Int=1 To 3
  53.                 DrawRect 0,0,800,600
  54.         Next
  55.         glBlendFunc(GL_ONE_MINUS_DST_COLOR,GL_ONE_MINUS_DST_COLOR)
  56.         glColor4b($0,$0,$0,$0)
  57.         DrawRect 0,0,800,600
  58.         glBlendFunc(GL_DST_COLOR,GL_DST_COLOR)
  59.         glColor4b($FF,$FF,$FF,$FF)
  60.                 SetColor $FF,$FF,$FF
  61.         For Counter:Int=1 To 3
  62.                 DrawRect 0,0,800,600
  63.         Next
  64.         glDisable(GL_BLEND)
  65.         Flip
  66. Until KeyHit(KEY_ESCAPE)


Comments :


Booticus(Posted 1+ years ago)

 CANT.....STOP......PLAYING WITH THIS COOODE!!!! Excellent work!


Filax(Posted 1+ years ago)

 Great !


DannyD(Posted 1+ years ago)

 very nice.


Regular K(Posted 1+ years ago)

 To fix this for v1.12 (demo at least) add SetGraphicsDriver(GLMax2DDriver()) above Graphics 800,600,0 and it works, to some extent.


ImaginaryHuman(Posted 1+ years ago)

 You shouldn't be setting the Max2d driver when it's doing custom OpenGL code, you should choose the GLGraphics() driver.


Jesse(Posted 1+ years ago)

 it works ok in windowed mode but full screen? can you or angel convert it to 100% gl graphics? That would really help some of us trying to learn OGL.  I know what its doing: A grayscale image is blended with another gray scale image and causes the effect. what I am interested on is how do you integrate the images in to ogl. I am going to try to work this out myself but a little help would be appreciated. I will post any progress.


ImaginaryHuman(Posted 1+ years ago)

 See this thread, it contains a pure OpenGL implementation:<a href="../Community/posts1410.html?topic=46378" target="_blank">http://www.blitzbasic.com/Community/posts.php?topic=46378[/url]The code may be slightly old - not sure but you might have to change to GLGraphics() to open a display.Also the blobs that it shows initially are based on a square blob field which creates inverted blobs (which I think are cool - since I invented them).Let me know if it works for you.


Jesse(Posted 1+ years ago)

 thank you, I will check it out.


tesuji(Posted 1+ years ago)

 Sorry to dig this up after so long but it's still useful.Here's an adaptation that uses a FBO and gets away with just two render passes, one to draw the individual blobs to a texture and the other to render that texture to the screen. It does combine Max2D & OpenGL but appears to work ok (for now).
Code: [Select]
SuperStrict

' Efficient 2D Metaball test using OpenGL FBO - Tesuji 2011
' A mashup of <a href="../Community/posts9d53.html?topic=45716" target="_blank">http://blitzbasic.com/Community/posts.php?topic=45716</a> and <a href="http://www.gamedev.net/topic/484576-efficient-2d-metaballs/" target="_blank">http://www.gamedev.net/topic/484576-efficient-2d-metaballs/</a>
'
' Uses 2 drawing passes, one to render to texture & the other to render to screen
' In the render to texture pass, each sprite is simply drawn additively into all four channels
' (with glBlendFunc(GL_ONE, GL_ONE)). So each sprite adds to the RGB color, as well as the "influence" (alpha).

' In the final render pass, the program draws a single quad using the previously generated texture with
' glBlendFunc(GL_ONE_MINUS_SRC_ALPHA, GL_ZERO), glEnable(GL_ALPHA_TEST) And glAlphaFunc(GL_GREATER, 0.5).
' The alpha testing causes the sharp cutoff between the "inside" and "outside" of the blob, and the blendfunc
' just creates the gradient inside this edge (To make it look more interesting than just flat color).

Import pub.glew

SetGraphicsDriver GLMax2DDriver()
Graphics 1024,768,0,0,GRAPHICS_BACKBUFFER | GRAPHICS_DEPTHBUFFER | GRAPHICS_ALPHABUFFER
glewInit()

' pre generate our blob image
Local img:TImage = TBlob.createMetaballImage(128, 1.0,1.0,1.0)
MidHandleImage img

' create a render to texture buffer
Local fbo:TFboDoubleBuffer = New TFboDoubleBuffer.Create([CreateImage(512,512)],[CreateImage(512,512)])

' create some simple blob objects
Local blobs:TList = New TList
For Local i:Int = 1 To 100
blobs.addLast(New TBlob.Create(img,Rnd(512),Rnd(512),Rnd(1.0)-.5,Rnd(1.0)-.5))
Next

Local alphaCutoff:Float = 0.5

While Not KeyHit(KEY_ESCAPE)

alphaCutoff :+ (KeyHit(KEY_LEFT)-KeyHit(KEY_RIGHT))*.05

For Local blob:TBlob = EachIn blobs
blob.update()
Next

   ' render blobs to texture
fbo.bind([0])
glClearColor 0.0,0.0,0.0,0.0
glClear GL_COLOR_BUFFER_BIT
SetAlpha 1.0
SetScale 1,1
SetColor 255,255,255
glBlendFunc(GL_ONE, GL_ONE)
For Local blob:TBlob = EachIn blobs
blob.render()
Next
glDisable(GL_BLEND)
fbo.unbind()

Cls

'draw blobs texture as outline
SetAlpha 1.0
SetBlend ALPHABLEND
SetScale 2,2
glEnable(GL_ALPHA_TEST)
glAlphaFunc(GL_GREATER, alphaCutoff)
glBlendFunc(GL_ONE_MINUS_SRC_ALPHA, GL_ZERO)
DrawImage fbo.images[0],0,0
glDisable(GL_BLEND)
glDisable(GL_ALPHA)

' draw blobs texture glow
SetBlend LIGHTBLEND
SetAlpha 0.5
SetColor 96,128,64
DrawImage fbo.images[0],0,0

Flip 1

Wend

Type TBlob

Field x:Float,y:Float
Field xv:Float,yv:Float
Field img:TImage

Method Create:TBlob(image:TImage,x:Float,y:Float,xv:Float,yv:Float)
Self.img = image
Self.x = x
Self.y = y
Self.xv = xv
Self.yv = yv
Return Self
End Method

Method update()
x :+ xv
y :+ yv
If x < 0 Or x > 512 xv = -xv
If y < 0 Or y > 512 yv = -yv
End Method

Method render()
DrawImage img,x,y
End Method

Function createMetaballImage:TImage(ballsize:Int=128, r:Float,g:Float,b:Float)

Local balldivider:Float
Local lineardivider:Float

Select ballsize
Case 128 '8x8
balldivider = 64
lineardivider = 0.5
Case 256 '16x16
balldivider = 256
lineardivider = 1
Case 512 ' 32x32
balldivider = 1024
lineardivider = 2
End Select

'Render the gradient image
SetBlend SOLIDBLEND
glClearColor 0.0,0.0,0.0,0.0
glClear GL_COLOR_BUFFER_BIT
SetColor r*255,g*255,b*255
For Local r:Float=1 To ballsize-1 Step 0.5
Local level:Float=r
level :* level
level = level/balldivider
SetAlpha level/256.0
DrawOval r/2,r/2,ballsize-r,ballsize-r
Next

Local img:TImage=CreateImage(ballsize,ballsize,1,FILTEREDIMAGE)
GrabImage(img,0,0,0)
Return img

End Function

End Type


Type TFboDoubleBuffer

Field colorBuffers:Int[]
    Field fb:Int[1]
    Field OrigX:Int,OrigY:Int
    Field OrigW:Int,OrigH:Int
    Field width:Int,height:Int
    Field images:TImage[]
    Field images1:TImage[]
    Field images2:TImage[]
    Field flipBit:Int = False

    Method Create:TFboDoubleBuffer(images1:TImage[],images2:TImage[])

        Self.images1 = images1
        Self.images2 = images2
        Self.images = images1
Self.width = images[0].width
Self.height = images[0].height

If (colorBuffers.length = 0) Then
Local maxBuffers:Int = 0
glGetIntegerv(GL_MAX_COLOR_ATTACHMENTS, Varptr(maxBuffers))

Local idx:Int = GL_COLOR_ATTACHMENT0_EXT;
colorBuffers = New Int[maxBuffers];
For Local n:Int = 0 To maxBuffers - 1
colorBuffers[n] = idx + n;
Next
End If

       createFBO()
 
      Return Self
    End Method
               
    Function initGraphics(width:Int,height:Int,bit:Int=0,mode:Int=60)
        SetGraphicsDriver(GLMax2DDriver())
        Graphics width,height,bit,mode
        glewInit()
     End Function
       
     Method createFBO()

        glGenFramebuffersEXT(1, fb)          
Assert glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT) = GL_FRAMEBUFFER_COMPLETE_EXT, "glCheckFramebufferStatusEXT != GL_FRAMEBUFFER_COMPLETE_EXT"
   
    End Method

    Method bindTextures(textures:Int[]=Null)
        glBindFramebufferEXT(GL_FRAMEBUFFER_EXT , fb[0]) ;
If textures = Null
textures = New Int[images.length]
For Local i:Int = 0 To images.length-1
textures[i] = i
Next
End If

For Local i:Int = 0 To textures.length-1
Local tid:Int = TGLImageFrame(images[textures[i]].frame(0)).name
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT+i, GL_TEXTURE_2D, tid, 0);
Next
Local drawBuffers:Int = True
If drawBuffers
Local buffers:Int[] = New Int[textures.length];
For Local i:Int = 0 To buffers.length-1
buffers[i] = colorBuffers[i]
Next
glDrawBuffers(buffers.length, buffers);

End If

    End Method
       
    Method bind(textures:Int[]=Null, noFlip:Int=False)
        GetViewport(OrigX,OrigY,OrigW,OrigH)
        bindTextures(textures)
        glMatrixMode GL_PROJECTION
        glLoadIdentity
        glOrtho 0,width,0,width,-1,1
        glMatrixMode GL_MODELVIEW
        glViewport 0,0,width,height
        glScissor 0,0,width,height

If Not noFlip flipBuffer()

    End Method

Method flipBuffer()
        If flipBit
            images = images1
         Else
          images = images2
         End If
        flipBit = Not flipBit
End Method
       
    Method unbind()
For Local i:Int = 0 To images.length-1
        glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT+i, GL_TEXTURE_2D,  0, 0);
Next
        glBindFramebufferEXT(GL_FRAMEBUFFER_EXT , 0)
        glMatrixMode GL_PROJECTION
        glLoadIdentity
        glOrtho 0,OrigW,Origh,0,-1,1
        glMatrixMode GL_MODELVIEW
        glViewport 0,0,OrigW,OrigH
        glScissor 0,0,OrigW,OrigH
    End Method
   
End Type



Leon Drake(Posted 1+ years ago)

 balls are touching....u know what that means...


Nate the Great(Posted 1+ years ago)

 tesuji, sounds like something I was looking for but when I run it, it looks like just a bunch of rendered green circles... any idea why this is?


tesuji(Posted 1+ years ago)

 Nate, maybe a problem with the FBO and your gfx card?I'll try and put a up a plain grabpixmap version and or a faster opengl equivalent and see if that makes any difference.<edit> here's a slower grabimage version that doesn't rely on FBOs. Does this work?<edit2> gah. worked on the mac but got the same when I tried it on a PC. Determined that the grabimage wasn't including alpha even though it does on the mac. Change to Graphics 1024,768,0,0,GRAPHICS_BACKBUFFER | GRAPHICS_DEPTHBUFFER | GRAPHICS_ALPHABUFFERHopefully that works.
Code: [Select]
SuperStrict

SetGraphicsDriver GLMax2DDriver()
Graphics 1024,768,0,0,GRAPHICS_BACKBUFFER | GRAPHICS_DEPTHBUFFER | GRAPHICS_ALPHABUFFER

' pre generate our blob image
Local img:TImage = TBlob.createMetaballImage(128, 1.0,1.0,1.0)
MidHandleImage img

' create a render to texture buffer
Local bufferImage:TImage = CreateImage(512,512,1,DYNAMICIMAGE | FILTEREDIMAGE)

' create some simple blob objects
Local blobs:TList = New TList
For Local i:Int = 1 To 100
blobs.addLast(New TBlob.Create(img,Rnd(512),Rnd(512),Rnd(1.0)-.5,Rnd(1.0)-.5))
Next

Local alphaCutoff:Float = 0.5

While Not KeyHit(KEY_ESCAPE)

alphaCutoff :+ (KeyHit(KEY_LEFT)-KeyHit(KEY_RIGHT))*.05

For Local blob:TBlob = EachIn blobs
blob.update()
Next

   ' render blobs to texture
Cls
SetAlpha 1.0
SetScale 1,1
SetColor 255,255,255
glBlendFunc(GL_ONE, GL_ONE)
For Local blob:TBlob = EachIn blobs
blob.render()
Next
glDisable(GL_BLEND)
GrabImage(bufferImage,0,0)

Cls

'draw blobs texture as outline
SetAlpha 1.0
SetBlend ALPHABLEND
SetScale 2,2
glEnable(GL_ALPHA_TEST)
glAlphaFunc(GL_GREATER, alphaCutoff)
glBlendFunc(GL_ONE_MINUS_SRC_ALPHA, GL_ZERO)
DrawImage bufferImage,0,0
glDisable(GL_BLEND)
glDisable(GL_ALPHA)

' draw blobs texture glow
SetBlend LIGHTBLEND
SetAlpha 0.5
SetColor 96,128,64
DrawImage bufferImage,0,0

Flip 1

Wend

Type TBlob

Field x:Float,y:Float
Field xv:Float,yv:Float
Field img:TImage

Method Create:TBlob(image:TImage,x:Float,y:Float,xv:Float,yv:Float)
Self.img = image
Self.x = x
Self.y = y
Self.xv = xv
Self.yv = yv
Return Self
End Method

Method update()
x :+ xv
y :+ yv
If x < 0 Or x > 512 xv = -xv
If y < 0 Or y > 512 yv = -yv
End Method

Method render()
DrawImage img,x,y
End Method

Function createMetaballImage:TImage(ballsize:Int=128, r:Float,g:Float,b:Float)

Local balldivider:Float
Local lineardivider:Float

Select ballsize
Case 128 '8x8
balldivider = 64
lineardivider = 0.5
Case 256 '16x16
balldivider = 256
lineardivider = 1
Case 512 ' 32x32
balldivider = 1024
lineardivider = 2
End Select

'Render the gradient image
SetBlend SOLIDBLEND
glClearColor 0.0,0.0,0.0,0.0
glClear GL_COLOR_BUFFER_BIT
SetColor r*255,g*255,b*255
For Local r:Float=1 To ballsize-1 Step 0.5
Local level:Float=r
level :* level
level = level/balldivider
SetAlpha level/256.0
DrawOval r/2,r/2,ballsize-r,ballsize-r
Next

Local img:TImage=CreateImage(ballsize,ballsize,1,FILTEREDIMAGE)
GrabImage(img,0,0,0)
Return img

End Function

End Type




BlitzSupport(Posted 1+ years ago)

 I get the green output with both versions, tesuji -- green balls moving over black background.BTW It looks like you're setting Hertz to 0 in the Graphics call! I have to take that out to make it do anything...


tesuji(Posted 1+ years ago)

 Managed to replicate the issue on a PC. Hopefully fixed now. Code edited above and seems to work, at least from here.


BlitzSupport(Posted 1+ years ago)

 Hi tesuji, yep, both versions are working now, well done. (Just to check, still green, but shaded properly instead of being solid.)


tesuji(Posted 1+ years ago)

 Thanks BlitzSupport.It should look something like this :


BlitzSupport(Posted 1+ years ago)

 Perfect, then!


Nate the Great(Posted 1+ years ago)

 both examples work now! thanks! [/i]

 

SimplePortal 2.3.6 © 2008-2014, SimplePortal