Archived

This topic is now archived and is closed to further replies.

Mulligan

Is this right?

Recommended Posts

Assuming i choose correct values for mass, and radius for stars and the center of the galaxy, would this work? (Should it work?) bool CGalaxy::Update() { ULDOUBLE G = 0.00000000006670f; ULDOUBLE dForce; ULDOUBLE dz, dx, dy; ULDOUBLE dRadius; for( int i = 0; i < m_iNumStirs; i++ ) { m_pStars->m_vPos.x += m_pStars[i]->m_vDir.x; m_pStars[i]->m_vPos.z += m_pStars[i]->m_vDir.z; m_pStars[i]->m_vPos.y += m_pStars[i]->m_vDir.y; dx = m_pStars[i]->m_vPos.x - m_vPos.x; dz = m_pStars[i]->m_vPos.z - m_vPos.z; dy = m_pStars[i]->m_vPos.y - m_vPos.y; dRadius = sqrt( dx*dx + dz*dz + dy*dy ); dForce = G * ( m_dMass * m_pStars[i]->m_dMass ) / dRadius; m_pStars[i]->m_vDir.x += dForce * ( dx / dRadius ); m_pStars[i]->m_vDir.z += dForce * ( dz / dRadius ); m_pStars[i]->m_vDir.y += dForce * ( dy / dRadius ); } return true; } [edited by - Mulligan on August 31, 2002 4:25:44 PM]

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
i couldn''t make out your code (missing structs)
and I noticed you are calculating the secant version of delta values.

Well, you could actually use vector fields and the computations would be more accurate and reliable. Vector fields are gradients of multidimensional surfaces that will give you a vector in the direction and magnitude of the field effect at that point.

Really, you only have to do the calculations once, then you could plugin some constants and get the vector effect of every body that effects the object you are trying to determine the new position of. In our case, the 3d surface would be an inverse sphere and the vector field would be the gradient of the sphere.

I forget the exact equation to take the gradient of (I can do the operation). I think it''s a negative inverse sphere equation multiplied by the magnitude of the acceleration constant. The gradient of the surface is the surface equation multiplied by the "del" operator.

If you give me till 10:00 eastern time, I will post the formula for the surface and the equations for the sphere.

Share this post


Link to post
Share on other sites
Gravitational fields are defined by Newton's Law of Gravitation , which states that the force of attraction exerted on a particle(object) of mass m1 located at (x,y,z) by a particle of mass m2 located at (a,b,c) is given by:

F(x,y,z) = (-G*m1*m2)/[(x-a)^2 + (y-b)^2 + (z-c)^2]  
*u

Where u is a unit vector in the direction of (x,y,z) from (a,b,c) and G is a gravitational constant.

------------------------------------------

Turns out you don't have to do any partial derivatives at all!!

I can solve these equation to give you a vector, but you have to look up the Gravitational Constant (a very small number).

First, we define the origin of the object that is exerting the force(O1 planet) as (a,b,c). We also define the Object being exerted on as points (x,y,z) (O2 ship).

Next, we build our unit u vector. Simply subtract the Object position (x,y,z) (O2 ship) form the object that is exerting the force(O1 planet). This will give a vector in the direction of O2 ship from O1 planet:

= u

To normalize the vector, divide each component by the magnitude:

Magnitude u = ||u|| = sqrt[(a-x)^2 + (b-y)^2 + (c-z)^2]

Normal u = u /||u|| = <(a-x)/||u||, (b-y)/||u||, (c-z)/||u||>

Now, compute the first part of the equation with the numbers plugged in:

[-G*m1*m2]/[(x-a)^2 + (y-b)^2 + (z-c)^2)]

Store this result, so we can multiply (scale) it into the unit vector u . Rember that G is a very small number. This should bring the masses multiplied to a relatively managable number. This number is then divided by the distance squared between the objects. Obviously, the farther apart the objects get, the bigger the denominator grows and the smaller the number it kicks out. The final result will always be negative. This negative number is going to scale the unit vector u according to the force exerted and will flip the vector to point towards the first object (O1) (a,b,c).

So make the first object [planet] (a,b,c) the larger object, so the force will be applied to the correct object [ship].

Now, that number that you stored (lets say R) will be multiplied into the unit vector u :

<[(x-a)/||u||]*R, [(y-b)/||u||]*R, [(z-c)/||u||]*R>

This will give us a vector pointing towards the planet exerting the force on the ship. The result will be a vector is terms of Meters/sec because this is a velocity field. So the magnitude of the vector will represent the meters traveled in 1 second.

For purposes of your engine, you will have to step this time down to your frame rate, which renders more than once a second. Since we know that this vector last for 1 second, we can multiply a constant (t) that represnted the time in seconds. At one second, the length will not be affected, as everything is multiplied by one. At anything less, the vector will be scaled back to the appropiate time key:

<[(x-a)/||u||]*R*t, [(y-b)/||u||]*R*t, [(z-c)/||u||]*R*t>


    
//These would have to be passed in or initialized by you.

float x,y,z;
float a,b,c;
float G,m1,m2;
float timekey;

//The vector would be the return value

float vector[3];
float Result = (-G*m1*m2)/((x-a)^2 + (y-b)^2 + (z-c)^2);

//Compute the unit vector facing (x,y,z) from (a,b,c)

vector[0] = (a-x)/sqrt((a-x)^2 + (b-y)^2 + (c-z)^2);
vector[1] = (b-y)/sqrt((a-x)^2 + (b-y)^2 + (c-z)^2);
vector[2] = (c-z)/sqrt((a-x)^2 + (b-y)^2 + (c-z)^2);

//Multiply the reult into the vector,

//changing it to point towards (a,b,c)

vector[0] = vector[0]*Result;
vector[1] = vector[1]*Result;
vector[2] = vector[2]*Result;

//Scale the vector by the time key

vector[0] = vector[0]*timekey;
vector[1] = vector[1]*timekey;
vector[2] = vector[2]*timekey;


You would computer all of the bodies affecting this mass and add all of them together. They would all be scaled back by their time keys to find the new appropiate position. This is pretty accurate physics approach, with the exception of resistance isn't calculated.

To test this, find G, the mass of the earth the distance of the earth to the moon. Draw a sphere (the correct size of earth) and place a ship at a position where the moon would be. Accelerate(!) and speed in a straight line past the earth, just to the side (use autopilot). You should either crash into earth or curve around it even though you flew straight.

-James





[edited by - natasdm on September 3, 2002 10:14:20 PM]

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
Now that I look at it, it is your code exactly! Funny how that works!

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
quote:
Original post by Mulligan

dForce = G * ( m_dMass * m_pStars->m_dMass ) / dRadius;



should be:

dForce = G * ( m_dMass * m_pStars[i]->m_dMass ) / dRadius^2;


quote:
[i]Original post by Mulligan

m_pStars->m_vDir.x += dForce * ( dx / dRadius );
m_pStars[i]->m_vDir.z += dForce * ( dz / dRadius );
m_pStars[i]->m_vDir.y += dForce * ( dy / dRadius );



should be:

m_pStars[i]->m_vDir.x = dForce * ( dx / dRadius );
m_pStars[i]->m_vDir.z = dForce * ( dz / dRadius );
m_pStars[i]->m_vDir.y = dForce * ( dy / dRadius );



And I think your vector is pointing towards the planet, so G doesn''t have to be a negative value.

Also, don''t forget to scale back with the t value.

-James

Share this post


Link to post
Share on other sites
Whoa, thanks for the massive responses! I'm implementing many of the changes that you all suggested.

quote:
Original post by Anonymous Poster
dForce = G * ( m_dMass * m_pStars->m_dMass ) / dRadius^2;



Ah ha! That explains a lot. I was having to fudge the mass values of the stars to make it work, but now I can plug in the correct (realistic) values for mass and it works like a charm.

About that last comment with the directional vectors, I think I might be right on this one. Here's why:
When it comes time to call Update() again, the star is already moving in a direction. All all gravitational influences just 'nudge' it from the path it was already traveling. Am I wrong? Probably, but its the only thing that works thus far.

Anywho, I changed a bunch of stuff to make it more realistic, but horribly slow...
Here is the code

  
bool CGalaxy::Update()
{
ULDOUBLE G = 0.0000000000667f;
ULDOUBLE dForce;
ULDOUBLE dz, dx, dy;
ULDOUBLE dRadius;

for( int i = 1; i < m_iNumStars; i++ )
{
// Mass of -1 just means the star was sucked into the center

// and shouldnt be computed anymore

if( m_pStars[i]->m_dMass == -1 )
continue;


// Update position

m_pStars[i]->m_vPos.x += m_pStars[i]->m_vDir.x;
m_pStars[i]->m_vPos.z += m_pStars[i]->m_vDir.z;
m_pStars[i]->m_vPos.y += m_pStars[i]->m_vDir.y;

for( int j = 1; j < m_iNumStars; j++ )
{
// Make sure were not looking at the same star twice

if( m_pStars[i] == m_pStars[j] )
continue;

dx = m_pStars[i]->m_vPos.x - m_pStars[j]->m_vPos.x;
dz = m_pStars[i]->m_vPos.z - m_pStars[j]->m_vPos.z;
dy = m_pStars[i]->m_vPos.y - m_pStars[j]->m_vPos.y;

dRadius = sqrt( dx*dx + dz*dz + dy*dy );

// The star has collided with the center of the galaxy

// (called Event Horizon?)

if( dRadius <= m_pStars[0]->m_dRadius )
{
m_pStars[0]->m_dMass += m_pStars[i]->m_dMass;
m_pStars[i]->m_dMass = -1;
m_pStars[i]->m_vPos.x = 0;
m_pStars[i]->m_vPos.y = 0;
m_pStars[i]->m_vPos.z = 0;
continue;
}

dForce = G * ( m_pStars[j]->m_dMass * m_pStars[i]->m_dMass ) / ( dRadius * dRadius );


m_pStars[i]->m_vDir.x += dForce * ( dx / dRadius );
m_pStars[i]->m_vDir.z += dForce * ( dz / dRadius );
m_pStars[i]->m_vDir.y += dForce * ( dy / dRadius );
}
}

return true;
}



I must explain the significance of m_pStars[ 0 ]. Is a star object like the rest, but Is 5,000 lightyears in diameter and super dense. It sits in the middle of the galaxy, and never moves. This is why the loops start at 1 instead of 0.

It renders a galaxy of 10,000 stars at a healthy 1 frame per 93 seconds, but looks beautiful when I combine the screenshots from each frame together.

This brings me to my next question. Are there any simple speed optimizations that can made to make the inner loop run faster? ANY small performance increase would help dramatically.

Once again, you brainiacs have helped me beyone compare.

Oh yea, what tags do I use to put my code into the white code box?


[edited by - Mulligan on September 4, 2002 6:48:36 PM]

[edited by - Mulligan on September 5, 2002 3:35:39 PM]

Share this post


Link to post
Share on other sites
Guest Anonymous Poster

I hate to burst your bubble, but I''m the only one who has replied so far. I really appreciate the cheers though! Turns out when you do take Cal 3, this stuff will become almost second nature.

And about you being right on the second part, you are (sort of). See, I was thinking you would calculate all of influence on an object and add all of the vectors together to get a final direction/magnitude. You could then shrink that vector to your time scale. The way I thought you were calculating it was calculating the velocity and adding it to the old velocity. This would increase the velocity exponentially, and incorrectly. If you zero out that vector before you compute the effects of all the bodies, then you should be very accurate. After computing all of the effects, scale the final vector and add it to your position(s).

As far as speed optimizations go, which graphics API are you using? I know some tricks on boosting performance, such as batching primitives, limiting vertex buffer writes and vertex shaders.

-James

Share this post


Link to post
Share on other sites
I''m using OpenGL, but that code is fast as lightning. The origional update function rendered 10,000 stars at 60fps. I render single points.

Share this post


Link to post
Share on other sites
I did this some time ago, in Turbo Pascal,
in 2D.

Im going to add my 2 cents to the discussion.
The way I did it was:
a-->b = fab
a-->c = fac
a-->d = fad

first, i calculated how stongly a particle wold get attracted
to all the other particles in the simulation (force ab =fab)
Then i would sum all those Fxx together, plus the inertial
value of the particle.

Now I knew where the next position of the particle would be.

This is all fine and works, but if you have 4 particles, you''re
cheking (n-1)*(n), or 3*4 = 12 calculations.

Then something ocurred to me.

After the attraction vector of a--->b gets calculated, and
all the stuff for particle a is calculated, i move on to
particle b, right? It ocurred to me that b--->a == a--->b !!

So, this is one vector you dont need to calculate, in fact all
the vectors you need to calculate, in a 4 particle simulation are:

a-->b
a-->c
a-->d
b-->c
b-->d
c-->d

This totals 6 calculations. Half of the initial value.
Mind you I didn''t invent the wheel. This is an old optimization technique. Hope you got the point, enjoy gravitics!








[Hugo Ferreira][Positronic Dreams][Stick Soldiers]
"Redundant book title: "Windows For Dummies".
"Camouflage condoms: So they won''t see you coming".

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
To put code in the source box, use the source tags:

  [source]  
[/source]

pentium31d is correct, you could cut your calculations down...

Let''s say that we only calculated one pair one per set and eliminated duplicates. That means we start at one and caluclate every star affecting the first star. Then we move on to two, where we calculate the effect on everything except the first planet, which we have already done. Then we do the third planet, neglecting it''s effect from the first two. So:

process remaining stars in list where n is current star:

for(int i = (n+1); i < Num_stars; i++)


That would run through and make sure not to calculate duplicates. The only problem is, that when it gets to the very last star, the star will not move. But if you were sitting on that star, the system would seem accurate because of relativity. Everything would be moving except the one you are on. Now if you were on the first star, the last star would look like it moved, even though it didn''t. A view point from this perspective would seem correct as well.

What do you think?
-James

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
actually, for your code, it would be:

for( int j = (i+1); j < m_iNumStars; j++ ) 

Share this post


Link to post
Share on other sites