Page 1 of 4 123 ... LastLast
Results 1 to 10 of 35

Thread: Some gravity particles

  1. #1

    Some gravity particles

    Made this with nxPascal, using custom made GLSL 3D-shader: (Changed to improved version of the video)

    Screenshot of earlier version: http://img32.imageshack.us/img32/2327/particles.png

    Each particle is now drawn with size 5 GL_POINTS. I used total 3000 particles, and any more would make it lag more horribly than it currently is. It's not about rendering though, as seen in the screenshot. I initialized it with 200000 particles and it wasn't even breaking a sweat at the vsync'd 63 fps with 0ms render time.

    So do you know how i can optimize this gravity calculation?
    Code:
    function Hypot3f_2(const x, y, z: single): single; inline;
    begin
      result:=sqrt(x*x+y*y+z*z);
    end;
    
    function VectorDist_2(const a, b: PVector): single; inline;
    begin
      result:=hypot3f_2(a^.x-b^.x, a^.y-b^.y, a^.z-b^.z);
    end;
    
    procedure TGame.GameLoop;
    var i, j: integer; d, f: single; vec: TVector;
        start: cardinal;
    begin
      start:=nx.GetTick;
      ... // Quick interpolation of camera, zoom etc...
    
      if not paused then begin
        // Update forces
        for i:=0 to count-2 do
          with star[i] do
            for j:=i+1 to count-1 do begin
              d:=vectordist_2(@v, @star[j].v);
              // If particles exactly overlap, rather keep current velocity this frame
              if d>0 then begin
                f:=0.00001/(d*d);
                d:=f/d;
                if star[j].positive<>positive then d:=-d;
                vec.x:=(star[j].v.x-v.x)*d;
                vec.y:=(star[j].v.y-v.y)*d;
                vec.z:=(star[j].v.z-v.z)*d;
                movement.x:=movement.x+vec.x;
                movement.y:=movement.y+vec.y;
                movement.z:=movement.z+vec.z;
                star[j].movement.x:=star[j].movement.x-vec.x;
                star[j].movement.y:=star[j].movement.y-vec.y;
                star[j].movement.z:=star[j].movement.z-vec.z;
              end;
            end;
        // Move particles
        for i:=0 to count-1 do
          with star[i] do begin
            v.x:=v.x+movement.x;
            v.y:=v.y+movement.y;
            v.z:=v.z+movement.z;
          end;
      end;
      looptime:=nx.GetTick-start;
    end;
    Last edited by User137; 12-06-2013 at 10:53 PM.

  2. #2
    While I'm not good at math I can't give you advices on improving the way you calculate your Physics.
    But as a programer I would definitly advise you to start using multithreading for this.

  3. #3
    I could precalculate the frames, store like every 20th, and then playback it with interpolation. That way threading wouldn't be a problem at least. Also my CPU only has 2 cores so i would only double the performance, but that's something i guess.

    *Sees a vision in mind of splined curves of the whole particle animation and drools a little...*
    Last edited by User137; 12-06-2013 at 11:42 PM.

  4. #4
    Quote Originally Posted by User137 View Post
    That way threading wouldn't be a problem at least. Also my CPU only has 2 cores so i would only double the performance, but that's something i guess.
    Why would threading be a problem? All you have to do is make sure that you don't actually move any of the particle until you have calculated all future position to every other particle.
    For this you would actually need double-buffering of some of the particle information (position, movment heading). And if you have particles saved as objects (classes) you can use Getter and Setter methods of class properties for easily handling of double-buffering.

  5. #5
    Problem is in that each thread will access to same particle data at the same time. I cannot split them to isolated index groups, because each particle is compared to every other. So many threads could apply forces to same particle at the same time. But it's just a simple sum operation, the calculation does not depend on value of the movement vector, so it might not matter. I do not know. I could do the "// Move particles" part in main thread after the forces are updated, cause i think that's the fastest part of it. I would also put main thread waiting for those to finish before continuing. It will go to same 1-4 fps while counting it anyway, it's hardly controllable at all.

    Extra treat, shaders:
    Code:
    #version 120
    attribute vec3 in_position;
    attribute vec4 in_color;
    varying vec4 color;
    uniform mat4 pmv;
    void main() {
      color = in_color;
      gl_Position = pmv * vec4(in_position, 1.0);
    }
    Code:
    #version 120
    varying vec4 color;
    void main() {
      gl_FragColor = color;
    }
    edit: Using 4 physics threads now on the time-critical part, and CPU use went up to 90% or so. I don't at least see any differences in the visual outcome, sure it counts it faster.
    Attached Images Attached Images
    Last edited by User137; 13-06-2013 at 03:16 AM.

  6. #6
    you should not approach this task with a brute force of the CPU, because you will surely get stuck.

    I can make a couple of suggestions to improve the algorithm:

    1 - use GPU to do your calculations.
    2 - cluster your space (each cluster can contain hundreds of particles). calculate the total mass and the center of gravity of each cluster. attract individual particles with each other within a cluster (or you might create sub-clusters if you have too many particles). attract individual particles within a cluster to the center of mass of the other clusters. this will significantly decrease the time complexity of your calculations (at the moment you have O(N) = N * N).

  7. #7
    Can i do clustering, if start situation is static mass that is mix of both gravity types? I think at beginning there would then be as many clusters as there are particles. I'm also not very convinced yet, because cluster's gravity center location is a living thing that depends on each of its children. I guess it would be more of a heuristic simulation.

    Another funny sidenote about this simulator, is that currently the radius of the particle mass is 10. But if i reduce it down to 0.001 or so - all particles in very tiny spot next to eachother, they literally explode in a "Big Bang". (But i've seen that before, didn't come as a surprise...)

    I think the clustering might work, but with different start situation. Still it would be very rough estimation. Say 50-100 clusters with random mass (mass being amount of assigned particles). Then each particle only orbiting the 1 cluster, and each cluster orbiting all the other clusters. Problem becomes of deciding when 1 particle switches to orbit another cluster, and i'm not sure i could solve it. Firstly the borderline between 2 clusters would be calculated with Newton's equations, spot between them where gravity force is equal. Wouldn't i be doing that calculation for each particle per each cluster all the time? Or each cluster would count and store its nearest neighbours every once in a while.

    Anyway that's too much work for simple test.
    Last edited by User137; 13-06-2013 at 03:58 PM.

  8. #8
    there might be one problem with using clusters, and that is the diminishing gravity over distance. I am not sure if it is possible to simulate this effect. I might give it a try myself.
    Last edited by Dan; 13-06-2013 at 04:32 PM.

  9. #9
    Quote Originally Posted by User137 View Post
    Problem is in that each thread will access to same particle data at the same time.
    Unles your threads are only reading the data and not changing it there should be no problem. Problem would only ocur if one of the threads would be changing some values of one particle while other tread would be reading it (data corruption).

    Quote Originally Posted by User137 View Post
    I could do the "// Move particles" part in main thread after the forces are updated, cause i think that's the fastest part of it. I would also put main thread waiting for those to finish before continuing.
    First of all never and I mean NEVER put the min thread on hold for other threads to finish. Why? Doing so you prevent your application to process Windows messages or System messages if you are using some other platform. And if you do so then your application would be considered as being hanged application by the OS and your OS might simply go and forcefully kill it. You definitly don't want this.
    If you use double-buffering approach you actually dont even need main thread to do any moving.

    Let me try to explain you how would I approach to this problem:
    First I would save information for each particle in class object with double-buffering so I that I have acces to the variables using properties. Why using properties?
    Becouse with getter and setter methods of properties you can easily implement double-buffering.
    The class code for particle wouls look something like this:
    Code:
    type
      TParticle = class(TObject)
      private
        PSecondBuffer: ^Boolean;
        FPosition1: T3DPoint;
        FPosition2: T3DPoint;
        FForce1: T3DVector;
        FForce2: T3DVector;
      protected
        function GetPosition: T3DPosition;
        procedure SetPosition(AValue: T3DPoint);
        function GetForce: T3DVector;
        procedure SetForce (AValue: T3DVector);
      public
        constructor Create(AOwner: TObject; var SecondBuffer: Boolean); override;
        property Position: T3DPoint read GetPosition write SetPosition;
        property Force: T3DVector read GetForce write SetForce;
      end;
    
    //We use this global variable to determine whether we will read from primary or secondary buffer
    var SecondBuffer: Boolean; 
    
    implementation
    
    
    { TParticle }
    constructor TParticle.Create(AOwner: TObject; var SecondBuffer: Boolean);
    begin
        inherited;
        //We assing pointer to external boalean variable for controling whether
        //we read from primary or secondary buffer
        FSecondBuffer := @SecondBuffer;
    end;
    
    function TParticle.GetForce: T3DVector;
    begin
        //If the second buffer is true then we read from second force variable
        //else we read from first force variable
        if FSecondBuffer = True then Result := FForce2 //Secondarry buffer
        else Result := FForce1; //Primary buffer
    end;
    
    function TParticle.GetPosition: T3DPosition;
    begin
        //If the second buffer is true then we read from second postiion variable
        //else we read from first position variable
        if FSecondBuffer = True then Result := FPosition2 //Secondarry buffer
        else Result := FPosition1; //Primary buffer
    end;
    
    procedure TParticle.SetForce(AValue: T3DVector);
    begin
        //With the difference of getter methods we now if second buffer is true
        //write data to first force variable to avoid changing the data that might
        //be read by other threads
        //We never change the same variable as it is available for reading
        if FSecondBuffer = True then FForce1 := AValue //Primary buffer
        esle FForce2 := AValue; //Secondarry buffer
    end;
    
    procedure TParticle.SetPosition(AValue: T3DPoint);
    begin
        //We handle position variable changing same as above.
        if FSecondBuffer = ture then FPosition1 := AValue //Primary buffer
        else FPosition2 := AValue; //Secondarry buffer
    end;
    As you see each class has two internal variables for every property (value) that might be changed by other threads during curent cycle while still preserving acces to current data without it being corupted by other threads.
    For contgrolling this we are actually using external boolean variable. This means that with a single call we can change, which internal variable would be used for reading and which for writing, for even a few thousands of class objects if we need it. This is probably the best solution for controlling class double-buffering.
    NOTE1: If you need per class double-bufering control you need as many global variables as you have distict classes.
    NOTE2: You could also save this information into the class itself but then you will have to update this information fora each class which could result in several thousands calls.

  10. #10
    Cool demo. Could you share sources and exe file so other people can play with it?

Page 1 of 4 123 ... LastLast

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •