Анимация с помощью OpenGl (всплески волн)

Пример, изображающий всплески волн. Хорош тем, что автор попытался сделать физику явления всплеска наиболее реалистичным.
Платформы: Windows , Linux.
Автор: Michael "h4tt3n" Nissen. Создано: 2010 году.

рендеринг

''        rough smoothed particle hydrodynamics (SPH) draft
''        Michael "h4tt3n" Nissen, july 5th 2010
''
''        todo:
''        predefined cell neighbors
''        better kernel (less bouncy liquid)
''        better broad phase collision detection
''        better mouse interaction
''        better code structure

''        includes
#INCLUDE Once "GL/gl.bi"


''        constants
Const As Single                dt                                                = 0.01                                                                                        ''        timestep
Const As Single                half_dt                                = 0.5*dt                                                                                ''        half timestep
Const As Single                r                                                 = 10                                                                                                ''        interaction radius
Const As Single                r_2                                         = 2*r                                                                                                ''        two radius (for grid)
Const As Single                r_sqrd                                 = r*r                                                                                                ''        radius squared
Const As Single                k                                                        = 400                                                                                                ''        global "spring stiffnes"
Const As Single                k_near                                = 3000                                                                                        ''        local "spring stiffnes"
Const As Single                viscosity                        = 2                                                                                                        ''        viscosity
Const As Single                rest_dens                 = 6                                                                                                        ''        rest density
Const As Single                grav_acc                        = 400                                                                                                ''        gravity
Const As Single                pi                                                = 4*Atn(1)                                                                        ''        pi
Const As Single                radius                                = 2                                                                                                        ''        particle distance on startup
Const As Integer  block_width                = 60                                                                                                ''        <--- change to reduce / increase num masses
Const As Integer  block_height        = 100                                                                                                ''
Const As Integer  num_masses                = block_width*block_height        ''        total number of masses
Const As Integer  neighbors                        = 48                                                                                                ''        max number of neighbors
Const As Integer         screen_wid                 = 800                                                                                                ''        screen width
Const As Integer         screen_hgt                 = 400                                                                                                ''        screen height
Const As Single         wall_friction        = 1                                                                                                        ''        wall friction (1 = full, 0 = off)
Const As Single         interaction_Radius        = 8                                                                                ''        mouse interaction radius

Const As Integer cell_dia                                                = r
Const As Integer cell_max_masses                = 24

Const As Integer num_cells_row                        = screen_wid/cell_dia
Const As Integer num_cells_col                        = screen_hgt/cell_dia

Const As Integer num_cells                                        = num_cells_row*num_cells_col

Const As Single cell_wid                                                = screen_wid/num_Cells_row
Const As Single cell_hgt                                                = screen_hgt/num_Cells_Col
Const As Single half_cell_wid                                = cell_wid/2


''        types
Type CellType
    As Integer Num_Masses, Mass(1 To cell_max_masses)
End Type


Type vectortype
    As Single x, y
End Type


Type NeighborType
    As Integer a
    As Single Dist
    As vectortype norm_dist
End Type


Type masstype
    As Integer num_neighbors
    As NeighborType Neighbor(1 To neighbors)
    As vectortype frc, vel, psn
    As Single density, angvel, torque
End Type


''        dim variables
Dim As Integer         center_x         = screen_wid\2

Dim As Integer         center_y        = screen_hgt\2


Dim As Integer mouse_x, mouse_y, mouse_x_old, mouse_y_old, mouse_vel_x, mouse_vel_y

Dim Shared As masstype mass(1 To num_masses)
Dim Shared As celltype cell(1 To num_cells_row, 1 To num_cells_col)

''        narrow phase collision detection. Make particles neighbors if distance < r
Sub CheckIfNeighbor(Byval a_ As Integer, Byval b_ As Integer)

    If mass(a_).num_neighbors = neighbors Then Exit Sub

    If mass(b_).num_neighbors = neighbors Then Exit Sub


    Dim As vectortype dist

    dist.x = mass(b_).psn.x-mass(a_).psn.x: If Abs(dist.x) > r Then Exit Sub

    dist.y = mass(b_).psn.y-mass(a_).psn.y: If Abs(dist.y) > r Then Exit Sub


    Dim As Single dist_sqrd = dist.x*dist.x+dist.y*dist.y

    If dist_sqrd > r_sqrd Or dist_sqrd = 0 Then Exit Sub


    Dim As Single distance = Sqr(dist_sqrd)

    Dim As vectortype norm_dist = (dist.x/distance, dist.y/distance)

    Dim As Single density = 1 - distance / r

    '' create neighbors and save data
    With mass(a_)
        .num_neighbors += 1

        .Neighbor(.num_neighbors).a = b_
        .Neighbor(.num_neighbors).dist = distance
        .Neighbor(.num_neighbors).norm_dist.x = norm_dist.x
        .Neighbor(.num_neighbors).norm_dist.y = norm_dist.y
        .Density += density
    End With


    With mass(b_)
        .num_neighbors += 1

        .Neighbor(.num_neighbors).a = a_
        .Neighbor(.num_neighbors).dist = distance
        .Neighbor(.num_neighbors).norm_dist.x = -norm_dist.x
        .Neighbor(.num_neighbors).norm_dist.y = -norm_dist.y
        .Density += density
    End With


End Sub


Randomize Timer


Dim As Integer n = 1


''        place particles
For i As Integer = 1 To block_width
    For j As Integer = 1 To block_height
        With mass(n)
            .psn.x = i * radius * 2

            .psn.y = screen_hgt - j * radius * 2


            .psn.x += (Rnd-Rnd)
            .psn.y += (Rnd-Rnd)

            .vel.x += (Rnd-Rnd)
            .vel.y += (Rnd-Rnd)

        End With

        n += 1

    Next

Next


''        create opengl screen
Screenres screen_wid, screen_hgt, 32,, 2

glMatrixMode(GL_PROJECTION)
glLoadIdentity()
glViewport(0, 0, screen_wid, screen_hgt)
glOrtho(0, screen_wid, screen_hgt, 0, 0, 32)
glMatrixMode(GL_MODELVIEW)
glLoadIdentity()
glClearColor (0.6, 0.7, 1.0, 0.0)
'glenable(GL_ALPHA)
glEnable(GL_BLEND)
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
'glblendfunc(GL_DST_COLOR , GL_SRC_COLOR)

Getmouse(mouse_x, mouse_y)

''        main loop
Do


    '' save old mouse state
    mouse_x_old = mouse_x
    mouse_y_old = mouse_y

    ''        get new mouse state
    Getmouse(mouse_x, mouse_y)

    '' calc mouse velocity
    mouse_vel_x = (mouse_x-mouse_x_old)/dt
    mouse_vel_y = (mouse_y-mouse_y_old)/dt

    ''        mouse interaction
    For i As Integer = 1 To num_masses
        With mass(i)

            Dim As vectortype dst

            dst.x = .psn.x - mouse_x:        If dst.x > interaction_radius Then Continue For

            dst.y = .psn.y - mouse_y:        If dst.y > interaction_radius Then Continue For


            If dst.x*dst.x+dst.y*dst.y > interaction_Radius*interaction_Radius Then Continue For


            If mouse_vel_x Then .vel.x += (mouse_vel_x-.vel.x) * 0.16

            If mouse_vel_y Then .vel.y += (mouse_vel_y-.vel.y) * 0.16


        End With
    Next

    ''        reset particles
    For i As Integer = 1 To num_masses
        With mass(i)
            .density = 0

            .num_neighbors = 0

        End With

    Next


    ''        reset cells
    For i As Integer = 1 To num_cells_row
        For j As Integer = 1 To num_cells_col
            cell(i, j).Num_Masses = 0

        Next

    Next


    ''        update grid (broad phase collision detection)
    For i As Integer = 1 To num_masses
        Dim As Integer cell_row = (mass(i).Psn.y\cell_hgt)+1

        Dim As Integer cell_col = (mass(i).Psn.x\cell_wid)+1

        With cell(cell_col, cell_row)
            If .num_masses = cell_max_masses Then Continue For

            .num_masses += 1

            .Mass(.num_masses) = i
        End With

    Next


    ''        narrow phase collision detection
    For i As Integer = 1 To num_cells_row
        For j As Integer = 1 To num_cells_col
            If cell(i, j).num_masses = 0 Then Continue For

            For k_ As Integer = 1 To cell(i, j).num_masses
                Dim As Integer a_ = cell(i, j).mass(k_)

                If mass(a_).num_neighbors = neighbors Then Continue For


                ''        cell(x, y) - self
                For l_ As Integer = k_+1 To cell(i, j).num_masses
                    CheckIfNeighbor(a_, cell(i, j).mass(l_))
                Next


                ''        cell(x, y) - cell(x+1, y)
                If i < num_cells_row Then

                    For l_ As Integer = 1 To cell(i+1, j).num_masses
                        CheckIfNeighbor(a_, cell(i+1, j).mass(l_))
                    Next

                End If


                If j = num_cells_col Then Continue For


                ''        cell(x, y) - cell(x, y+1)
                For l_ As Integer = 1 To cell(i, j+1).num_masses
                    CheckIfNeighbor(a_, cell(i, j+1).mass(l_))
                Next


                ''        cell(x, y) - cell(x+1, y+1)
                If i < num_cells_row Then

                    For l_ As Integer = 1 To cell(i+1, j+1).num_masses
                        CheckIfNeighbor(a_, cell(i+1, j+1).mass(l_))
                    Next

                End If


                ''        cell(x, y) - cell(x-1, y+1)
                If i > 1 Then

                    For l_ As Integer = 1 To cell(i-1, j+1).num_masses
                        CheckIfNeighbor(a_, cell(i-1, j+1).mass(l_))
                    Next

                End If


            Next

        Next

    Next


    ''        set pressure
    For i As Integer = 1 To num_masses

        If mass(i).num_neighbors = 0 Then Continue For


        ''        mass i global pressure
        Dim As Single pressure = -k * (mass(i).density - rest_dens)

        For j As Integer = 1 To mass(i).num_neighbors

            Dim As Single gradient = 1 - mass(i).neighbor(j).dist / r

            Dim As Integer l = mass(i).neighbor(j).a

            ''        mass i + l global pressure
            Dim As Single press = (pressure - k * (mass(l).density - rest_dens) )

            ''        mass i + l local pressure
            press -= k_near * gradient

            ''        viscosity
            Dim As vectortype vel = (mass(i).vel.x-mass(l).vel.x, mass(i).vel.y-mass(l).vel.y)

            Dim As Single proj_vel = vel.x*mass(i).neighbor(j).norm_dist.x+vel.y*mass(i).neighbor(j).norm_dist.y

            press -=  viscosity * proj_vel * gradient

            press *= gradient

            mass(i).frc.x += press * mass(i).neighbor(j).norm_dist.x
            mass(i).frc.y += press * mass(i).neighbor(j).norm_dist.y

            mass(l).frc.x -= press * mass(i).neighbor(j).norm_dist.x
            mass(l).frc.y -= press * mass(i).neighbor(j).norm_dist.y

        Next


    Next


    ''        integrate (since particle mass = 1, force and acceleration are identical)
    For i As Integer = 1 To num_masses
        With mass(i)

            .frc.y += grav_acc

            .vel.x += .frc.x * half_dt
            .vel.y += .frc.y * half_dt

            .angvel += .torque * half_dt

            .psn.x += .vel.x * dt + .frc.x * dt * half_dt
            .psn.y += .vel.y * dt + .frc.y * dt * half_dt

            .vel.x += .frc.x * half_dt
            .vel.y += .frc.y * half_dt

            .angvel += .torque * half_dt

            .frc.x = 0

            .frc.y = 0


            .torque = 0


        End With

    Next


    ''        keep particles inside screen
    For i As Integer = 1 To num_masses
        With mass(i)

            If .psn.x < radius Then .psn.x = radius: .vel.x = -.vel.x*wall_friction: .vel.y *= wall_friction
            If .psn.x > screen_wid - radius Then .psn.x = screen_wid -radius: .vel.x = -.vel.x*wall_friction: .vel.y *= wall_friction

            If .psn.y < radius Then .psn.y = radius: .vel.y = -.vel.y*wall_friction: .vel.x *= wall_friction
            If .psn.y > screen_hgt - radius Then .psn.y = screen_hgt -radius: .vel.y = -.vel.y*wall_friction: .vel.x *= wall_friction

        End With

    Next


    ''        draw to screen
    glClear(GL_DEPTH_BUFFER_BIT Or GL_COLOR_BUFFER_BIT)

    glLoadIdentity()

    glEnable(GL_POINT_SMOOTH)
    glPointSize(r)
    glColor4f(0.0f, 0.0f, 0.0f, 0.25f)

    glBegin(GL_POINTS)
    For i As Integer = 1 To num_masses
        glVertex2f(mass(i).psn.x, mass(i).psn.y)
    Next

    glEnd()

    glDisable(GL_POINT_SMOOTH)
    glPointSize(1)
    glColor4f(1.0f, 1.0f, 0.0f, 0.75f)

    glBegin(GL_POINTS)
    For i As Integer = 1 To num_masses
        glVertex2f(mass(i).psn.x, mass(i).psn.y)
    Next

    glEnd()

    glFlush()

    Flip()

    Sleep 1, 1


Loop Until Multikey(1)

End