Анимация с помощью 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