I tried to implement collision between models in my LWJGL game and it seems that the objects are in constant collision, even when the collision radius is just 0.I have put the code for the collision below, as well as a link to a source that I was using to help with the bounding sphere collision.
package model;
import org.lwjgl.util.vector.Vector3f;
public class BoundingSphere {
private Vector3f mid = new Vector3f();
private float radius;
public BoundingSphere(Vector3f midpoint, float radius) {
this.mid = midpoint;
this.radius = radius;
}
public boolean isColliding(BoundingSphere other){
float diffX = (other.mid.x - mid.x);
float diffY = (other.mid.y - mid.y);
float diffZ = (other.mid.z - mid.z);
float diffXSquared = (float) Math.pow(diffX, 2);
float diffYSquared = (float) Math.pow(diffY, 2);
float diffZSquared = (float) Math.pow(diffZ, 2);
float radiusSums = (other.radius + radius);
float radiusSumsSquared = (float)Math.pow(radiusSums, 2);
if (diffXSquared + diffYSquared + diffZSquared > radiusSumsSquared){
return true;
}
else{
return false;
}
}
}
Collision Detection Page
It appears that you have inverted the condition. It is colliding only if:
((x2 + y2 + z2) <= r2)
If you want overlap instead of collision then "<=" will be "<"
Related
I am currently working on a project using 3d simplex noise and the marching cubes algorithm in order to procedurally generated terrain. I am trying to implement collision detection between the player object and terrain mesh but I have no clue how to start. I have read some articles and posts about using JBullet and other libraries but they do not support complex meshes such as the ones generated by simplex noise. In order to simplify things for myself I decided to make it so that the player can only move in the direction I point meaning that I would only need to check if a singular point on the player is intersecting with the terrain. Are there any methods to implement such a process? (edit: I've already looked into barycentric coordinates but I have no idea how to implement into the game)
Current Player Code
package Entities;
import org.lwjgl.glfw.GLFW;
import Engine.Input;
import Maths.Vector3f;
import Models.TexturedModel;
public class Player extends Entity {
public float xspeed = 0,zspeed = 0, yspeed = 0;
public Vector3f mousePos;
public float yrotation = 0, zrotation = 0;
public float maxYRotation = 75f;
private double lastMousePosX = 600 , newMousePosX;
private double lastMousePosY = 500 , newMousePosY;
private float speed = 3;
public Player(TexturedModel model, Vector3f position, float rotX, float rotY, float rotZ, float scale) {
super(model, position, rotX, rotY, rotZ, scale);
}
public void move(){
checkInput();
System.out.println("x ="+this.position.x+" y ="+this.position.y+" z ="+this.position.z);
checkCollision();
}
public boolean checkCollision(){
if(terrain != null){
for(int i = 0; i<terrain.getVertices().length; i+=9){
Vector3f vertex1 = new Vector3f(terrain.getVertices()[i],terrain.getVertices()[i+1],terrain.getVertices()[i+2]);
Vector3f vertex2 = new Vector3f(terrain.getVertices()[i+3],terrain.getVertices()[i+4],terrain.getVertices()[i+5]);
Vector3f vertex3 = new Vector3f(terrain.getVertices()[i+6],terrain.getVertices()[i+7],terrain.getVertices()[i+8]);
//Check if point p is interseting triangle (vertex1, vertex2, vertex3)
if(someCalculationFunction(position, vertex1, vertex2, vertex3){
return true;
}
}
}
return false;
}
public void checkInput(){
newMousePosX = Input.getMouseX();
newMousePosY = Input.getMouseY();
float dx = (float)(newMousePosX-lastMousePosX)*0.07f;
float dy = (float)(newMousePosY-lastMousePosY)*0.07f;
if(!Input.isMouseDown(GLFW.GLFW_MOUSE_BUTTON_1)){
this.rotY -= dx/2;
this.rotX -= dy*0.8f;
}
if(Math.abs(rotX) > 50){
this.rotX = Math.abs(rotX)/rotX*50;
}
if(this.rotY<0){
this.rotY = 360;
}
float horizontalDistance = speed*(float)(Math.cos(Math.toRadians(rotX)));
float verticleDistance = speed*(float)(Math.sin(Math.toRadians(rotX)));
if(Input.isKeyDown(GLFW.GLFW_KEY_W)){
this.position.x += horizontalDistance*Math.sin(Math.toRadians(-rotY));
this.position.z -= horizontalDistance*Math.cos(Math.toRadians(-rotY));
this.position.y += verticleDistance;
}else if(Input.isKeyDown(GLFW.GLFW_KEY_S)){
this.position.x -= horizontalDistance*Math.sin(Math.toRadians(-rotY));
this.position.z += horizontalDistance*Math.cos(Math.toRadians(-rotY));
this.position.y -= verticleDistance;
}
lastMousePosX = newMousePosX;
lastMousePosY = newMousePosY;
}
}
I'm not positive if I understood the question right, but this answer will address the problem of ensuring the players height is that of the terrain that it is standing on
With barycentric coordinates you can calculate what the players height is supposed to be by using the heights of the three vertices that make up that triangle:
public static float baryCentric(Vector3f p1, Vector3f p2, Vector3f p3, Vector2f pos) {
float det = (p2.z - p3.z) * (p1.x - p3.x) + (p3.x - p2.x) * (p1.z - p3.z);
float l1 = ((p2.z - p3.z) * (pos.x - p3.x) + (p3.x - p2.x) * (pos.y - p3.z)) / det;
float l2 = ((p3.z - p1.z) * (pos.x - p3.x) + (p1.x - p3.x) * (pos.y - p3.z)) / det;
float l3 = 1.0f - l1 - l2;
return l1 * p1.y + l2 * p2.y + l3 * p3.y;
}
In order to get these three points you can make a calculation using the world coordinates of your player:
//Assuming the world is constructed of equal height and width sized triangles
float gridSquareSize = SIZE_OF_TERRAIN_MESH / NUM_TRIANGLES_PER_ROW;
float xCoord = worldX % gridSquareSize / gridSquareSize;
float zCoord = worldZ % gridSquareSize / gridSquareSize;
With the xCoord and zCoord you can determine the 3 points that you need to use for your baryCentric calculation
I am unable to create several instances of the waveClock object even though I have put it in an array and marked the centre positions for each object. I would like to create 4 objects in one window, all responding to different sound frequencies/beat onsets etc
Could someone shed some light on how to go about this? I believe it may be an issue with the centerX and centerY variables in the waveClock class
ArrayList<waveClock> waveClocks = new ArrayList<waveClock>();
//global variables
float angnoise, radiusnoise;
float xnoise, ynoise;
float angle = -PI/6;
float radius;
float strokeCol = 254;
int strokeChange = -1;
int speed; //changes speed of visualisation once beat is detected?
void setup()
//for every waveClock we need 180 pixels width, then add 20 pixels for first gap
size(740, 650);
background(255);
//code is called
waveClocks.add(new waveClock(100, height/2, minRadius, bassColour, lowBassBand, highBassBand, numberOfLowOnsetsThreshold));
waveClocks.add(new waveClock(280, height/2, minRadius, midColour, lowMidBand, highMidBand, numberOfMidOnsetsThreshold));
waveClocks.add(new waveClock(460, height/2, minRadius, highColour, lowHighBand, highHighBand, numberOfHighOnsetsThreshold));
waveClocks.add(new waveClock(640, height/2, minRadius, veryHighColour, lowVeryHighBand, highVeryHighBand, numberOfVeryHighOnsetsThreshold));
//set the min and max radius of each of the viz circles
/* for (int i = 0; i < waveClocks.size(); i++) {
//go through the arraylist of waveClocks and set the min and max radius of each circle
waveClocks.get(i).setMinMaxRadius(minRadius, maxRadius);
}*/
song.play();
beat = new BeatDetect(song.bufferSize(), song.sampleRate());
bl = new BeatListener(beat, song);
}
void draw() {
//clear the screen by painting it black
//background(0);
for (int i = 0; i < waveClocks.size(); i++) {
//has there been a beat in the range? get(circle ID).low band, high band etc.
if (beat.isRange(waveClocks.get(i).getLowBand(), waveClocks.get(i).getHighBand(), waveClocks.get(i).getOnsetThreshold())) {
waveClocks.get(i).setMaxRadius();
}
//waveClocks.get(i).drawCircle();
waveClocks.get(i).drawWaveClock();
}
}
waveClock class in a separate tab
//class is an architecture blueprint
//objects are the actual buildings built from the methods (can make as many as you like)
//constructor is the builder/constructor literally
class waveClock {
float centerX; //co-ordinates of circle's position
float centerY; //co-ordinates of circle's position
float radius; //avg radius
// float minRadius; //smallest size it can be
// float maxRadius; //biggest size it can be
color col; //colour
int onsetThreshold; //
int lowBand; //looks at lowest band of frequency and makes circle sensitive to it
int highBand; //looks at highest band of frequency and makes circle sensitive to it
boolean onset; //has there been an onset (beat has occurred or not?)
//the constructor
waveClock(float x, float y, float r, color c, int lb, int hb, int t) {
centerX = x;
centerY = y;
radius = r;
col = c;
lowBand = lb;
highBand = hb;
onsetThreshold = t;
}
void drawWaveClock() {
radiusnoise += 0.005;
radius = (noise(radiusnoise)*350) + 1;
angnoise += 0.005;
angle += (noise(angnoise)*6) - 3;
if (angle > 360) {
angle -= 360;
} else if (angle < 0) {
angle += 360;
}
xnoise += 0.01;
ynoise =+ 0.01;
float centerX = width/2 + (noise(xnoise)*100) - 50;
float centerY = height/2 + (noise(ynoise)*100) - 50;
float rad = radians(angle);
float x1 = centerX + (radius*cos(rad));
float y1 = centerY + (radius*sin(rad));
float opprad = rad + PI;
float x2 = centerX + (radius*cos(opprad));
float y2 = centerY + (radius*sin(opprad));
strokeCol += strokeChange;
if (strokeCol > 354) {
strokeChange = -1;
} else if (strokeCol < 0) {
strokeChange = 1;
}
stroke(strokeCol, 60);
strokeWeight(1);
line(x1, y1, x2, y2);
}
}
You aren't ever using the class-level centerX and centerY variables. Instead, you're recalculating a new centerX and centerY in the drawWaveClock() function.
float centerX = width/2 + (noise(xnoise)*100) - 50;
float centerY = height/2 + (noise(ynoise)*100) - 50;
These are all drawn from the center of the screen, so the waves will end up in the same position.
In the future, please try to narrow your problem down to a MCVE that demonstrates the problem. Also please use proper naming conventions- classes start with an upper-case letter, for example. Good luck.
Currently I have an ArrayList of vertices in a 3-dimensional cartesian coordinates system. The polygon is random. It can be a car, a cup or even a dragon.
Assuming the density does not change, how to calculate the centre of mass (x,y,z) of this 3D object?
I am storing the faces and vertices in ArrayList.
public ArrayList<stlFace> StlFaces = new ArrayList<stlFace>();
public ArrayList<VertexGeometric> VertexList = new ArrayList<VertexGeometric>();
I was using this for calculating surface which is proportional to mass of each face or triangle. And to calculate center off mass of each triangle and center of mass of whole object I was using this. I added helper methods getCenter() and getSurface() to Face class to encapsulate calculations specific to just one face/triangle.
public static class Vertex {
public float x = 0;
public float y = 0;
public float z = 0;
public Vertex(float x, float y, float z) {
this.x = x;
this.y = y;
this.z = z;
}
}
public static class Face {
public Vertex v1;
public Vertex v2;
public Vertex v3;
public Face(Vertex v1, Vertex v2, Vertex v3) {
this.v1 = v1;
this.v2 = v2;
this.v3 = v3;
}
public Vertex getCenter() {
Vertex triangleCenter = new Vertex(0, 0, 0);
triangleCenter.x += v1.x;
triangleCenter.x += v2.x;
triangleCenter.x += v3.x;
triangleCenter.y += v1.y;
triangleCenter.y += v2.y;
triangleCenter.y += v3.y;
triangleCenter.z += v1.z;
triangleCenter.z += v2.z;
triangleCenter.z += v3.z;
triangleCenter.x /= 3;
triangleCenter.y /= 3;
triangleCenter.z /= 3;
return triangleCenter;
}
public float getSurface() {
float x1 = v1.x - v2.x;
float x2 = v1.y - v2.y;
float x3 = v1.z - v2.z;
float y1 = v1.x - v3.x;
float y2 = v1.y - v3.y;
float y3 = v1.z - v3.z;
return (float) Math.sqrt(
Math.pow(x2 * y3 - x3 * y2, 2) +
Math.pow(x3 * y1 - x1 * y3, 2) +
Math.pow(x1 * y2 - x2 * y1, 2)
) / 2f;
}
}
public static Vertex calculateMassCenter(List<Face> faces) {
Vertex massCenter = new Vertex(0, 0, 0);
float mass = 0;
for (Face face : faces) {
Vertex triangleCenter = face.getCenter();
float faceMass = face.getSurface();
mass += faceMass;
massCenter.x += faceMass * triangleCenter.x;
massCenter.y += faceMass * triangleCenter.y;
massCenter.z += faceMass * triangleCenter.z;
}
massCenter.x /= mass;
massCenter.y /= mass;
massCenter.z /= mass;
return massCenter;
}
I am creating a custom view which is a kind of arc slider progress view.I can draw more or less of the arc based on where the user touches(on the x axis) by calculating the sweep, i do this by first calculating the percetage where the user touched along the x axis..0% would be all the way to the left and 100% would be all the way to the right.
I want to take this a step further, instead off drawing the arc based on the x coordinate that the user presses, I want to make it move only when the user touches on the actual arc draw path, so its more realistic. I am still new to custom views and my maths is limited but if I get some tips I would be grateful thanks
class ArcProgress extends View {
Context cx;
float width;
float height;
float center_x, center_y;
final RectF oval = new RectF();
final RectF touchArea = new RectF();
float sweep = 0;
float left, right;
int percent = 0;
public ArcProgress(Context context) {
super(context);
cx = context;
}
public int getPercentage() {
return percent;
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
setBackgroundColor(0xfff0ebde);
width = (float) getWidth();
height = (float) getHeight();
float radius;
if (width > height) {
radius = height / 3;
} else {
radius = width / 3;
}
Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setColor(0xffd2c8b6);
paint.setStrokeWidth(35);
paint.setStyle(Paint.Style.STROKE);
center_x = width / 2;
center_y = height / 2;
left = center_x - radius;
float top = center_y - radius;
right = center_x + radius;
float bottom = center_y + radius;
oval.set(left, top, right, bottom);
//this is the background arc, it remains constant
canvas.drawArc(oval, 180, 180, false, paint);
paint.setStrokeWidth(10);
paint.setColor(0xffe0524d);
//this is the red arc whichhas its sweep argument manipulated by on touch
canvas.drawArc(oval, 180, sweep, false, paint);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_MOVE) {
float xPosition = event.getX();
float yPosition = event.getY();
if (oval.contains(xPosition, yPosition)) {
float x = xPosition - left;
float s = x * 100;
float b = s / oval.width();
percent = Math.round(b);
sweep = (180 / 100.0f) * (float) percent;
invalidate();
} else {
if (xPosition < left) {
percent = 0;
sweep = (180 / 100.0f) * (float) percent;
invalidate();
}
if (xPosition > right) {
percent = 100;
sweep = (180 / 100.0f) * (float) percent;
invalidate();
}
}
}
return true;
}
}
I want to make it move only when the user touches on the actual arc
draw path
At the beginning of onTouchEvent() you need to check whether xPosition and yPosition are fulfilling some condition. If yes, you do the stuff, which you are doing now. If no, return true.
Condition:
We want to check whether x, y are in that grey arc background:
Let's calculate a distance from (x, y) to that point (a, b) in the center:
final dist = distance(x, y, a, b)
distance() is a simple Euclidean distance between points (x,y) and (a,b):
double distance(int x, int y, int a, int b)
{
return Math.sqrt((x - a) * (x - a) + (y - b) * (y - b));
}
x, y are in that grey arc background, if y > Y && dist >= r && dist <= R.
Does this work for you?
You don't need a lot of Maths. You can calculate the distance of the touch point from the center of your arc (it's a circle so it's easy) and the compare that with the radius you are using. That will tell you if the point is on the arc (almost, see below for full case).
Point touchEv = ...;
Point circleCenter = ...;
//the radius of the circle you used to draw the arc
float circleRadius = ...;
//how far from the arc should a touch point treated as it's on the arc
float maxDiff = getResources().getDimension(R.dimen.max_diff_dp);
//calculate the distance of the touch point from the center of your circle
float dist = Math.pow(touchEv.x-circleCenter.x,2) + Math.pow(touchEv.y- circleCenter.y,2)
dist = Math.sqrt(dist);
//We also need the bounding rect of the top half of the circle (the visible arc)
Rect topBoundingRect = new Rect(circleCenter.x - circleRadius,
circleCenter.y - circleRadius,
circleCenter.x + circleRadius,
circleCenter.y);
if (Math.abs(dist - circleRadius) <= maxDiff &&
topBoundingRect.contains(touchEv.x, touchEv.y)) {
// the user is touching the arc
}
I have an arrow on the edge of the screen in my Java game that's supposed to point to an object else where on the map but it just keeps going round the screen and fails to point at the object, any ideas why?
Here's my code:
float angle = (float)Math.toDegrees(Math.atan2(currentInteractive.getY()-player.pos[1], currentInteractive.getX()-player.pos[0]));
arrow.setRotation(angle);
float magnitude;
float abs_cos_angle = (float) Math.abs(Math.cos(angle));
float abs_sin_angle = (float) Math.abs(Math.sin(angle));
if (Main.DISPLAY_WIDTH/2*abs_sin_angle <= Main.DISPLAY_HEIGHT/2*abs_cos_angle)
{
magnitude = Main.DISPLAY_WIDTH/2/abs_cos_angle;
}
else
{
magnitude = Main.DISPLAY_HEIGHT/2/abs_sin_angle;
}
float ax = (float) (camera.viewPort.getCenterX() + Math.cos(angle)*magnitude);
float ay = (float) (camera.viewPort.getCenterY() + Math.sin(angle)*magnitude);
arrow.draw(ax, ay, Color.green);
I fixed the problem, turns out it was because I hadn't converted the angle to radians and it was messing with the magnitude, I also had to encapsulate some parts of the maths with brackets to ensure it was calculated in the right order.
Here's my code now:
Interactive currentInteractive = interactiveList.get(i);
currentInteractive.draw();
float angle = (float)Math.toDegrees(Math.atan2(currentInteractive.getY()-player.pos[1], currentInteractive.getX()-player.pos[0]));
arrow.setRotation(angle+90);
float magnitude;
float abs_cos_angle = (float) Math.abs(Math.cos(Math.toRadians(angle)));
float abs_sin_angle = (float) Math.abs(Math.sin(Math.toRadians(angle)));
if (Main.DISPLAY_WIDTH/2*abs_sin_angle <= Main.DISPLAY_HEIGHT/2*abs_cos_angle)
{
magnitude = (Main.DISPLAY_WIDTH-20)/2/abs_cos_angle;
}
else
{
magnitude = (Main.DISPLAY_HEIGHT-20)/2/abs_sin_angle;
}
float ax;
if(currentInteractive.pos[0] > player.pos[0]-(Main.DISPLAY_WIDTH/2) && currentInteractive.pos[0] < player.pos[0]+(Main.DISPLAY_WIDTH/2))
{
ax = (float) currentInteractive.pos[0];
}
else
{
ax = (float) (Math.cos(Math.toRadians(angle)) * magnitude) + camera.viewPort.getCenterX()-10;
}
float ay;
if(currentInteractive.pos[1] > player.pos[1]-(Main.DISPLAY_HEIGHT/2) && currentInteractive.pos[1] < player.pos[1]+(Main.DISPLAY_HEIGHT/2))
{
ay = (float) currentInteractive.pos[1];
}
else
{
ay = (float) (Math.sin(Math.toRadians(angle)) * magnitude) + camera.viewPort.getCenterY()-10;
}
arrow.draw(ax, ay, Color.green);