Particle (cloth) to sphere collision - algorithm

What is the proper way to add a sphere constraint to a cloth sim?
I am trying to add a sphere (or capsule) constraing to Skeel Lee's cloth simulation source code, but I am not sure how to do it properly.
I created a rather simple constraint which "kicks" the particle back out of the sphere in the opposite direction (opposite from the vector towards the center):
void SatisfySphereConstraints()
{
foreach (var simObj in this.simObjects)
simObj.CurrPosition += SphereConstraint(simObj.CurrPosition, _center, _radius);
}
Vector3 SphereConstraint(Vector3 position, Vector3 center, float radius)
{
var delta = position - center;
var distance = delta.Length();
if (distance < radius)
return (radius - distance) * delta / distance;
return Vector3.Zero;
}
And then I inserted the method in the existing code:
ApplyForces();
Integrate();
for (var i = 0; i < constraintIterations; i++)
{
foreach (Constraint constraint in constraints)
constraint.SatisfyConstraint();
SatisfySphereConstraints(); // <-- I added it here
}
The collision code works fairly well for situations like this (C is the center of the sphere, P is the current particle position, P' is the resolved position):
But the problem occurs if particles are moving very quickly, because then the particle basically jumps to the other side of the sphere (P1 is the previous position, P2 is the current position, P' is how I think it should be resolved), instead of returning back to the previous position:
Since this is a cloth simulation, the cloth basically jumps over the sphere in that case, instead of being "stopped" by the sphere.
Now, I could try to return in the direction of the previous point, but since the sphere might also be moving, I am not sure if P1 is even a valid position (and if it will make sense). Also, it seems to be more computationally expensive - is this how I am supposed to do it, or not?

Cloth like things snapping to the wrong side of an obstacle and getting stuck there is not too uncommon. Even more common is fast moving objects overlapping way too much when the collision is detected.
A common solution is, on detecting a collision, sub divide the previous step until the collision is less severe and then resolve it. I think you will find trying to detect how deep the collision is to be difficult in your case, but if you could limit the top speed of spheres in your system you could binary split the frames in which collisions occur a fixed number of times and assume it will be good enough?
if (it collides at time T and didn't at T-1)
if (it collides at T-0.5)
try T-0.75
else
try T-0.25
etc...
Are you prepared to accept that it will sometimes be wrong, or does it have to be always a good result?

Related

Collision detection on enemy with wall when there is none

I am trying to develop basic enemy AI on a simple platformer game after following Shaun Spalding's gamemaker 2 platformer tutorials on youtube. My code is the exact same as his on the tutorial but for some reason, when my enemy detects collision with the wall he turns around as he is suppose to and then detects another collision where there is none, causing him to turn around again.
This is my code:
// Horizontal collision
if (place_meeting(x+hsp, y, oWall)) {
show_debug_message(hsp)
while (!place_meeting(x+sign(hsp), y, oWall)) {
x += sign(hsp); // slows down I think
}
hsp = -hsp;
}
x += hsp;
The -hsp part is where he turns around. Somehow, he is detecting another collision as soon as he does so, even though the value of hsp is inverted. Can anyone possibly point me in the direction of why this may be occuring?
(Value of hsp initialized at 3 and never changed only for inversion).
Is it turning back to the wall after a short while, or is it stuck and is flickering to left and right rapidly? Both could involve that the collision point isn't updating well.
When I face with collision problems, I'll use a crosshair sprite, and draw it at the same position as where it should be colliding. that way I've a visible view of the 'collision point'.
Another cause could be the sprite's origin point, that determines at which position the x and y appears, and that the sprite by turning collides with the wall itself. Keep in mind that the origin point is at the center of it's collision mask, to avoid been stuck in a wall.
EDIT: Another possibility: the collision point still checks inside the sprite.
For that, you could also try using an offset that keeps the collision point away from the sprite collision, but to let that work, you'll need to keep the inverse direction away from your horizontal speed. Something like this:
// Horizontal collision
_offset = 15; //moves the collision point away to check in front of the sprite. value depends on the size of the sprite.
_dir = 1; //the direction, should only be 1 or -1
//hsp should no longer be used to inverse, use a new variable (like _dir) instead
collisionPoint = (hsp + offset) * _dir;
if (place_meeting(x + collisionPoint , y, oWall)) {
show_debug_message(collisionPoint)
while (!place_meeting(x+sign(collisionPoint), y, oWall)) {
x += sign(collisionPoint); // slows down I think
}
_dir = -_dir
}
x += hsp * _dir;

My mesh flips for a rotation smaller than math.pi

I am coming back since I am having this geometric problem that I am not familiar with on Unity.
For a f-zero style game, I have a collider box (white on the screen captures) which is the origin of my raycast, and is bound to the movement of the vehicle.
In the shown code, this is this.collider. I control its rotation via a traditional applymatrix and there is no problem.
Then, on top of that, I have the rendered body of the vehicle in this.meshes. It inherits the rotation of the collider box, but gets some extra rotation on its vertical axis to give a visual sliding dynamic during the hard turns.
It is separate from the collider to keep the vector.forward of the movement (and the raycast) not affected by the extra-rotation. This is purely visual.
My question is: what is the best way to implement it?
I tried different things, but, basically, if I copy the position and rotation of the collider, no problem. As soon as I try to add some extra rotation = this.driftRotation, my body flips when rotation.y value is less than -math.pi. I can adjust the value of the rotation by incrementing Math.PI (like in Unity), but it doesn't work here.
No clean solution found with applyMatrix neither, and not a lot of google answers on "vertical rotation flip mesh"... though I'm pretty sure this pissue is common.
Some code:
this.meshes.position.set(
this.collider.position.x,
this.collider.position.y,
this.collider.position.z);
this.meshes.rotation.x = this.collider.rotation.x;
this.meshes.rotation.y = this.collider.rotation.y + this.driftRotation;
this.meshes.rotation.z = this.collider.rotation.z;
Enclosed more explicit pictures:
Thank you
Marquizzo, that's precisely the point: the 3rd px follows the 2nd one, so I'm still turning right but rotation suddenly flips (again, when rotation.y reaches -PI).
Anyway, I fixed it by not trying to directly change rotation.y value, but playing with matrix. Just takes time to understand what does what.
For those who may face a similar pb, here is my temp solution, until I find sthing more performant:
this.meshes.matrix.identity();
if (Math.abs(driftAmount) > 0)
{
this.driftAxis.copy(this.driftDirection);
this.driftValue = js.Utils.lerp(this.driftValue, Math.sign(driftAmount) * 0.4, 0.05);
this.meshes.matrix.makeRotationAxis(this.driftAxis, this.driftValue);
}
else if (Math.abs(this.driftValue) > 0)
{
this.driftAxis.copy(this.driftDirection);
this.driftValue = js.Utils.lerp(this.driftValue, 0, 0.1);
if (Math.abs(this.driftValue) < 0.001)
{
this.driftValue = 0;
}
this.meshes.matrix.makeRotationAxis(this.driftAxis, this.driftValue);
}
this.meshes.applyMatrix(this.collider.matrix);
I had to add a driftAxis along a driftDrection, which is my axis for my vertical rotation.
For ref. I think this subject is +/- bound to the issue I had:
https://github.com/mrdoob/three.js/issues/1460
Now I have another issue, how to add another rotation to this.meshes on another axis, the forward one, for a rolling effect, because if I just add another makeRotationAxis in this code it just skips the first one. But that sounds less difficult to figure out, there must exist the equivalent of combineMatrix something...

Few objects moving straight one by one in Unity

I'am trying to create some snake-like movement, but i cant implement algorithm to move one body part straight by another and so on.
I wanna to have some auto-moved snake which consists of separate blocks ( spheres ). This snake should move along some path. I generate path with bezier spline and have already implemented one future snake's part along it. Point for head is obtained from spline by next api:
class BezierSpline
{
Vector3 GetPoint(float progress) // 0 to 1
}
And than I have SnakeMovement script
public class SnakeMovement : MonoBehaviour
{
public BezierSpline Path;
public List<Transform> Parts;
public float minDistance = 0.25f;
public float speed = 1;
//.....
void Update()
{
Vector3 position = Path.GetPoint(progress);
Parts.First().localPosition = position;
Parts.First().LookAt(position + Path.GetDirection(progress));
for (int i = 1; i < Parts.Count; i++)
{
Transform curBody = Parts[i];
Transform prevBody = Parts[i - 1];
float dist = Vector3.Distance(prevBody.position, curBody.position);
Vector3 newP = prevBody.position;
newP.y = Parts[0].position.y;
float t = Time.deltaTime * dist / minDistance * curspeed;
curBody.position = Vector3.Slerp(curBody.position, newP, t);
curBody.rotation = Quaternion.Slerp(curBody.rotation, prevBody.rotation, t);
}
//....
}
For now, if I stopped head movement all parts dont preserve distance and keep moving to the head position. Another problem with above algorithm is that parts don't exectly follow the head path. They can "cut" corners while turning.
The main idea is to have user/ai control for only head(first body part) and each followed part should exectly repeat head path and preserve distance between its neighbours.
For a snake like motion you are likely to get lots of strange behaviours if you treat spheres as seperate objects. While i can imagine its possible to get it to work, I think this is not the best approach.
First solution that comes to mind is to create a List, onto which you would add to index 0, on every frame, the position of the head of the snake.
The list would grow, and all the other segments would wait their turn, so lag x frames, and on each update segment y would have position of list[x*y]
If Count() of the list is greater than number_of_segments*lag, you RemoveAt(Count()-1)
This can be optimized as changing the list is somewhat costly (a ring buffer would be better suited, but a Queue could also work. For starters i find Lists much easier to follow and you can always optimize later). This may behave a bit awkward if your framerate varies a lot but should be very stable in general (as in - no unpredictable motion, we only re-use the same values over and over)
Second method:
You mentioned using a bezier spline to generate a path. beziers are parametrized by a float t so you have something like
SplineAt(t).
if you take your bezier_path_length and distance_between_segments, than segment n should have position of
SplineAt(t-n*distance_between_segments/bezier_path_length)

Spawn particle at edge of screen

I've searched far and wide, so if there's a similar question please forgive me but I just couldn't find it.
To put what I'm trying to do in context: I want to create an infinitely-generated field of stars that disappear as they go offscreen and reappear at the edge of the screen where the camera is moving. I'm working with a top-down view, so it must be pretty simple to achieve this, but alas I haven't a clue.
I'm using the following code to determine whether a star has gone off-screen and then replace it:
//update camera frustum
camera.projScreenMatrix.multiplyMatrices(
camera.projectionMatrix,
camera.matrixWorldInverse
);
camera.frustum.setFromMatrix(camera.projScreenMatrix);
//loop through stars
var stars=scene.stars.geometry.vertices;
for(var i=0;i<stars.length;i++) {
if(!camera.frustum.containsPoint(stars[i])) {
stars[i]=new THREE.Vector3(
// fill in the blank
);
scene.stars.geometry.verticesNeedUpdate=true;
}
}
Since I'm using a perspective camera, I know I'll need to somehow factor in camera.fov and other perspective elements, but as you can tell I'm no expert on the third dimension.
Assuming I have an angle or normalized vector telling me the direction the view is panning, how would I go about creating a vertex along the edge of the screen regardless of its Z position?
If I'm not clear enough, I'll be happy to clarify. Thanks.
I know this is an old question, but I came across it while looking for an answer and found a simple, trigonometry reliant method to get the left edge of the camera frustum, and I'm sharing it in case someone else might find it useful:
// Get half of the cameras field of view angle in radians
var fov = camera.fov / 180 * Math.PI / 2;
// Get the adjacent to calculate the opposite
// This assumes you are looking at the scene
var adjacent = camera.position.distanceTo( scene.position );
// Use trig to get the leftmost point (tangent = o / a)
var left = Math.tan( fov ) * adjacent * camera.aspect;
Basically, this gets the leftmost point, but if you don't multiply by the aspect ratio you should get a point in a circle around your camera frustum, so you could translate a point any direction away from the cameras focus and it would always be outside the frustum.
It works by assuming that the imaginary plane that is the camera is perpendicular to the line connecting the camera and its focus, so there is a straight angle. This should work if you want objects further away as well (so if you want them at a further point from the camera you just need to increase the distance between the focus and the camera).
Well, countless headaches and another question later, I've come up with a fairly makeshift answer. Just in case by some unlikely chance someone else has the same question, the following function plots a point on the scene relative to the camera's current view with whatever Z specified:
//only needs to be defined once
var projector=new THREE.Projector();
//input THREE.Vector3
function(vector) {
var z=vector.z;
vector.z=0;
projector.unprojectVector(vector,camera);
return camera.position.clone().add(
vector
.sub(camera.position)
.normalize()
.multiplyScalar(
-(camera.position.z-z)/vector.z
)
);
The x and y, in this case, both range from -1 to 1 for bottom-left to top-right. You can use position/window.Width and position/window.Height for extra precision (using mouse coordinates or what have you).

Collision detection problem (intersection with plane)

I'm doing a scene using openGL (a house). I want to do some collision detection, mainly with the walls in the house.
I have tried the following code:
// a plane is represented with a normal and a position in space
Vector planeNor(0,0,1);
Vector position(0,0,-10);
Plane p(planeNor,position);
Vector vel(0,0,-1);
double lamda; // this is the intersection point
Vector pNormal; // the normal of the intersection
// this method is from Nehe's Lesson 30
coll= p.TestIntersionPlane(vel,Z,lamda,pNormal);
glPushMatrix();
glBegin(GL_QUADS);
if(coll)
glColor3f(1,0,0);
else
glColor3f(1,1,1);
glVertex3d(0,0,-10);
glVertex3d(3,0,-10);
glVertex3d(3,3,-10);
glVertex3d(0,3,-10);
glEnd();
glPopMatrix();
Nehe's method:
#define EPSILON 1.0e-8
#define ZERO EPSILON
bool Plane::TestIntersionPlane(const Vector3 & position,const Vector3 & direction, double& lamda, Vector3 & pNormal)
{
double DotProduct=direction.scalarProduct(normal); // Dot Product Between Plane Normal And Ray Direction
double l2;
// Determine If Ray Parallel To Plane
if ((DotProduct<ZERO)&&(DotProduct>-ZERO))
return false;
l2=(normal.scalarProduct(position))/DotProduct; // Find Distance To Collision Point
if (l2<-ZERO) // Test If Collision Behind Start
return false;
pNormal= normal;
lamda=l2;
return true;
}
Z is initially (0,0,0) and every time I move the camera towards the plane, I reduce its z component by 0.1 (i.e. Z.z-=0.1 ).
I know that the problem is with the vel vector, but I can't figure out what the right value should be. Can anyone please help me?
You're passing "vel" (which I suppose is velocity of the moving thing) as "Position", and Z (which I suppose is position) as "Direction".
Your calculation of "Distance to Collision Point" makes no sense. It doesn't take position of the plane into account at all (or maybe it does, if the variables are misnamed).
You define pNormal, but I can't see any use for it. Is it supposed to mean something else?
It's almost impossible to get something like this working without understanding the math. Try a simpler version of the test, maybe assuming a z=0 plane and +z-axis movement, get that working and then take another look at the general case.
Thank you for your help.
I looked into the code again and I changed the collision detection method into the following:
//startPoint: the ray's starting point.
//EndPoint: the ray's ending point.
//lamda: the intersection point.
bool Plane::TestIntersionPlane(const Vector3& startPoint,const Vector3& Endpoint, double& lamda)
{
double cosAlpha=Endpoint.scalarProduct(normal); // calculates the angle between the plane's normal and the ray vector.
// Determine If Ray Parallel To Plane
if ((cosAlpha<ZERO)&&(cosAlpha>-ZERO))
return false;
// delta D is the plane's distance from the origin minus the ray's distance from the origin.
double deltaD = distance - startPoint.scalarProduct(normal); //distance is a double representing the plane's distance from the origin.
lamda= deltaD/cosAlpha;// distance between the plane and the vector
// if the distance between the ray and the plane is greater than zero then they haven't intersected.
if(lamda > ZERO)
return false;
return true;
}
This seems to work with all planes except when the ray is too far from the plane. For example if the plane is at z=-10 and the ray's starting point is: 0,0,3 and it's ending point is 0,0,2 then this is detected as a collision but when I move the ray to start(0,0,2) and end(0,0,1) it's not detected as a collision.
The math seems correct to me, so I'm not sure how to handle this.

Resources