Fluid/Path-building thingy-jig
Blitz3D Forums/Blitz3D Programming/Fluid/Path-building thingy-jig
| ||
This is something I've been messing around with for the past hour or so, it's based on something I saw on bit-101.com and I decided to give it a go myself. Imagine a grid of signposts; when a particle reaches a signpost it turns slightly in the direction of the signpost and the signpost turns slightly to match the previous direction of the particle. The emergent behaviour is quite interesting as after a few moments "gulleys" begin to form which the particles flow down. When you see it, it'll be a little clearer! It could do with a bit of optimisation, and being 2D in Blitz3D it'll be a little slower anyway. It's not too bad on my computer (1000 particles, 40x30 grid), your mileage may vary! Anyway, the code: Graphics 800, 600, 32, 1 SetBuffer BackBuffer() Global MAXX = GraphicsWidth() Global MAXY = GraphicsHeight() Const GRIDX = 40 Const GRIDY = 30 Const CELL = 20 Const MIDCELL = 10 Const NUM_PARTICLES = 1000 Const PARTICLE_TURN = 15 Const PARTICLE_SPEED= 5 Const GRID_TURN = 1 Dim grid(GRIDX, GRIDY) For y = 0 To GRIDY - 1: For x = 0 To GRIDX - 1 grid(x, y) = Rand(360) Next: Next Type p_dat Field x#, y# Field ang End Type Global p.p_dat For n=0 To NUM_PARTICLES - 1 p.p_dat = New p_dat p\x = Rnd(MAXX) p\y = Rnd(MAXY) p\ang = Rnd(360) Next Repeat Cls count = MilliSecs() ; Move particles For p.p_dat = Each p_dat gx = Int(p\x / CELL) gy = Int(p\y / CELL) If p\ang > grid(gx, gy) Then p\ang = p\ang - PARTICLE_TURN: grid(gx, gy) = grid(gx, gy) + GRID_TURN If p\ang < grid(gx, gy) Then p\ang = p\ang + PARTICLE_TURN: grid(gx, gy) = grid(gx, gy) - GRID_TURN If p\ang < 0 Then p\ang = p\ang + 360 If p\ang > 359 Then p\ang = p\ang - 360 h# = Sin(p\ang) * PARTICLE_SPEED v# = Cos(p\ang) * PARTICLE_SPEED p\x = p\x + h# p\y = p\y + v# If p\x < 0 Then p\x = p\x + MAXX If p\x > MAXX Then p\x = p\x - MAXX If p\y < 0 Then p\y = p\y + MAXY If p\y > MAXY Then p\y = p\y - MAXY Next If KeyHit(57) Then For y = 0 To GRIDY - 1: For x=0 To GRIDX - 1 grid(x, y) = turn * 90 Next: Next turn = turn + 1 End If If KeyHit(19) Then For y = 0 To GRIDY - 1: For x=0 To GRIDX - 1 grid(x, y) = Rnd(360) Next: Next End If ; Draw particles Color 255, 255, 255 For p.p_dat = Each p_dat Plot p\x, p\y Next ; Draw grid count = MilliSecs() Color 32, 32, 32 For y = 0 To GRIDY - 1: For x = 0 To GRIDX - 1 x1 = x * CELL + MIDCELL y1 = y * CELL + MIDCELL x2 = x1 + Sin(grid(x, y)) * MIDCELL y2 = y1 + Cos(grid(x, y)) * MIDCELL Line x1, y1, x2, y2 If grid(x, y) < 0 Then grid(x, y) = grid(x, y) + 360 If grid(x, y) > 359 Then grid(x, y) = grid(x, y) - 360 Next: Next count = MilliSecs() - count Color 255, 255, 255 Text 10, 10, count Text 10, MAXY - 26, "R - randomise all squares; Space - Flip direction of squares NSEW; Esc - quit" Flip Until KeyHit(1) End |
| ||
That's very interesting. Nice technique. Im sure it has implications although I'm just not certain of what just yet. I notice the movement of the particles eventually aligns the randomised barriers. If these aprticles gained/lost energy through their collisions I wonder what the results would be like then... |
| ||
Yeah, eventually a stable pattern is produced and nothing much else seems to happen. Although, all it takes is one particle to nudge a barrier, and it can flip the whole situation over in a matter of a few minutes. Gotta love chaos theory! I'm going to experiement with this a bit more, and try and optimise it as well. |
| ||
Cool. All it takes is a random delay between Flips to keep the pattern from stabilising - would make an excellent screensaver. ... actually - just tried that and what you need is a burst of consecutive Flips followed by a settling down period. First Sswift's orbiting balls, now AdamJ's Shoals of the Canyons (?) .... looks like an emergent behaviour renaissance for Blitz coders ! |
| ||
This is realy cool! It looks like fluid dynamics. With a little optimization, I think that it could be used to make fire, smoke, and other particle effects! |
| ||
I also thought of a swarm of ants..... |
| ||
hey this is cool, thanks. I changed and added some things. The angle turning wasn't being calculated correctly, so I added a function that makes an angle turn toward another angle by turning whichever way is closest. I also made it preserve gulleys by not turning grid angles that are already close to the particle's angle. I also added a water effect. Set TRAILS to 1 to see it. its cool. |
| ||
Ah! I thought the angles weren't being calculated correctly, but I couldn't work out what. (this was 2am though!) Thanks for fixing it. And also the lockbuffer stuff as well, I forgot you could do that. Much faster! Glad you like it anyway, I'm wondering what else I could do with this. I'm tempted to make it in 3D mode instead and use a spherical gradiant sprite to make it look more like realistically flowing water. I could even make it flow in 3 dimensions thinking about it, more like smoke. I'll post it when I'm done! EDIT: Excellent, you can bump up the number of particles now as well, I have 10,000 running quite smoothly... EDIT: Just tested the water effect as well, that's really quite impressive well done! |
| ||
thanks. this is so awesome for texture generation! Press T to make a texture. You can fiddle with the constants too... for a long long time. Heres some textures I made. Isn't the top one freaky as hell? It looks like a zombie ![]() |
| ||
I love the bottom texture, looks like some kind of wood grain or rope. I've done a little altering of the code. Nothing major, just something so you can turn the grid on/off and fiddled with the particle color code. SeedRnd MilliSecs() Graphics 800, 600, 0, 1 Const TRAILS=0 If Not trails Then SetBuffer BackBuffer() Global MAXX = GraphicsWidth() Global MAXY = GraphicsHeight() Const GRIDX = 40 Const GRIDY = 30 Const CELL = 20 Const MIDCELL = 10 Const NUM_PARTICLES = 5000 Const PARTICLE_TURN = 15 Const PARTICLE_SPEED= 2 Const GRID_TURN = 1 Const RECT_SIZE = 50 Const RECT_SPEED = 2 Dim grid(GRIDX, GRIDY) For y = 0 To GRIDY - 1: For x = 0 To GRIDX - 1 grid(x, y) = Rand(360) Next: Next Type p_dat Field x#, y# Field ang Field r, g, b, dec_col End Type Global p.p_dat For n=0 To NUM_PARTICLES - 1 p.p_dat = New p_dat p\x = Rnd(0,MAXX-1) p\y = Rnd(0,MAXY-1) p\ang = Rnd(360) p\r = 64 p\g = 64 p\b = Rnd(64) + 192 p\dec_col = (p\r * $FF00) + (p\g * $FF) + p\b Next Repeat If Not trails Then Flip:Cls ; Move particles For p.p_dat = Each p_dat gx = Int(p\x / CELL) gy = Int(p\y / CELL) g=grid(gx, gy) p\ang=anglemoveto(p\ang,g,PARTICLE_TURN) ;only change the grid angle if it is at least 30 degrees different from the particle's angle If Abs(angledif(g,p\ang))>30 Then grid(gx, gy)=anglemoveto(g,p\ang,GRID_TURN) EndIf h# = Sin(p\ang) * PARTICLE_SPEED v# = Cos(p\ang) * PARTICLE_SPEED p\x = p\x + h# p\y = p\y + v# If p\x < 0 Then p\x = p\x + MAXX ElseIf p\x >= MAXX Then p\x = p\x - MAXX EndIf If p\y < 0 Then p\y = p\y + MAXY ElseIf p\y >= MAXY Then p\y = p\y - MAXY EndIf Next If trails Then LockBuffer BackBuffer() For p = Each p_dat WritePixel p\x, p\y, p\dec_col Next UnlockBuffer BackBuffer() For p = Each p_dat movex=Sin(p\ang)*Rnd(1,RECT_SPEED) movey=Cos(p\ang)*Rnd(1,RECT_SPEED) size=Rand(2,rect_size) x=p\x-size/2 y=p\y-size/2 CopyRect x,y,size,size,x+movex,y+movey Next Else count = MilliSecs() LockBuffer BackBuffer() ; Draw particles For p = Each p_dat WritePixel p\x, p\y, p\dec_col Next ; Draw grid If grid_switch Then Color 44, 44, 44 For y = 0 To GRIDY - 1: For x = 0 To GRIDX - 1 x1 = x * CELL + MIDCELL y1 = y * CELL + MIDCELL x2 = x1 + Sin(grid(x, y)) * MIDCELL y2 = y1 + Cos(grid(x, y)) * MIDCELL Line x1, y1, x2, y2 Next: Next End If UnlockBuffer BackBuffer() count = MilliSecs() - count Color 255, 255, 255 If KeyHit(34) Then grid_switch = Not grid_switch If KeyHit(59) Then showhelp = Not showhelp If showhelp Then Text 10, 10, "Render time: " + count + " m/sec" Text 10, 30, "G - Show/Hide Grid" Text 10, 46, "R - Randomise all squares" Text 10, 62, "F - Flip direction of grid" Text 10, 78, "Esc - quit" Else Text 10, 10, "F1 - help" End If If KeyHit(33) Then Cls For y = 0 To GRIDY - 1: For x=0 To GRIDX - 1 grid(x, y) = turn * 90 Next: Next turn = turn + 1 If turn>3 Then turn=0 End If If KeyHit(19) Then Cls For y = 0 To GRIDY - 1: For x=0 To GRIDX - 1 grid(x, y) = Rnd(360) Next: Next End If EndIf Until KeyHit(1) End ;makes the angle a little closer to the target angle and returns it Function AngleMoveTo#(anglenow#,angletar#,amount#) amount=amount*Sgn(angletar-anglenow) If Abs(angletar-anglenow)>180 Then anglenow=anglenow-amount Else anglenow=anglenow+amount EndIf If anglenow<0 Then anglenow=anglenow+360 ElseIf anglenow>360 Then anglenow=anglenow-360 EndIf Return anglenow End Function ;how far away two angles are, assuming they can skip over the end of anglemax to get to 0 ;gives positive and negative return values Function AngleDif#(anglenow#,angletar#,anglemax=360) Local temp# temp#=angletar-anglenow If temp>+anglemax/2 Then Return temp-anglemax If temp<-anglemax/2 Then Return temp+anglemax Return temp End Function |
| ||
As for the 3D effects for water, My thoughts on the best implementation are: The 'dots' represent Positive Y Vertex points for triangles in a mesh with some soft-surface physics. Bit techy for me to even attempt, but that's my thoughts anyway. |
| ||
I've created a 3D version of this, but it's not working as how I imagined it. Rather than arranging itself into gulleys like the 2D version, there's no hint of any kind of order at all. If you look a little closer, you do notice that the particles loosely follow each other, but not very much. I don't think it's a problem with the code as such, more to do with the fact that in 3D the particles have more freedom of movement and won't be as inclined to follow each other so much. Perhaps if I left it running a bit longer something may emerge, but I'm doubting it. I'm going to keep looking to make sure I haven't made any mistakes and I'll post the source when I'm done. |