I have made a drawSquare() function that draws several 2D squares on my screen:
void drawSquare(GLfloat length, GLfloat x, GLfloat y, GLfloat outline){
// x1,y1 is the top left-hand corner coordinate
// and so on...
GLfloat x1, y1, x2, y2, x3, y3, x4, y4;
x1 = x - length / 2;
y1 = y + length / 2;
x2 = x + length / 2;
y2 = y + length / 2;
x3 = x + length / 2;
y3 = y - length / 2;
x4 = x - length / 2;
y4 = y - length / 2;
// ACTUAL SQUARE OBJECT
glColor3f(0.0, 1.0, 1.0); // Colour: Cyan
glBegin(GL_POLYGON);
glVertex2f(x1, y1); // vertex for BLUE SQUARES
glVertex2f(x2, y2);
glVertex2f(x3, y3);
glVertex2f(x4, y4);
glEnd()
}
When I click a square, I need its colour to change. I already have a mouse function set up that displays the mouse location when I right click:
void processMouse(int button, int state, int x, int y)
{
if ((button == GLUT_LEFT_BUTTON) && (state == GLUT_DOWN)){
// gets mouse location
printf("Clicked on pixel %d - %d", x, y);
// prints to console
}
}
and an if statement inside the above if statement like this:
if (x > 0 && x < 95 && y > 302 && y < 395) {
// there's a square at this location!
// change the square colour!
}
When I place exit(0); inside this statement:
if (x > 0 && x < 95 && y > 302 && y < 395) {
exit(0);
}
My program exits fine, so the condition works, I just want to know how I can somehow call my drawSquare() function again with a different colour.
Initially when I call my drawSquare(), it's called in my display function like this:
void display(){
glClear(GL_COLOR_BUFFER_BIT); /* clear window */
// there are some other primatives here too
drawSquare(100,150, 750,true); // square drawn
}
SOLUTION
Here's how I fixed my issue. I made a global boolean variable boolean variable areaClicked = false; that checks if user has clicked, we set as false by default.
In my mouse function, I check if a square has been clicked, if so, set the boolean to true:
if (x >= 0 && x <= 95 && y >= 302 && y <= 380) { // area of box
areaClicked = true;
}
Now in my display function, we check if the boolean has been triggered, if it has, then display my recoloured square, otherwise, do nothing:
if (areaClicked != false) {
drawRecolour(100, 50, 350, true); // 4th square drawn
}
else areaClicked = false;
In your event handler set a variable, then trigger a redraw.
if (x > 0 && x < 95 && y > 302 && y < 395) {
// there's a square at this location!
// change the square colour!
square_color = ...;
glutPostRedisplay();
}
In your display function check for the variable and use that to determine the color:
// you should probably make the color a parameter of drawSquare
void drawSquare(GLfloat length, GLfloat x, GLfloat y, GLfloat outline){
// OpenGL doesn't do "objects". It **DRAWS** things. Like pen on paper
glColor3f(square_color); // <---- uses variable
glBegin(GL_POLYGON);
...
glEnd()
}
Related
Question: I need to upgrade an old Embarcadero VCL graphic math application by introducing antialiased lines. So, I wrote in C++ the algorithm indicated in the page: https://en.wikipedia.org/wiki/Xiaolin_Wu%27s_line_algorithm.
How to write correctly the function 'plot' to draw the pixel at (x,y) with a brightness 'c', especially on the Embarcadero VCL.
Solution:
This solution has been possible by the contribution of #Spektre (use of a union to mix colors according to some brightness). pC is a canvas pointer, funcColor is the line intended color, and are properties of the Observer class:
//Antialiased line:
void Observer::aaLine(int x0, int y0, int x1, int y1)
{
union {
uint32_t dd;//The color value
uint8_t db[4];//To work on channels: {00.RR.GG.BB}
} c, c0;//Line color, and background color
//Color mixer, with calculations on each channel, because there is no
//Alpha channel with VCL:
auto plot = [&](int X, int Y, float brightness){
c.dd = funcColor;//Line color
c0.dd = pC->Pixels[X][Y];//Background color
//Find coefficients to simulate transparency, where there is not:
//Front color is augmented when background is decreased:
for(int i = 0; i < 3; ++i)
c.db[i] = int(c.db[i] * brightness + c0.db[i] * (1 - brightness));
//Output obtained by conversion:
pC->Pixels[X][Y] = static_cast<TColor>(c.dd);
};
//Wu's algorithm:
//Fractional part of x:
auto fpart = [](double x) { return x - floor(x); };
auto rfpart = [&](double x) { return 1 - fpart(x); };
bool steep = abs(y1 - y0) > abs(x1 - x0);//Means slope > 45 deg.
if(steep) {
std::swap(x0, y0);
std::swap(x1, y1);
}
if( x0 > x1 ) {
std::swap(x0, x1);
std::swap(y0, y1);
}
double dx = x1 - x0, dy = y1 - y0, gradient = (dx == 0. ? 1. : dy/dx) ;
//Handle first endpoint
double xend = x0,
yend = y0 + gradient * (xend - x0),
xgap = rfpart(x0 + 0.5),
xpxl1 = xend, // this will be used in the main loop
ypxl1 = floor(yend);
if( steep ) {
plot(ypxl1, xpxl1, rfpart(yend) * xgap);
plot(ypxl1+1, xpxl1, fpart(yend) * xgap);
}
else {
plot(xpxl1, ypxl1 , rfpart(yend) * xgap);
plot(xpxl1, ypxl1+1, fpart(yend) * xgap);
}
auto intery = yend + gradient; // first y-intersection for the main loop
//Handle second endpoint
xend = round(x1);
yend = y1 + gradient * (xend - x1);
xgap = fpart(x1 + 0.5);
auto xpxl2 = xend, //this will be used in the main loop
ypxl2 = floor(yend);
if( steep ){
plot(ypxl2 , xpxl2, rfpart(yend) * xgap);
plot(ypxl2+1, xpxl2, fpart(yend) * xgap);
//Main loop:
for(double x = xpxl1 + 1 ; x <= xpxl2 - 1 ; x += 1) {
plot(int(intery) , x, rfpart(intery));
plot(int(intery+1), x, fpart(intery));
intery += gradient;
}
}
else {
plot(xpxl2, ypxl2, rfpart(yend) * xgap);
plot(xpxl2, ypxl2+1, fpart(yend) * xgap);
//Main loop:
for(double x = xpxl1 + 1 ; x <= xpxl2 - 1 ; x += 1) {
plot(x, int(intery), rfpart(intery));
plot(x, int(intery+1), fpart(intery));
intery += gradient;
}
}
}//Observer::aaLine.
The source code above is updated, and works for me as a solution.
The image below comes from tests: Blue's are NOT antialiased, and Red's ones are the results from the solution above. I am satisfied with what I want to do.
I think your problem lies in this:
auto plot = [&](double X, double Y, double brighness){
pC->Pixels[X][Y] = brightness; };
If I understand it correctly pC is some target TCanvas ... this has 2 major problems:
pC->Pixels[X][Y] = brightness; will handle brightness as color according to selected mode (so copy,xor,... or whatever) and not as brightness.
I would use form of alpha blending where you take originaly render color (or background) and wanted color of rendered line and mix it with brightness as parameter:
TColor c0=pC->Pixels[X][Y],c0=color of your line;
// here mix colors c = (c0*(1.0-brightness)) + (c1*brightness)
// however you need to do this according to selected pixelformat of you graphic object and color channel wise...
pC->Pixels[X][Y]=c;
Beware VCL transparency does not use alpha parameter its just opaque or not ... For more info about the mixing see similar:
Digital Differential Analyzer with Wu's Algorithm in OpenGL
especially pay attention to the:
union
{
DWORD dd;
BYTE db[4];
} c,c0;
as TColor is 32bit int anyway ...
speed of pC->Pixels[X][Y] in VCL (or any GDI based api) is pitiful at best
in case you handle many pixels you should consider to use ScanLine[Y] from Graphics::TBitmap ... and render to bitmap as backbufer. This usually improve speed from ~1000 to ~10000 times. for more info see:
Graphics rendering in C++
I can use the function CheckCollisionCircleRec(Vector2{ x, y }, radius, paddleRect) to find out simply if my circle has collided with my rectangle, but I want to be able to find out what side of the rectangle my circle has collided with. How would I go about doing this? None of the algorithms I've made are working. Example of my most recent blunder:
if (x - radius <= 0 || x + radius >= screenWidth) {
speedX *= -1;
}
else if (y - radius <= 0 || y + radius >= screenHeight) {
speedY *= -1;
}
else if (CheckCollisionCircleRec(Vector2{ x, y }, radius, paddleRect)) {
float paddleBottom = paddleRect.y + paddleRect.height;
float paddleRight = paddleRect.x + paddleRect.width;
if (range(paddleRect.x, paddleRect.x + speedX / 100, x + radius)) {
x = paddleRect.x - radius;
speedX *= -1;
}
if (range(paddleRight - speedX / 100, paddleRight, x - radius)) {
x = paddleRight + radius;
speedX *= -1;
};
if (range(paddleRect.y, paddleRect.y + speedY / 100, y + radius)) {
y = paddleRect.y - radius;
speedY *= -1;
}
if (range(paddleBottom - speedY / 100, paddleBottom, y - radius)) {
y = paddleBottom + radius;
speedY *= -1;
};
EDIT:
Here's the function I used to get the working end result:
// px and py are the ball's previous locations
// x and y are the ball's current locations
void checkCollision(Rectangle rectangle) {
int left = rectangle.x;
int right = rectangle.x + rectangle.width;
int top = rectangle.y;
int bottom = rectangle.y + rectangle.height;
if (CheckCollisionCircleRec(Vector2{ x, y }, radius, rectangle)) {
if (px < left) {
speedX = negative(speedX);
x = left - radius;
}
else if (px > right) {
speedX = positive(speedX);
x = right + radius;
}
else if (py < top) {
speedY = negative(speedY);
y = top - radius;
}
else if (py > bottom) {
speedY = positive(speedY);
y = bottom + radius;
};
};
};
A simply way is to use the PREVIOUS location of your circle. Not sure if you can in your program, but since you have an x and y handy, I'll assume you can have a prevX and prevY. I'll also assume these values represent the CENTER of the circle.
Now if (prevX < paddleRect.x), then you likely collided with the left side (not guaranteed, but resolving ambiguities with complete accuracy requires recursively simulating your physics at smaller and smaller timesteps, which is likely unnecessary here). You can also constrain this more tightly with something like if (prevX < paddleRect.x && prevY > paddleRect.y && prevY < paddleRect.y + paddRect.height). There are various constraints you can add depending on how cleanly you want the side to be hit before detecting it. You can add corner hits, etc.
The reason for using the previous location is that, if your circle is moving fast enough, then in a single frame it can jump straight into the middle of the rectangle. It's usually necessary to use the previous position to give more specific collision information in the current-location collision
Hey I want to draw something when a form pops up. I noticed the OnPaint even but it doesn't show my drawing. When I add it to a button it does show what I wanted to draw.
This is what I draw:
void TSelectElementForm::DrawLinesInLayout()
{
EraseLayOut(); //draws a new square over the previous lines
int X_origin, Y_origin;
int X1, Y1, X2, Y2, X3, Y3, X4, Y4;
int VectorSize;
int scale;
X_origin = Layout->Left +5;
Y_origin = Layout->Top +5;
Canvas->Pen->Color=clRed;
Canvas->MoveTo(X_origin,Y_origin);
Canvas->LineTo(X_origin,Y_origin+10);
Canvas->MoveTo(X_origin,Y_origin);
Canvas->LineTo(X_origin+10,Y_origin);
VectorSize = ElementVector[Index].vectorLP.size();
scale = CalculateScale();
for(int i =0; i < VectorSize; i++)
{
int LPX = ElementVector[Index].vectorLP[i].X;
int LPY = ElementVector[Index].vectorLP[i].Y;
int BeginX = ElementVector[Index].el_beginX;
Canvas->Pen->Color=clLime;
X1 = X_origin + ((LPX - BeginX) / scale);
Y1 = Y_origin + (LPY / scale);
if(i == 0)
{
Canvas->MoveTo(X1, Y1);
}
else
{
Canvas->LineTo(X1, Y1);
}
}
}
So when I call this method in a button it works, but when I call this in a OnShow event or OnPaint event it doesn't draw anything.
The strange thing is, when I simply just create a new project with a drawLine() method(that draws 1 line) and a OnPaint event It DOES work. How is that possible?
So, simply 2 questions:
Is this method to long to draw it before my form pops up?
How can I get this drawn on my form right away when it pops up?
I am trying to build a game in Opengl. Before I start making better movement mechanics I want to get collision working. I have cube-cube collision working and I have sphere-sphere collision working, but can't figure out cube-sphere collision. Since I want it in 3d I have the pivot at the center of the objects. Anyone have any suggestions?
EDIT: This is the code I currently have:
bool SphereRectCollision( Sphere& sphere, Rectangle& rect)
{
//Closest point on collision box
float cX, cY;
//Find closest x offset
if( sphere.getCenterX() < rect.GetCenterX())//checks if the center of the circle is to the left of the rectangle
cX = rect.GetCenterX();
else if( sphere.getCenterX() > rect.GetCenterX() + rect.GetWidth()) //checks if the center of the circle is to the right of the rectangle
cX = rect.GetCenterX() + rect.GetWidth();
else //the circle is inside the rectagle
cX = sphere.getCenterX();
//Find closest y offset
if( sphere.getCenterY() > rect.GetCenterY() + rect.GetHeight() )
cY = rect.GetCenterY();
else if( sphere.getCenterY() < rect.GetCenterY() - rect.GetHeight() )
cY = rect.GetCenterY() + rect.GetHeight();
else
cY = sphere.getCenterY();
//If the closest point is inside the circle
if( distanceSquared( sphere.getCenterX(), sphere.getCenterY(), cX, cY ) < sphere.getRadius() * sphere.getRadius() )
{
//This box and the circle have collided
return false;
}
//If the shapes have not collided
return true;
}
float distanceSquared( float x1, float y1, float x2, float y2 )
{
float deltaX = x2 - x1;
float deltaY = y2 - y1;
return deltaX*deltaX + deltaY*deltaY;
}
I found the solution. I had the right idea, but didn't quite know how to execute it:
bool SphereRectCollision( Sphere& sphere, Rectangle& rect)
{
float sphereXDistance = abs(sphere.X - rect.X);
float sphereYDistance = abs(sphere.Y - rect.Y);
float sphereZDistance = abs(sphere.Z - rect.Z);
if (sphereXDistance >= (rect.Width + sphere.Radius)) { return false; }
if (sphereYDistance >= (rect.Height + sphere.Radius)) { return false; }
if (sphereZDistance >= (rect.Depth + sphere.Radius)) { return false; }
if (sphereXDistance < (rect.Width)) { return true; }
if (sphereYDistance < (rect.Height)) { return true; }
if (sphereZDistance < (rect.GetDepth)) { return true; }
float cornerDistance_sq = ((sphereXDistance - rect.Width) * (sphereXDistance - rect.Width)) +
((sphereYDistance - rect.Height) * (sphereYDistance - rect.Height) +
((sphereYDistance - rect.Depth) * (sphereYDistance - rect.Depth)));
return (cornerDistance_sq < (sphere.Radius * sphere.Radius));
}
This algorithm doesn't work when a hit happen on an edge, the 2nd set of if conditions triggers but a collision isn't occuring
I am working on moving a small rectangle around a screen using the thumbstick on the xbox controller. I have it perfect for the mouse Input but I seem to have run into a problem with the controller. The square appears on the screen at (0,0) the deadzone for the controller. When I move the thumbstick the square move a specific amount and no further, when I lift my finger off the stick it goes back to (0,0). The code is below, its pretty simple code but cant get it to work. Thanks for any help.
Here is the first part.
void Graphics::rectangle(int x, int y)
{
{
r1.x1 = x;
r1.y1 = y;
r1.x2 = r1.x1 + 50;
r1.y2 = r1.y1 + 50;
}
}
Here is the second part.
LONG x = input->getGamepadThumbRX(0);
LONG y = input->getGamepadThumbRY(0);
float x1 = x/32767;
float y1 = y/32767;
if(x1 > 0 && x1 < 0.2f || x1 < 0 && x1 > -0.2f){
x1 = 0;
}
if(y1 > 0 && y1 < 0.2f || y1 < 0 && y1 > -0.2f){
y1 = 0;
}
float factor = 10;
int dx = int(factor * x1);
int dy = int(factor * y1);
graphics->rectangle(dx,dy);
I finally got a solution to this problem. I added the variables xNew and yNew that take the values of x and y and add them on to the existing values. This allowed me to move the square around the screen. xNew and yNew are initialize at the top of the class below is the code for the input from controller and the result in rectangle.
`
void Graphics::rectangle(int x, int y)
{
xNew += x;
yNew += -y;
{
r1.x1 = xNew;
r1.y1 = yNew;
r1.x2 = r1.x1 + 50;
r1.y2 = r1.y1 + 50;
}
}
void Game::update()
{
LONG x = input->getGamepadThumbRX(0);
LONG y = input->getGamepadThumbRY(0);
float x1 = x/32767;
float y1 = y/32767;
float factor = 10;
int dx = int(factor * x1);
int dy = int(factor * y1);
graphics->rectangle(dx,dy);
}`
The problem is the mouse is a "Relative Movement" Input and the thumb-stick is a "Absolute Movement" input. What you need to do is when you are working with the thumb-sticks your final line needs to be something like
graphics->rectangle(xOld + dx, yOld + dy);
where xOld and yOld is the location of the square before you applied the function.
Might be a little late, but I do have a very easy-to-use C++ XInput wrapper. Have a look here
It literally takes a few lines of code and you have full support for XInput controllers in your app :)