grid based physics optimization = weird results...
BlitzMax Forums/BlitzMax Programming/grid based physics optimization = weird results...
| ||
I was working on my physics engine and I thought of something... why dont I update all of the collisions with my grid optimization backwards instead of forwards to see if it increases stability... now the order you do your collisions shouldnt make a whole lot of difference should it? no but for some reason the boxes start intersecting and jittering all over the screen. Does anyone know enough about grid optimizations in verlet physics to guide me in the right direction... here is what I did...'grid loop For y = 0 To GridHeight-1 For x = 0 To GridWidth-1 'grid stuff here Next Next was changed to this 'grid loop For y = GridHeight-1 To 0 Step -1 For x = 0 To GridWidth-1 'grid stuff here Next Next now kotsoft said he noticed that my fluid simulations had 'grid artifacts' so I believe there must be a bug in my code that leads to 'grid artifacts' (aka the fluid forming a grid shape which looks really odd for a fluid) and it also must cause my code to not run properly backwards... :p but from the evidence given, Nate the Great and his magnifying glass could not find the bug... edit: heck why dont I just give out the whole of grid.bmx Private Global GridArray:Grid[100] Global Gridlength:Int Global GridWidth:Int Global GridColumnCount:Int = 0 Global GridRowCount:Int = 0 Public Global GridActive:Int = False Type Grid 'this preforms fixed grid optimizations Field x:Int 'x and y in array not in the physics world Field y:Int Field VIG:verlet[20] Field VIGcount:Int Function Active:Int() Return GridActive End Function Function GridSize:Int() Return GridLength End Function Function Activate:Int(siz:Int) maxverlsiz = siz gridlength = maxverlsiz gridwidth = gridlength GridColumncount = (physicsrightbound-physicsleftbound)/gridwidth + 1 GridRowCount = (physicslowerbound-physicsupperbound)/gridlength + 1 gridarray = gridarray[..((GridRowCount+1)*(GridColumnCount+1))] GridActive = True Local x:Int = 0 Local y:Int = 0 For x = 0 To (GridColumnCount*GridRowCount)-1 Gridarray:grid[x] = New grid Next Local g:grid For x = 0 To GridColumncount-1 For y = 0 To gridRowCount-1 g:grid = Gridarray:grid[(x+y*GridColumnCount)] g.x = x g.y = y Next Next Return True End Function Function Clear() 'clears the grid Local g:grid Local i:Int For i = 0 To (GridRowCount*GridColumnCount)-1 g:grid = GridArray:grid[i] g.VIGcount = 0 Next End Function Function Update() Grid.Clear 'clear the grid Grid.Sort 'sort each verlet into the grid Grid.Collisions 'calculate collisions for each grid piece End Function Function Collisions() Local g:grid Local gg:grid Local ggg:grid Local x:Int = 0 Local y:Int = 0 Local xflag:Int = 0 Local yflag:Int = 0 For y = 0 To GridRowCount-1 For x = 0 To GridColumncount-1 g:grid = gridarray:grid[(x + (y)*GridColumnCount)] If g.VIGcount Then xflag = 0 yflag = 0 grid.collidegrid(g:grid,g:grid) If g.y < gridrowcount-1 Then yflag = True If g.x < gridcolumncount-1 Then xflag = True If xflag Then ggg:grid = gridarray:grid[g.x+1+(g.y)*GridColumnCount] grid.collidegrid(g:grid,ggg:grid) If yflag Then gg:grid = gridarray:grid[g.x+(g.y+1)*GridColumnCount] grid.collidegrid(g:grid,gg:grid) grid.collidegrid(gg:grid,ggg:grid) gg:grid = gridarray:grid[g.x+1+(g.y+1)*gridColumnCount] grid.collidegrid(g:grid,gg:grid) EndIf Else If yflag Then gg:grid = gridarray:grid[g.x+(g.y+1)*GridColumnCount] grid.collidegrid(g:grid,gg:grid) EndIf EndIf EndIf Next Next End Function Function CollideGrid(g:grid,gg:grid) If g.VIGCount And gg.VIGcount Then Local v:verlet Local vv:verlet For Local cnt1:Int = 0 To g.VIGcount-1 For Local cnt2:Int = 0 To gg.VIGcount-1 v:verlet = g.VIG:verlet[cnt1] If g = gg Then v.done = True vv:verlet = gg.VIG:verlet[cnt2] If (v <> vv) And ((g = gg And vv.done = False) Or g >< gg) Then DoVcollisions(v:verlet,vv:verlet) Next Next EndIf End Function Function Sort() Local vcnt:Int Local v:verlet Local x:Int Local y:Int Local g:grid For vcnt = 0 To verlcount-1 'loop through all verlets and place them in the correct grid cells v:verlet = verletarray:verlet[vcnt] 'extracts verlet out of verlet array x = (v.x-physicsleftbound)/GridWidth 'computes the x and y grid position the verlets should be stored in y = (v.y-physicsupperbound)/Gridwidth If x > -1 And y > -1 And x < gridcolumncount And y < gridrowcount Then g:grid = gridarray:grid[x+y*GridColumnCount] 'gets the grid instance out of the grid array g.add(v:verlet) 'adds verlet to the grid cell EndIf Next End Function Function IsActive:Int() 'allows user to know if the grid is active or not Return GridActive End Function Method add(v:verlet) VIG:verlet[VIGcount] = v:verlet VIGcount :+ 1 If VIGcount > VIG.length - 2 Then VIG = VIG[..VIG.length+20] v.GD:grid = Self End Method End Type IK its messy and uncommented but I am working on it... commenting every line until I find the bug. edit 2: oo I love when this happens... I try to figure something out for ages then boom it hits me within a minute of posting :) problem solved edit 3: nevermind, it doesnt work |
| ||
so does anyone know about verlet based physics around here? |
| ||
Not really... but could it be that calculating the grid from start to finish each time would grid artifact the calculations... what if... and this is a big WHAT IF, you started at a different point each loop and cycle once through it... or used some kind of randomization technique that would hit every point? |
| ||
or used some kind of randomization technique that would hit every point? thats a good idea if I could find one fast enough.... I guess I could make a stack (array) or all the grids in order then switch 2 radom grids 50 times... that might do the trick, but I know I am doing something else wrong... there is something that is buggy about the code... it should in theory work perfectly backwards but it doesnt!?! |
| ||
Am i correct in saying that vertlets will not interact unless they are in the same grid? if so what happens when vertlets are on the right and left or top and bottom borders of adjacent grids? {edit} On closer look at the code I am incorrect...wrongo... see below |
| ||
Could be here?Function CollideGrid(g:grid,gg:grid) If g.VIGCount And gg.VIGcount Then Local v:verlet Local vv:verlet For Local cnt1:Int = 0 To g.VIGcount-1 For Local cnt2:Int = 0 To gg.VIGcount-1 v:verlet = g.VIG:verlet[cnt1] If g = gg Then v.done = True vv:verlet = gg.VIG:verlet[cnt2] If (v <> vv) And ((g = gg And vv.done = False) Or g >< gg) Then DoVcollisions(v:verlet,vv:verlet) Next Next EndIf End Function Don't know if this is correct but you might be able to optimize this... Function CollideGrid(g:grid,gg:grid) If g.VIGCount And gg.VIGcount Then Local v:verlet Local vv:verlet For Local cnt1:Int = 0 To g.VIGcount-1 v = g.VIG[cnt1] For Local cnt2:Int = 0 To gg.VIGcount-1 vv:verlet = gg.VIG[cnt2] If v=vv Continue If vv.done=False DoVcollisions(v,vv) Next v.done=True Next EndIf End Function this part was confusing the sh&t out of me... If g = gg Then v.done = True so if you are processing the same grids this vertlet is done? If (v <> vv) And ((g = gg And vv.done = False) Or g >< gg) Then DoVcollisions(v:verlet,vv:verlet) so if the vertlets are different AND (the grids are the same and the second vertlet is not done... or the grids are different) do vertlet collisions Eyes crossed :P |
| ||
When you reverse your grid processing you are looking at grids behind you rather than forward... I think your grid reversal problem is here: If g.y < gridrowcount-1 Then yflag = True If g.x < gridcolumncount-1 Then xflag = True If xflag Then ggg:grid = gridarray:grid[g.x+1+(g.y)*GridColumnCount] grid.collidegrid(g:grid,ggg:grid) If yflag Then gg:grid = gridarray:grid[g.x+(g.y+1)*GridColumnCount] grid.collidegrid(g:grid,gg:grid) grid.collidegrid(gg:grid,ggg:grid) gg:grid = gridarray:grid[g.x+1+(g.y+1)*gridColumnCount] grid.collidegrid(g:grid,gg:grid) EndIf Else If yflag Then gg:grid = gridarray:grid[g.x+(g.y+1)*GridColumnCount] grid.collidegrid(g:grid,gg:grid) EndIf EndIf |
| ||
thanks for taking such an indepth look I will explain all of the quirks that you pointed out. this part was confusing the sh&t out of me... this part is a bit confusing but its basically saying if I am checking for collisions between two grids and they are the same grid then v.done = true so I wont redundantly check for collisions which can lead to weird results we use vv.done later... in the next line If (v <> vv) And ((g = gg And vv.done = False) Or g >< gg) Then DoVcollisions(v:verlet,vv:verlet) this says if the verlets are not the same then if the grid is the same and these verlets have not been checked togethor already or if the grids are different the it doesnt matter if they have been checked already then wait theres a flaw in there somewhere... I sense it but I dont really see it.... you know the feeling. I think your grid reversal problem is here: I dont see a problem.. could you be a little more specific? |
| ||
You check the next grid to the right, down, and down-right... When you reverse the flow wouldn't you need to check the left, upper, and upper-left? maybe wait theres a flaw in there somewhere... I sense it but I dont really see it.... you know the feeling. Ya, I was getting the sense something was wrong.. try the re-written version I did: Function CollideGrid(g:grid,gg:grid) If g.VIGCount And gg.VIGcount Then Local v:verlet Local vv:verlet For Local cnt1:Int = 0 To g.VIGcount-1 v = g.VIG[cnt1] For Local cnt2:Int = 0 To gg.VIGcount-1 vv:verlet = gg.VIG[cnt2] If v=vv Continue If vv.done=False DoVcollisions(v,vv) Next v.done=True Next EndIf End Function Basically the above compares each vertlet in grid g to each vertlet in grid gg. If vertlets are the same (should only occur when the same grid is being compared) it passes over it... if the vertlet being compared has already been done it also ignores it. Otherwise it will DoVCollisions... finally when the vertlet in grid g is complete it flags as done. |
| ||
Ok I will try it skully :) and I havent tried it yet because I dont have verlet max with me atm but I am thinking about just rewriting the grid function if yours doesnt work. |
| ||
Funny thing is... if I adapt your vertlet code to my tile engine... everything's position consists of a tile pointer, x, & y... so they are automatically sorted by the map tile size for collision detection :) |
| ||
[Funny thing is... if I adapt your vertlet code to my tile engine... everything's position consists of a tile pointer, x, & y... so they are automatically sorted by the map tile size for collision detection :) yeah I guess... I dont know how I would go about doing verlet to tile collisions though... Its harder than it sounds. |
| ||
Its easy.. I have a pixelcollides function for particles :) |
| ||
I hope its fast enough for fluids!!! lol |
| ||
Might be... it just reads the image pixelmap... might not be for the number of particles you're dealing with though! |
| ||
yeah it may be best to use polygons to outline the tiles but those dont have a grid optimization atm |
| ||
the pixelcollision is working at 100000 operations in 43 ms on my machine |
| ||
hmmm so I assume 1,000 fluid points in .43 ms? and then lets say theres 100 tiles thats 100,000 43 ms again :p |
| ||
Well, we can process them differently.. Since each fluid point needs to be processed, we could loop through those, comparing them to other points within adjacent tiles rather than looping through every tile. In fact, you could adapt that to your code as well... since you store the grid that each point is contained in within the vertlet itself, you could loop through the vertlets and process the grids as you go... this way you would never touch a grid that does not contain a vertlet. Food for thought... |
| ||
I'm making some assumptions here... like there is a TList of vertletsFor v:vertlet=eachin Vertlets If Not v.flag g:Grid=gridarray[v.x+(v.y)*GridColumnCount] CollideGrid(g,g) If v.y < gridrowcount-1 Then yflag = True If v.x < gridcolumncount-1 Then xflag = True If xflag Then ggg:grid = gridarray:grid[v.x+1+(v.y)*GridColumnCount] grid.collidegrid(g,ggg) If yflag Then gg = gridarray:grid[v.x+(v.y+1)*GridColumnCount] grid.collidegrid(g,gg) grid.collidegrid(gg,ggg) gg = gridarray:grid[v.x+1+(v.y+1)*gridColumnCount] grid.collidegrid(g,gg) EndIf Else If yflag Then gg = gridarray:grid[v.x+(v.y+1)*GridColumnCount] grid.collidegrid(g,gg) EndIf EndIf EndIf Next |
| ||
I'm making some assumptions here... like there is a TList of vertlets well I use arrays for speed but I am going to add one for user accesability... right now everything is low level physics... no advanced bodies or groups or anything... |
| ||
Ok then you can just replace the eachin vertlets with rifling through your array. |
| ||
yeah... I decdided to just rewrite my grid now though. |