Animated Plasma Blend

Started by MrBcx, October 06, 2024, 04:53:20 PM

Previous topic - Next topic

MrBcx

#2
Here is the OpenGL version



'***************************************************************************
' Pulse Plasma Blend by QB64 forum members DAV and BPLUS (2024-10-02)
' Converted to BCX by MrBcx October 4, 2024 - Change display using mouse.
' Tested with Pelles, Lcc-Win32, Mingw, and MSVC-2022
' Converted to BCX and refactored for OpenGL by MrBcx October 4, 2024
'***************************************************************************
$BCXVERSION "8.1.7"
GUI "Plasma Blend OpenGL", PIXELS
'***************************************************************************
#include <GL/gl.h>          // Core OpenGL functions
#include <GL/glu.h>         // GLU library (OPTIONAL)
$PRAGMA comment(lib, "opengl32.lib")
$PRAGMA comment(lib, "glu32.lib")
'***************************************************************************
CONST WIDTH = 800
CONST HEIGHT = 600

GLOBAL AS HGLRC hRC
GLOBAL AS HWND Form1, ActiveWindow
GLOBAL AS INT equationChoice
GLOBAL AS SINGLE m1, m2, m3


SUB FORMLOAD
    Form1 = BCX_FORM("Use the Left and Right Mouse Buttons to change.", 0, 0, WIDTH-50, HEIGHT-50)
    CALL InitOpenGL
    CENTER Form1
    SHOW Form1
    CALL Render
END SUB


BEGIN EVENTS
    SELECT CASE CBMSG
    CASE WM_CREATE
        RANDOMIZE TIMER
    CASE WM_QUIT, WM_CLOSE, WM_DESTROY
        CALL CleanupOpenGL
        SLEEP(100)
        END
    END SELECT
END EVENTS


SUB Render
    DIM cx, cy
    DIM AS DOUBLE t, pulse

    cx = WIDTH / 2
    cy = HEIGHT / 2
    equationChoice = 1 ' Default equation
    m1 = 9
    m2 = 27
    m3 = 3

    DO
        ActiveWindow = GetActiveWindow()

        IF ActiveWindow = Form1 THEN
            IF LCLICK THEN
                m3 = INT(RND * 12)
                m1 = INT(RND * 3) + 2
                m2 = m1 * m3
            END IF

            IF RCLICK THEN
                equationChoice = equationChoice + 1
                IF equationChoice > 6 THEN equationChoice = 1
            END IF
        END IF

        t = t + 0.03
        pulse = SIN(t) * 0.8

        CALL DrawPlasma(cx, cy, pulse, t)
        SLEEP(0)
        DOEVENTS
    LOOP
END SUB



SUB DrawPlasma(cx AS DOUBLE, cy AS DOUBLE, pulse AS DOUBLE, t AS DOUBLE)
    DIM AS DOUBLE rad, a
    DIM AS LONG r1, g1, b1, r2, g2, b2
    DIM AS LONG r, g, b
    DIM AS LONG y, x, dx, dy

    glClear(GL_COLOR_BUFFER_BIT)

    FOR y = 0 TO HEIGHT
        dy = y - cy

        FOR x = 0 TO WIDTH
            dx = x - cx
            rad = SQR(dx * dx + dy * dy) / 100

            SELECT CASE equationChoice
            CASE 1 ' ORIGINAL
                a = atan2(dy, dx) + t
            CASE 2 ' SPIRAL_TWIST
                a = atan2(dy, dx) + SIN(rad * 2 + t)
            CASE 3 ' CHURN_SIN
                a = atan2(dy, dx) + SIN(t) * 4
            CASE 4 ' CHURN_COS
                a = atan2(dy, dx) + COS(t) * 4
            CASE 5 ' SPIRAL_RADIUS
                a = atan2(dy, dx) + SIN(t) * rad
            CASE 6 ' EDGE_WAVE
                a = atan2(dy, dx) + SIN(t * 4 + (x / WIDTH)) * 0.5
            END SELECT

            r1 = (SIN(rad * m3 + t) + SIN(a * m1 + t)) * 127
            g1 = (SIN(rad * m3 + t + 1) + SIN(a * m1 + t + 1)) * 127
            b1 = (SIN(rad * m3 + t + 2) + SIN(a * m1 + t + 2)) * 127

            r2 = (SIN(rad * 3 + t) + SIN(a * 3 + t + 1)) * 127 + 128
            g2 = (SIN(rad * 3 + t + 2) + SIN(a * m2 + t + 3)) * 127 + 128
            b2 = (SIN(rad * 3 + t + 4) + SIN(a * m2 + t + 4)) * 127 + 128

            r = r1 * (1 - pulse) + r2 * pulse
            g = g1 * (1 - pulse) + g2 * pulse
            b = b1 * (1 - pulse) + b2 * pulse

            glColor3ub(r, g, b)
            glBegin(GL_POINTS)
            glVertex2f(x, y)
            glEnd()
        NEXT
    NEXT

    glFlush()

    LOCAL hdc AS HDC
    hdc = GetDC(Form1)
    SwapBuffers(hdc)
    ReleaseDC(Form1, hdc)
END SUB



SUB InitOpenGL
    DIM pfd AS PIXELFORMATDESCRIPTOR
    DIM hdc AS HDC

    hdc = GetDC(Form1)
    pfd.nSize = SIZEOF(pfd)
    pfd.nVersion = 1
    pfd.dwFlags = PFD_DRAW_TO_WINDOW OR PFD_SUPPORT_OPENGL OR PFD_DOUBLEBUFFER
    pfd.iPixelType = PFD_TYPE_RGBA
    pfd.cColorBits = 24
    pfd.cDepthBits = 16
    pfd.iLayerType = PFD_MAIN_PLANE

    LOCAL pixelFormat AS INT
    pixelFormat = ChoosePixelFormat(hdc, &pfd)
    SetPixelFormat(hdc, pixelFormat, &pfd)

    hRC = wglCreateContext(hdc)
    wglMakeCurrent(hdc, hRC)

    ' Setup OpenGL environment
    glMatrixMode(GL_PROJECTION)
    glLoadIdentity()
    gluOrtho2D(0, WIDTH, HEIGHT, 0)
    glMatrixMode(GL_MODELVIEW)
    glLoadIdentity()

    ReleaseDC(Form1, hdc)
END SUB


SUB CleanupOpenGL
    wglMakeCurrent(0, 0)
    wglDeleteContext(hRC)
END SUB




If you want to compile this with MINGW using BED, simply add these two references to Binary Files to Link box:

C:\Mingw\x86_64-w64-mingw32\lib\libopengl32.a     
C:\Mingw\x86_64-w64-mingw32\lib\libglu32.a


Be sure to change the paths to match where your MINGW is installed.


If you want to compile this with LccWin32 using BED, simply add these two references to Binary Files to Link box:

c:\lcc\lib\opengl32.lib   
c:\lcc\lib\glu32.lib


Be sure to change the paths to match where your LccWin32 is installed.





MrBcx

#1
Here is a colorful demo to play with.  It requires BCX 8.1.7 and any compiler to build.  Builds easily in BED.

This uses my QuickPixels library (included) and surprising as it may seem, it uses only the Windows GDI.

Everything you'll see when it's running is created one pixel at a time.  Let that sink in  ;)

I'll also be uploading an OpenGL version which runs a little bit faster and looks a little bit different but still
a cool demo and something to learn from.

Here is the Windows GDI version:



'***************************************************************************
' Plasma Blend Pulse by QB64 forum members DAV and BPLUS (2024-10-02)
' Converted to BCX by MrBcx October 4, 2024 - Change display using mouse.
' Tested with Pelles, Lcc-Win32, Mingw, and MSVC-2022
'***************************************************************************
$BCXVERSION "8.1.7"
GUI "Plasma Blend", PIXELS

CONST WIDTH = 1024
CONST HEIGHT=  768


SUB FORMLOAD
    GLOBAL AS HWND Form1, Canvas, ActiveWindow
    Form1 = BCX_FORM("Use the Left and Right Mouse Buttons to experiment.", 0, 0, WIDTH-50, HEIGHT-50)
    Canvas = BCX_BITMAP(0, Form1, 0, 0, 0, WIDTH, HEIGHT)
    CENTER Form1
    SHOW Form1
    CALL Render
END SUB


BEGIN EVENTS
    SELECT CASE CBMSG
    CASE WM_CREATE
        RANDOMIZE TIMER
        GLOBAL AS HDC BackBuffer
        GLOBAL AS HBITMAP BackBitmap
        BackBuffer = MAKEHDC(NEWBMP)
        BackBitmap = MAKEBMP(BackBuffer)

    CASE WM_QUIT, WM_CLOSE, WM_DESTROY
        ' Clean up resources
        DeleteDC(BackBuffer)
        DeleteObject(BackBitmap)
        SLEEP(100)
        END
    END SELECT
END EVENTS



SUB Render
    DIM AS DOUBLE cx, cy, m, m1, m2, m3, y, x, a, timing, pulse, rad
    DIM AS LONG r1, g1, b1, r2, g2, b2, r, g, b
    DIM AS LONG dy, dy2, dx, dx2
    '*************************************************************************
    '   Define constants for selecting which of DAV's equations to test
    '*************************************************************************
    CONST ORIGINAL = 1
    CONST SPIRAL_TWIST = 2
    CONST CHURN_SIN = 3
    CONST CHURN_COS = 4
    CONST SPIRAL_RADIUS = 5
    CONST EDGE_WAVE = 6
    CONST DISTORT = 7
    DIM AS INT equationChoice
    '*************************************************************************
    equationChoice = ORIGINAL   ' Change this to test different equations
    '*************************************************************************
    cx = WIDTH / 2
    cy = HEIGHT / 2
    m1 = 9
    m2 = 27
    m3 = 3

    '********************************************
    '      Supports my FastPixels Library
    '********************************************
    LOCAL hPlasmaBmp AS HBITMAP
    LOCAL Myhdc AS HDC
    hPlasmaBmp = NEWBMP
    Myhdc = MAKEHDC(hPlasmaBmp)
    IF NULL = Myhdc THEN EXIT SUB
    '********************************************

    DO
        '********************************************
        '  We only want Mouse clicks from our demo
        '********************************************
        ActiveWindow = GetActiveWindow()
        IF ActiveWindow = Form1 THEN
            IF LCLICK THEN
                m3 = INT(RND * 12)
                m = INT(RND * 3) + 2
                m1 = m * m3
                m2 = m * m1
            END IF

            IF RCLICK THEN
                equationChoice++
                IF equationChoice  > DISTORT THEN equationChoice = ORIGINAL
            END IF
        END IF
        '********************************************

        timing = timing + 0.03
        pulse = SIN(timing) * 0.7  ' pulse factor

        IF NOT OpenBmpData(Myhdc) THEN
            MSGBOX "Failed to open back buffer"
            EXIT SUB
        END IF

        FOR y = 0 TO HEIGHT STEP 1
            dy = y - cy                  ' Precompute y - cy
            dy2 = dy * dy                ' Precompute (y - cy)^2

            FOR x = 0 TO WIDTH STEP 1
                dx = x - cx              ' Precompute x - cx
                dx2 = dx * dx            ' Precompute (x - cx)^2

                ' Precompute radius only once for each (x, y)
                rad = SQR(dx2 + dy2) / 100

                ' Calculate 'a' based on the selected equation
                SELECT CASE equationChoice
                CASE ORIGINAL
                    a = atan2(dy, dx) + timing

                CASE SPIRAL_TWIST
                    a = atan2(dy, dx) + SIN(rad * 2 + timing)

                CASE CHURN_SIN
                    a = atan2(dy, dx) + SIN(timing) * 4

                CASE CHURN_COS
                    a = atan2(dy, dx) + COS(timing) * 4

                CASE SPIRAL_RADIUS
                    a = atan2(dy, dx) + SIN(timing) * rad

                CASE EDGE_WAVE
                    a = atan2(dy, dx) + SIN(timing * 4 + (x / WIDTH)) * 0.5

                CASE DISTORT
                    a = atan2(dy, dx) + (x / WIDTH) * SIN(timing) * 5
                END SELECT

                ' 1st plasma colors
                r1 = (SIN(rad * m3 + timing)     + SIN(a * m1 + timing))     * 127
                g1 = (SIN(rad * m3 + timing + 1) + SIN(a * m1 + timing + 1)) * 127
                b1 = (SIN(rad * m3 + timing + 2) + SIN(a * m1 + timing + 2)) * 127

                ' 2nd plasma colors
                r2 = (SIN(rad * 3 + timing)     + SIN(a * 3 + timing + 1))  * 127 + 128
                g2 = (SIN(rad * 3 + timing + 2) + SIN(a * m2 + timing + 3)) * 127 + 128
                b2 = (SIN(rad * 3 + timing + 4) + SIN(a * m2 + timing + 4)) * 127 + 128

                ' Blend plasma colors using pulse factor
                r = r1 * (1 - pulse) + r2 * pulse
                g = g1 * (1 - pulse) + g2 * pulse
                b = b1 * (1 - pulse) + b2 * pulse

                SetPixelFast(x, y, RGB(r, g, b))
            NEXT
        NEXT

        ' Close QuickPixels back buffer
        CloseBmpData(BackBuffer)

        ' BLIT our backbuffer to our Canvas
        DIM RAW MyHdc AS HDC
        MyHdc = GetDC(Canvas)
        BitBlt(MyHdc, 0, 0, WIDTH, HEIGHT, BackBuffer, 0, 0, SRCCOPY)
        ReleaseDC(Canvas, MyHdc)

        SLEEP(0)  ' Don't overwork the CPU
        DOEVENTS
    LOOP
END SUB



'========================================================================
' By Kevin Diggins       Quick Pixels GDI library     June 19, 2024
'                              MIT License
'========================================================================

GLOBAL QP_BmpSize AS DWORD
GLOBAL QP_PixelBuffer[8300000] AS DWORD  ' Support 2k screens @ 32-bit
GLOBAL QP_hBmp AS HBITMAP
GLOBAL QP_MemHDC AS HDC
GLOBAL QP_BmpWidth, QP_BmpHeight


FUNCTION OpenBmpData(hdc AS HDC) AS BOOL
    IF QP_hBmp THEN
        DeleteObject(QP_hBmp)
        DeleteDC(QP_MemHDC)
    END IF

    QP_MemHDC = CreateCompatibleDC(hdc)
    IF NOT QP_MemHDC THEN
        MSGBOX "Error: CreateCompatibleDC failed."
        FUNCTION = FALSE
    END IF

    QP_hBmp = CreateCompatibleBitmap(hdc, GetDeviceCaps(hdc, HORZRES), GetDeviceCaps(hdc, VERTRES))
    IF NOT QP_hBmp THEN
        DeleteDC(QP_MemHDC)
        MSGBOX "Error: CreateCompatibleBitmap failed."
        FUNCTION = FALSE
    END IF

    IF NOT SelectObject(QP_MemHDC, QP_hBmp) THEN
        MSGBOX "Error: SelectObject failed."
        DeleteObject(QP_hBmp)
        DeleteDC(QP_MemHDC)
        FUNCTION = FALSE
    END IF

    IF NOT BitBlt(QP_MemHDC, 0, 0, GetDeviceCaps(hdc, HORZRES), GetDeviceCaps(hdc, VERTRES), hdc, 0, 0, SRCCOPY) THEN
        MSGBOX "Error: BitBlt failed."
        DeleteObject(QP_hBmp)
        DeleteDC(QP_MemHDC)
        FUNCTION = FALSE
    END IF

    LOCAL bmpInfo AS BITMAP
    IF NOT GetObject(QP_hBmp, SIZEOF(bmpInfo), &bmpInfo) THEN
        MSGBOX "Error: GetObject failed."
        DeleteObject(QP_hBmp)
        DeleteDC(QP_MemHDC)
        FUNCTION = FALSE
    END IF

    QP_BmpWidth = bmpInfo.bmWidth
    QP_BmpHeight = bmpInfo.bmHeight
    QP_BmpSize = QP_BmpWidth * QP_BmpHeight * (bmpInfo.bmBitsPixel / 8)

    IF QP_BmpSize > SIZEOF(QP_PixelBuffer) THEN
        MSGBOX "Error: QP_PixelBuffer size needs to be increased or choose a smaller bitmap."
        DeleteObject(QP_hBmp)
        DeleteDC(QP_MemHDC)
        FUNCTION = FALSE
    END IF

    LOCAL bmi AS BITMAPINFO
    bmi.bmiHeader.biSize = SIZEOF(BITMAPINFOHEADER)
    bmi.bmiHeader.biWidth = QP_BmpWidth
    bmi.bmiHeader.biHeight = -QP_BmpHeight
    bmi.bmiHeader.biPlanes = 1
    bmi.bmiHeader.biBitCount = bmpInfo.bmBitsPixel
    bmi.bmiHeader.biCompression = BI_RGB

    IF GetDIBits(hdc, QP_hBmp, 0, QP_BmpHeight, &QP_PixelBuffer, &bmi, DIB_RGB_COLORS) THEN
        FUNCTION = TRUE
    ELSE
        MSGBOX "Error: GetDIBits failed."
        DeleteObject(QP_hBmp)
        DeleteDC(QP_MemHDC)
        FUNCTION = FALSE
    END IF
END FUNCTION



SUB CloseBmpData(hdc AS HDC)
    IF NOT QP_hBmp THEN
        MSGBOX "Error: CloseBmpData detected QP_hBmp is corrupted."
        EXIT SUB
    END IF

    LOCAL bmi AS BITMAPINFO
    bmi.bmiHeader.biSize = SIZEOF(BITMAPINFOHEADER)
    bmi.bmiHeader.biWidth = QP_BmpWidth
    bmi.bmiHeader.biHeight = -QP_BmpHeight
    bmi.bmiHeader.biPlanes = 1
    bmi.bmiHeader.biBitCount = 32
    bmi.bmiHeader.biCompression = BI_RGB
    bmi.bmiHeader.biSizeImage = QP_BmpSize

    DIM RAW result AS LONG
    result = SetDIBits(QP_MemHDC, QP_hBmp, 0, QP_BmpHeight, &QP_PixelBuffer, &bmi, DIB_RGB_COLORS)
    IF result = 0 THEN
        MSGBOX "Error: SetDIBits returned zero scanlines."
        EXIT SUB
    END IF

    BitBlt(hdc, 0, 0, QP_BmpWidth, QP_BmpHeight, QP_MemHDC, 0, 0, SRCCOPY)

    DeleteObject(QP_hBmp)
    QP_hBmp = NULL
    DeleteDC(QP_MemHDC)
    QP_MemHDC = NULL
END SUB


SUB SetPixelFast(x AS LONG, y AS LONG, rgbColor AS DWORD)
    IF x < 0 OR x >= QP_BmpWidth OR y < 0 OR y >= QP_BmpHeight THEN
        EXIT SUB
    END IF

    DIM RAW pixelOffset AS DWORD
    pixelOffset = x + (y * QP_BmpWidth)
    QP_PixelBuffer[pixelOffset] = rgbColor
END SUB