Reputation: 3495
I'm trying a script in maya to see if i can get gravity and that lot working with spherical objects, and currently it works fine (tried making a moon and earth to scale and calibrated the gravity effect to that), but I'd like to try get it a step further and add collisions to make objects bounce off each other, but after looking around for ages yesterday, I found it's hard enough with 2d objects, never mind 3d.
I've managed to write a bit which can detect a collision, where it gets the distance between both objects and compares it against the radius of both objects, which seems to work (needs rewriting though), but I couldn't figure out what to do next. To be honest, I'm not even sure if it's possible to do with something like this without some advanced university knowledge which is why I eventually gave up :P
Just for an idea of how it works, here's the current stage of the main part - objSel[j] is the current selected object, allObjects is everything but the current selected object
def moveObjects(originalTime,objSel,objMultiplySize):
moveAmounts = []
crashed = False
for j in range(len(objSel)):
#get initial values
originalVelocity = getVelocity(objSel[j],originalTime,objMultiplySize)
objVolume = getVolume(objSel[j],objMultiplySize)
allObjects = selectAllOtherObjects(objSel[j], objSel)
#calculate gravity effect on object
xDist = 0
yDist = 0
zDist = 0
for i in range (0, len(allObjects) ):
attraction = calcuateForce(objSel[j],allObjects[i],objMultiplySize)
distanceFromObj = getDistance(allObjects[i],objSel[j],objMultiplySize)[1]
xDist += distanceFromObj[0] * attraction / (objVolume*2.15*math.pi)
yDist += distanceFromObj[1] * attraction / (objVolume*2.15*math.pi)
zDist += distanceFromObj[2] * attraction / (objVolume*2.15*math.pi)
gravityEffect = [xDist,yDist,zDist]
newX = (originalVelocity[0]+gravityEffect[0])/objMultiplySize
newY = (originalVelocity[1]+gravityEffect[1])/objMultiplySize
newZ = (originalVelocity[2]+gravityEffect[2])/objMultiplySize
newVelocity = [newX,newY,newZ]
moveAmounts.append( newVelocity )
#-----------this whole bit needs rewriting--------
py.currentTime( originalTime + 1, edit = True, update = True)
for j in range(len(moveAmounts)):
#collision detection
allObjects = selectAllOtherObjects(objSel[j], objSel)
originalRadius = getRadius(objSel[j],objMultiplySize)
for i in range (0, len(allObjects) ):
objRadius = getRadius(allObjects[i],objMultiplySize)
objDistance = getDistance(allObjects[i],objSel[j],objMultiplySize)
if objDistance[0] < objRadius + originalRadius:
force1 = moveAmounts[j][0]*objMultiplySize * objRadius
print "Crashed"
crashed = True
if crashed != True:
#move object
py.move( objSel[j], float(moveAmounts[j][0]), float(moveAmounts[j][1]), float(moveAmounts[j][2]), relative = True )
py.setKeyframe( objSel[j], attribute='translateX')
py.setKeyframe( objSel[j], attribute='translateY')
py.setKeyframe( objSel[j], attribute='translateZ')
else:
#dunno what to do here
for i in range (0, len(allObjects) ):
mass1 = getVolume(allObjects[i],objMultiplySize)
velocity1 = getVelocity(allObjects[i],originalTime,objMultiplySize)
mass2 = getVolume(objSel[j],objMultiplySize)
velocity2 = getVelocity(objSel[j],originalTime,objMultiplySize)
m1v1m2v2X = mass1*velocity1[0] + mass2*velocity2[0]
m1v1m2v2Y = mass1*velocity1[1] + mass2*velocity2[1]
m1v1m2v2Z = mass1*velocity1[2] + mass2*velocity2[2]
totalMass = mass1+mass2
Upvotes: 1
Views: 3278
Reputation: 4129
While thomas's collision is a good, simple elastic collision equation, at scales of planetary bodies, there is no such thing as an elastic collision. Or, should I say, the planets themselves don't collide elastically. Instead of thinking of planets as if they were big bouncy balls, a more accurate analogy is thinking of them as if they were big droplets of water. While not completely identical, there is a lot of similarity between water drops and planets in the way they interact when they collide.
A good, quick illustration of the math that will produce that kind of result can be seen here. While that simulation is not specific to celestial bodies, the basic principles behind the 'squishy' collisions still applies.
I also made a version of it for myself with different simulation parameters, complete with tectonic activity and volcanoes, though no universal gravity, (made it lag too much - all I have is a gravity well in the center) so it won't form any large moons. You can download it to examine/play with here, if you want (you can use the mouse to grab chunks of the planet to throw at it; g turns the gravity well on/off). Keep in mind that those "comets" orbiting the planet that forms in the center are huge. If the planet was the size of earth, those comets would be around the size of texas at least.
While both of the simulations I linked to were written in java, the same math and physics can be applied in any language.
Upvotes: 1
Reputation: 27575
First of all, it seems more like a math problem than a python problem, since the same rationale would work for any language.
Second, if all your objects are spherical, then you doesn't need to actually detect a TRUE COLLISION, but actually just to check if the distance between object centers is smaller than the sum of their radius. You can get quickly this distance (if your space is euclidean) with this function (which I havent seen defined in your code):
def get_distance(obj1, obj2):
# euclidean distance in three dimensions:
return ( (obj2.x-obj2.x)**2 + (obj2.y-obj2.y)**2 + (obj2.z-obj2.z)**2 ) ** 0.5
Now, from what I have understood, you want to BOUNCE the objects. This requires, by definition, that for any moment the object has a VELOCITY property, which is a vector with direction and magnitude. When you update the object position, you must also update its velocity (most probably from its previous position).
Finally, when you bounce you have to consider the object position, its velocity vector, and the normalized vector which points to the center of the other object it is colliding with. Then, you would perform some vector arithmetics to get its new velocity vector.
(UPDATE): if the objects have different masses, and you want a realistic bouncing, then use the Ellastic Collision concepts in Thomas' answer!
So, as further research "directions", I'd suggest:
Hope this helps!
Upvotes: 0
Reputation: 7919
Referencing http://en.wikipedia.org/wiki/Elastic_collision :
for i in range (0, len(allObjects) ):
mass1 = getVolume(allObjects[i],objMultiplySize)
velocity1 = getVelocity(allObjects[i],originalTime,objMultiplySize)
mass2 = getVolume(objSel[j],objMultiplySize)
velocity2 = getVelocity(objSel[j],originalTime,objMultiplySize)
v1new = velocity1; //just initialization
v2new = velocity2; //just initialization
v1new[0] = (velocity1[0] *( mass1-mass2) + 2*mass2*velocity2[0])/(mass1 + mass2);
v2new[0] = (velocity2[0] *( mass2-mass1) + 2*mass1*velocity1[0])/(mass1 + mass2);
v1new[1] = (velocity1[1] *( mass1-mass2) + 2*mass2*velocity2[1])/(mass1 + mass2);
v2new[1] = (velocity2[1] *( mass2-mass1) + 2*mass1*velocity1[1])/(mass1 + mass2);
v1new[2] = (velocity1[2] *( mass1-mass2) + 2*mass2*velocity2[2])/(mass1 + mass2);
v2new[2] = (velocity2[2] *( mass2-mass1) + 2*mass1*velocity1[2])/(mass1 + mass2);
Assuming elastic collision, you can do collision calculations for each dimension separately.
Upvotes: 2