I recently started to develop a Windows Phone game with XNA. I have problem as you might have guessed collision detection. After looking up tutorials about all the types that can be achieved I decided I will go for the basic rectangular collision detection. I have a rotating sprite and a method that calculates the bounding box every time in the Update() method so I know where it's bounding box is then I simply check for intersection between all the lines of the box with all the lines of the other sprite's boxes. But since my box is appearing square shaped and my texture of that rotating sprite is Rectangular I wanna scale the bounding box so it will be closer to the texture's size. Here is what I have for calculating the corners of the rotating bounding box:
double baseAngle = Math.Atan(this.Height / this.Width);
double len = Math.Sqrt(this.Height * this.Height / 4 + this.Width * this.Width / 4);
Vector2 tr = new Vector2((float)(Math.Sin(baseAngle + this.Rotation) * len) + this.Position.X, (float)(Math.Cos(baseAngle + this.Rotation) * len) + this.Position.Y);
Vector2 tl = new Vector2((float)(Math.Sin(Math.PI - baseAngle + this.Rotation) * len) + this.Position.X, (float)(Math.Cos(Math.PI - baseAngle + this.Rotation) * len) + this.Position.Y);
Vector2 bl = new Vector2((float)(Math.Sin(Math.PI + baseAngle + this.Rotation) * len) + this.Position.X, (float)(Math.Cos(Math.PI + baseAngle + this.Rotation) * len) + this.Position.Y);
Vector2 br = new Vector2((float)(Math.Sin(2 * Math.PI - baseAngle + this.Rotation) * len) + this.Position.X, (float)(Math.Cos(2 * Math.PI - baseAngle + this.Rotation) * len) + this.Position.Y);`
any help would be appreciated. Thanks
when you scale, it only appears bigger widht and height are same. so bounding box is same as for original. try multypllying height and width with scale number where you calculate bounding box.
and you cannot rotate bounding box, you will have to use matrix.class but you can allways use circle collision.
circle collision
int circlesColliding(int x1, int y1, int radius1, int x2, int y2, int radius2) {
//compare the distance to combined radii
int dx = x2 - x1;
int dy = y2 - y1;
int radii = radius1 + radius2;
if ((dx * dx) + (dy * dy) < radii * radii) {
return true;
} else {
return false;
}
}
Related
I want to calculate the mass, center of mass, and inertia tensor of player created objects. Some of the objects will be hollow instead of solid. I am creating a closed triangle mesh for their object. Note that this is for a physics building game so I want the values to be as accurate as possible (within reason).
Based on the references listed below, I can get the properties for a solid object by creating a tetrahedron from each triangle of the mesh and calculating the signed volume which gives the mass (volume * density) and the center of mass. I even got the inertia tensor calculation working.
How do I do the same for a hollow object?
For the mass and center of mass, I am iterating through the triangles of the mesh and totaling their area and calculating the area-weighted average of their positions, then multiplying the surface area by a "density" value to get the mass.
public static (float, Vector3) CalculateSurfaceArea(Mesh mesh)
{
Vector3[] vertices = mesh.vertices;
int[] triangles = mesh.triangles;
float totalArea = 0f;
Vector3 temp = Vector3.zero;
for (int i = 0; i <= triangles.Length - 3; i += 3)
{
Vector3 v1 = vertices[triangles[i]];
Vector3 v2 = vertices[triangles[i + 1]];
Vector3 v3 = vertices[triangles[i + 2]];
Vector3 edge21 = v2 - v1;
Vector3 edge31 = v3 - v1;
float area = Vector3.Cross(edge21, edge31).magnitude / 2f; // area of the triangle
totalArea += area;
Vector3 triCenter = (v1 + v2 + v3) / 3f; // center of the triangle
temp += triCenter * area;
}
Vector3 areaCenter = temp / totalArea;
return (totalArea, areaCenter);
}
For the inertia tensor, I am trying a similar approach where I iterate through all the triangles and total up their moments of inertia and using the parallel-axis theorem to account for their positions but (a) I am not sure this is correct and (b) how do I calculate the products of inertia (Ixy, Ixz, Iyz)?
public static (Vector3, Quaternion) CalculateHollowInertiaTensor(Mesh mesh)
{
Vector3[] vertices = mesh.vertices;
int[] triangles = mesh.triangles;
double Ixx = 0f;
double Iyy = 0f;
double Izz = 0f;
double Ixy = 0f;
double Ixz = 0f;
double Iyz = 0f;
for (int i = 0; i <= triangles.Length - 3; i += 3)
{
Vector3 v1 = vertices[triangles[i]];
Vector3 v2 = vertices[triangles[i + 1]];
Vector3 v3 = vertices[triangles[i + 2]];
Vector3 edge21 = v2 - v1;
Vector3 edge31 = v3 - v1;
Vector3 center = (v1 + v2 + v3) / 3f; // center of the triangle
Vector3 offset = center - v1;
float area = Vector3.Cross(edge21, edge31).magnitude / 2f; // area of the triangle
// Moment of inertia of triangle rotating around the first vertex
// https://en.wikipedia.org/wiki/List_of_moments_of_inertia
// I = (1/6)m(P.P + P.Q + Q.Q)
float triIxx = (edge21.y * edge21.y + edge21.z * edge21.z + edge21.y * edge31.y + edge21.z * edge31.z + edge31.y * edge31.y + edge31.z * edge31.z) / 6f;
float triIyy = (edge21.x * edge21.x + edge21.z * edge21.z + edge21.x * edge31.x + edge21.z * edge31.z + edge31.x * edge31.x + edge31.z * edge31.z) / 6f;
float triIzz = (edge21.x * edge21.x + edge21.y * edge21.y + edge21.x * edge31.x + edge21.y * edge31.y + edge31.x * edge31.x + edge31.y * edge31.y) / 6f;
// Shift to the center of the triangle
triIxx -= offset.y * offset.y + offset.z * offset.z;
triIyy -= offset.x * offset.x + offset.z * offset.z;
triIzz -= offset.x * offset.x + offset.y * offset.y;
// Shift to the origin (using parallel-axis theorem)
triIxx += center.y * center.y + center.z * center.z;
triIyy += center.x * center.x + center.z * center.z;
triIzz += center.x * center.x + center.y * center.y;
Ixx += triIxx * area;
Iyy += triIyy * area;
Izz += triIzz * area;
//Ixy += area * center.x * center.y;
//Ixz += area * center.x * center.z;
//Iyz += area * center.y * center.z;
}
Matrix<double> inertiaTensor = Matrix<double>.Build.Dense(3, 3);
inertiaTensor[0, 0] = Ixx;
inertiaTensor[1, 1] = Iyy;
inertiaTensor[2, 2] = Izz;
inertiaTensor[0, 1] = inertiaTensor[1, 0] = -Ixy;
inertiaTensor[0, 2] = inertiaTensor[2, 0] = -Ixz;
inertiaTensor[1, 2] = inertiaTensor[2, 1] = -Iyz;
Debug.Log(inertiaTensor);
//Matrix<double> inertiaTensorInverse = inertiaTensor.Inverse();
// Find the principal axes and simplified inertia tensor
Evd<double> evd = inertiaTensor.Evd();
Vector3 inertiaTensorDiagonal = MakeVector3(evd.EigenValues.Real());
Quaternion inertiaTensorRotation = Quaternion.LookRotation(
MakeVector3(evd.EigenVectors.Column(2)),
MakeVector3(evd.EigenVectors.Column(1))
);
return (inertiaTensorDiagonal, inertiaTensorRotation);
}
References I have found so far:
How to calculate the volume of a 3D mesh object the surface of which is made up triangles
How to calculate the volume of a 3D mesh object the surface of which is made up triangles
List of moments of inertia
https://en.wikipedia.org/wiki/List_of_moments_of_inertia
Polyhedral Mass Properties, David Eberly
https://www.geometrictools.com/Documentation/PolyhedralMassProperties.pdf
Explicit Exact Formulas for the 3-D Tetrahedron Inertia Tensor in Terms of its Vertex Coordinates, F. Tonon
http://docsdrive.com/pdfs/sciencepublications/jmssp/2005/8-11.pdf
Efficient Feature Extraction for 2D/3D Objects in Mesh Representation, Cha Zhang and Tsuhan Chen
http://chenlab.ece.cornell.edu/Publication/Cha/icip01_Cha.pdf
===== EDIT =====
Based on John Alexiou's answer, I reimplemented the algorithm for hollow objects:
public static (float, Vector3, Vector3, Quaternion) CalculateHollowMassCenterInertiaTensor(Mesh mesh)
{
Vector3[] vertices = mesh.vertices;
int[] triangles = mesh.triangles;
double Ixx = 0f;
double Iyy = 0f;
double Izz = 0f;
double Ixy = 0f;
double Ixz = 0f;
double Iyz = 0f;
float totalMass = 0f;
Vector3 temp = Vector3.zero;
for (int i = 0; i <= triangles.Length - 3; i += 3)
{
Vector3 v1 = vertices[triangles[i]];
Vector3 v2 = vertices[triangles[i + 1]];
Vector3 v3 = vertices[triangles[i + 2]];
Vector3 center = (v1 + v2 + v3) / 3f; // center of the triangle
float area = (Vector3.Cross(v1, v2) + Vector3.Cross(v2, v3) + Vector3.Cross(v3, v1)).magnitude / 2f; // area of the triangle
totalMass += area;
temp += center * area;
Ixx += area * (v1.y * v1.y + v1.z * v1.z + v2.y * v2.y + v2.z * v2.z + v3.y * v3.y + v3.z * v3.z) / 3f;
Iyy += area * (v1.x * v1.x + v1.z * v1.z + v2.x * v2.x + v2.z * v2.z + v3.x * v3.x + v3.z * v3.z) / 3f;
Izz += area * (v1.x * v1.x + v1.y * v1.y + v2.x * v2.x + v2.y * v2.y + v3.x * v3.x + v3.y * v3.y) / 3f;
Ixy += area * (v1.x * v1.y + v2.x * v2.y + v3.x * v3.y) / 3f;
Ixz += area * (v1.x * v1.z + v2.x * v2.z + v3.x * v3.z) / 3f;
Iyz += area * (v1.y * v1.z + v2.y * v2.z + v3.y * v3.z) / 3f;
}
Vector3 centerOfMass = temp / totalMass;
Matrix<double> inertiaTensor = Matrix<double>.Build.Dense(3, 3);
inertiaTensor[0, 0] = Ixx;
inertiaTensor[1, 1] = Iyy;
inertiaTensor[2, 2] = Izz;
inertiaTensor[0, 1] = inertiaTensor[1, 0] = -Ixy;
inertiaTensor[0, 2] = inertiaTensor[2, 0] = -Ixz;
inertiaTensor[1, 2] = inertiaTensor[2, 1] = -Iyz;
Debug.Log(inertiaTensor);
// Find the principal axes and simplified inertia tensor
Evd<double> evd = inertiaTensor.Evd();
Vector3 inertiaTensorDiagonal = MakeVector3(evd.EigenValues.Real());
Quaternion inertiaTensorRotation = Quaternion.LookRotation(
MakeVector3(evd.EigenVectors.Column(2)),
MakeVector3(evd.EigenVectors.Column(1))
);
return (totalMass, centerOfMass, inertiaTensorDiagonal, inertiaTensorRotation);
}
It is giving me the wrong values for the inertia tensor though.
mass
Expected Inertial Tensor
Actual Inertial Tensor
Cube
6.00
1.6667 1.6667 1.6667
3.0000 3.0000 3.0000
Sphere
3.11
0.5236 0.5236 0.5236
0.5151 0.5162 0.5163
Cylinder
7.80
4.5488 1.7671 4.5488
8.7134 1.8217 8.7134
Cube has 1m sides, Sphere has 0.5m radius, Cylinder has 0.5m radius and 2m height. All are centered around the origin.
Note that I am using the total surface area as the mass. Assume that the thickness * density = 1 so mass = area * thickness * density becomes mass = area * 1. I will be picking better values for those later because I assume that will not have that big of an effect on the algorithm.
Also note that some rounding is to be expected because the shapes are being approximated by a triangular mesh.
For a shell body to have mass and mass moment of inertia, the sides must have some thickness ε>0.
This defines the mass of a triangle defined by the vectors A, B and C and thickness ε and density ρ as
area = 1/2*Math.Abs( Vector3.Cross(A,B) + Vector3.Cross(B,C) + Vector3.Cross(C,A) )
mass = ρ*area*ε
or the above can be used to find the density from the total mass of an object, once the total surface area is calculated.
To find the mass moment of inertia you need to define a function returning the MMOI matrix of a unit mass particle from the location vector r=(x,y,z).
Matrix3 Mmoi(Vector3 r)
{
// | y^2+z^2 -x y -x z |
// I = | -x y x^2+z^2 -y z |
// | -x z -y z x^2+y^2 |
return new Matrix3(
r.y*r.y + r.z*r.z, -r.x*r.y, -r.x*r.y,
-r.x*r.y, r.x*r.x + r.z*r.y, -r.y*r.z,
-r.x*r.z, -r.y*r.z, r.x*r.x + r.y*r.y);
}
and then calculate the MMOI of a triangle from the vertices and the mass
Matrix3 Mmoi(double m, Vector3 A, Vector3 B, Vector3 C)
{
return (m/3)*(Mmoi(A)+Mmoi(B)+Mmoi(C));
}
The above is derived from the surface integral over the triangle, and since [SO] does not support math formatting I am omitting the details here.
Yes the above is true, the MMOI of a surface triangle is that of the average MMOI of the three vertices.
Update
In correlating the above with CAD I realized you have to integrate over both front and back surfaces of the triangle. The result that matches CAD mass properties is
Matrix3 Mmoi(double m, Vector3 A, Vector3 B, Vector3 C)
{
return 2*(m/3)*(Mmoi(A)+Mmoi(B)+Mmoi(C));
}
I have a Big Circle and several small circles around it as seen in picture
First I'm drawing middle small circle like this:
cxSmallMiddle = cxBig + radiusBig + hDist + radiusSmall;
sySmallMiddle = radiusBig;
cxBig is center of Big circle. hDist is the distance I want every small circle to be from big circle.
So this way now middle small circle's middle point is parallel to big circle's.
Now I want to draw next small circle with hDist from big circle and vDist (vertical distance) from middle small circle.
So this way hDist and vDist will control the distance small circles are separated from big circle and gap between small circles accordingly.
how can I find cx and cy for other buttons?
This is a hand drawn finished version
Edit: added a code suggested by #Gene
#Override
public void onDraw(Canvas canvas) {
float radiusBig = 110f * singleDp;
float cxBig = screenWidth / 2f;
//float cyBig = screenHeight / 2f;
float cyBig = radiusBig + strokeWidth + (20*singleDp);
canvas.drawCircle(cxBig, cyBig, radiusBig, paint);
float radiusSmall = 20 * singleDp;
float vDist = 0 * singleDp;
float hDist = 0 * singleDp;
float acPoint = radiusBig;
float bcPoint = radiusSmall + vDist;
float theta = (float) Math.acos(bcPoint / acPoint);
int i = 0;
double x_i = acPoint * Math.cos(i * theta) + cxBig;
double y_i = acPoint * Math.sin(i * theta) + cyBig;
canvas.drawCircle((float) x_i, (float) y_i, radiusSmall, paint);
i = 1;
x_i = acPoint * Math.cos(i * theta) + cxBig;
y_i = acPoint * Math.sin(i * theta) + cyBig;
canvas.drawCircle((float) x_i, (float) y_i, radiusSmall, paint);
}
I experimented a lot with this code and this is what I got. When I draw i=0 is almost 45 degree distance from i=0. While experimenting, I discovered if I specify vDist = 80; then it looks okay. The bigger the vDist the closer it gets to i=0.
This is high school trigonometry. There's a right triangle formed by the big circle center (A), the small circle center (C), and the point (B) on the horizontal radius directly below the small circle center.
The length of edge BC is vDist + 2 * radiusSmall. The length of AC is radiusBig
Let \theta be the angle BAC. Then
sin(\theta) = BC / AC = (vDist + 2 * radiusSmall) / radiusBig.
So you can determine \theta:
\theta = arcsin((vDist + radiusSmall) / radiusBig)
Once you have \theta, the locations of the circles wrt the origin are
x_i = radiusBig * cos(i * \theta)
y_i = radiusBig * sin(i * \theta)
For i = 0, +1, -1, +2, -2, ...
Edit
Okay here is a quick hack in Java Swing. Sorry in the original post I said arccos when I meant arcsin.
import java.awt.Graphics;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Circles extends JPanel {
public static void main(String[] a) {
JFrame f = new JFrame();
f.setSize(800, 800);
f.add(new Circles());
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setVisible(true);
}
#Override
public void paint(Graphics g) {
int cx = 400, cy = 400, rBig = 200, rSmall = 40, hDist = 20, vDist = 10;
drawCircle(g, cx, cy, rBig); // Big circle.
int rSmallCircleCenters = rBig + hDist + rSmall;
double theta = Math.asin(((double) vDist + 2 * rSmall) / rSmallCircleCenters);
int nPairs = 3;
for (int i = 1 - nPairs; i < nPairs; ++i) {
int dx = (int) (rSmallCircleCenters * Math.cos(i * theta));
int dy = (int) (rSmallCircleCenters * Math.sin(i * theta));
drawCircle(g, cx + dx, cy + dy, rSmall);
drawCircle(g, cx - dx, cy - dy, rSmall);
}
}
private void drawCircle(Graphics g, int cx, int cy, int r) {
g.drawOval(cx - r, cy - r, 2 * r, 2 * r);
}
}
Here's what it draws:
I have successfully coded a static spiral using lines, and now I'm supposed to make the spiral rotate from frame to frame. I tried incrementing the angle used for the x and y positions of the end of the lines with each frame, but the spiral doesn't move at all.
void draw() {
for (int i = 0; i < 15 * NUM_LINES; i++) {
float lineEndX = width / 2 + radius * cos(angle + startAngle);
float lineEndY = height / 2 + radius * sin(angle + startAngle);
line (lineStartX, lineStartY, lineEndX, lineEndY);
lineStartX = lineEndX;
lineStartY = lineEndY;
radius = radius + 0.047;
angle += 0.01 % (TWO_PI * NUM_TURNS);
}
startAngle += START_ANGLE_CHANGE;
angle = 0;
}
Add background(255); to your draw function. Also define lineStartX, lineStartY and radius there so their values are reset every time the function is called.
void draw() {
background(255);
float lineEndX = width / 2;
float lineEndY = height / 2;
float radius = 5;
for (int i = 0; i < 15 * NUM_LINES; i++) {
float lineEndX = width / 2 + radius * cos(angle + startAngle);
float lineEndY = height / 2 + radius * sin(angle + startAngle);
line (lineStartX, lineStartY, lineEndX, lineEndY);
lineStartX = lineEndX;
lineStartY = lineEndY;
radius = radius + 0.047;
angle += 0.01 % (TWO_PI * NUM_TURNS);
}
startAngle += START_ANGLE_CHANGE;
angle = 0;
}
Working example here.
I am considering porting an iPhone project from core animation to OpenGL-ES.
I need to render a button that is constructed from CGPathRef s.
But it seems that GL has no provision for Bezier Curves.
Can anyone provide some code that renders a Bezier Curve in GL?
This will accept a series of points to draw a rounded bezier line. It must use point sprites. If you send it a line of three points, and a number of point sprites to draw, it will create a bezeir line. The code is based of something I found somewhere, but I cannot remember where.
It requires:
CGPoint origin - First Point
CGPoint control - Mid Point
CGPoint destination - End Point
int segments - Number of points to render.
To calculate the number of points, I use:
count = MAX(ceilf(sqrtf(([[currentStroke objectAtIndex:i+2] CGPointValue].x - [[currentStroke objectAtIndex:i] CGPointValue].x)
* ([[currentStroke objectAtIndex:i+2] CGPointValue].x - [[currentStroke objectAtIndex:i] CGPointValue].x)
+ ((invertedYThirdCoord - invertedYBegCoord) * (invertedYThirdCoord - invertedYBegCoord))) / 2), 1)*4;
Anyway, the code (in C++):
CGPoint vertices[segments];
CGPoint midPoint;
float x, y;
float t = 0.0;
for(int i = 0; i < (segments); i++)
{
x = pow(1 - t, 2) * origin.x + 2.0 * (1 - t) * t * control.x + t * t * destination.x;
y = pow(1 - t, 2) * origin.y + 2.0 * (1 - t) * t * control.y + t * t * destination.y;
vertices[i] = CGPointMake(x, y);
t += 1.0 / (segments);
}
midPoint = CGPointMake(x, 288 - y);
glVertexPointer(2, GL_FLOAT, 0, vertices);
glDrawArrays(GL_POINTS, 0, segments);
Following this render as normal.
Does anyone have an algorithm for drawing an arrow in the middle of a given line. I have searched for google but haven't found any good implementation.
P.S. I really don't mind the language, but it would be great if it was Java, since it is the language I am using for this.
Thanks in advance.
Here's a function to draw an arrow with its head at a point p. You would set this to the midpoint of your line. dx and dy are the line direction, which is given by (x1 - x0, y1 - y0). This will give an arrow that is scaled to the line length. Normalize this direction if you want the arrow to always be the same size.
private static void DrawArrow(Graphics g, Pen pen, Point p, float dx, float dy)
{
const double cos = 0.866;
const double sin = 0.500;
PointF end1 = new PointF(
(float)(p.X + (dx * cos + dy * -sin)),
(float)(p.Y + (dx * sin + dy * cos)));
PointF end2 = new PointF(
(float)(p.X + (dx * cos + dy * sin)),
(float)(p.Y + (dx * -sin + dy * cos)));
g.DrawLine(pen, p, end1);
g.DrawLine(pen, p, end2);
}
Here's a method to add an arrow head to a line.
You just have to give it the coordinates of your arrow tip and tail.
private static void drawArrow(int tipX, int tailX, int tipY, int tailY, Graphics2D g)
{
int arrowLength = 7; //can be adjusted
int dx = tipX - tailX;
int dy = tipY - tailY;
double theta = Math.atan2(dy, dx);
double rad = Math.toRadians(35); //35 angle, can be adjusted
double x = tipX - arrowLength * Math.cos(theta + rad);
double y = tipY - arrowLength * Math.sin(theta + rad);
double phi2 = Math.toRadians(-35);//-35 angle, can be adjusted
double x2 = tipX - arrowLength * Math.cos(theta + phi2);
double y2 = tipY - arrowLength * Math.sin(theta + phi2);
int[] arrowYs = new int[3];
arrowYs[0] = tipY;
arrowYs[1] = (int) y;
arrowYs[2] = (int) y2;
int[] arrowXs = new int[3];
arrowXs[0] = tipX;
arrowXs[1] = (int) x;
arrowXs[2] = (int) x2;
g.fillPolygon(arrowXs, arrowYs, 3);
}