How do I perform image clipping efficiently within a parent RECT? - c++

So,
I'm playing around with the DIRECTX API in C++ and designing a sprite interface that does clipping within parent RECT's, like a UI window or another sprite or what have you.
This is because, later on, I'm going to make a scrolling function for a UI window.
I think an image would be appropriate to demonstrate what I am trying to achieve.
In order to do clipping calculations, at least four variables/structs are required (from what I can discern):
Parent RECT: the "clipping" bounds
D3DXIMAGE_INFO: containing the image information, such as width and height
Sprite position
Draw RECT: for the LPD3DXSPRITE->Draw() function. Basically, what part of the image should be drawn. This is where the "clipping" is done.
Now, I think it would be confusing if I showed you my entire interface and its inner workings and how it handles the variables.
So, instead,
this piece of code demonstrates how I'm currently calculating the clipping.
void ClippingCalculation(){
RECT parent_bounds, draw_rect;
D3DXVECTOR2 sprite_position; //Relative to parent rect
D3DXIMAGE_INFO img_info;
//Fill image info
D3DXGetImageInfoFromFile("image.png", &img_info); //Image is 100x100px
//Define the rects
SetRect(&parent_bounds, 0, 0, 200, 200);
SetRect(&draw_rect, 0, 0, img_info.Width, img_info.Height); //Draw the entire image by default
//Give it a position that makes a portion go out of parent bounds
sprite_position = D3DXVECTOR2(150, 150); // 3/4 of the entire image is out of bounds
//Check if completely within bounds
bool min_x = (sprite_position.x > parent_bounds.left),
min_y = (sprite_position.y > parent_bounds.top),
max_x = (sprite_position.x+img_info.Width < parent_bounds.right),
max_y = (sprite_position.y+img_info.Height < parent_bounds.bottom);
if(min_x && min_y && max_x && max_y)
return; //No clipping needs to be done
float delta_top = parent_bounds.top - sprite_position.y,
delta_left = parent_bounds.left - sprite_position.x,
delta_bottom = parent_bounds.bottom - sprite_position.y,
delta_right = parent_bounds.right - sprite_position.x;
//Check if completely outside bounds
bool out_left = (delta_left >= img_info.Width),
out_top = (delta_top >= img_info.Height),
out_bottom = (delta_bottom >= img_info.Height),
out_right = (delta_right >= img_info.Width);
if(out_left || out_top || out_bottom || out_right){
//No drawing needs to be done, it's not even visible
return; //No clipping
}
if(!min_x) draw_rect.left = delta_left;
if(!min_x) draw_rect.top = delta_top;
if(!max_x) draw_rect.right = delta_right;
if(!max_y) draw_rect.bottom = delta_bottom;
return;
}
My questions are:
Do you see anything immediately wrong with the code?
Is it efficient or inefficient?
The clipping is done every time the sprite is repositioned, loaded or when a parent is added.
Is the math solid? Should I approach it differently?
Could it be done in a better way and could you provide examples?

Clipping a RECT is a lot easier than this:
clipped_rect.left= MAX(rect_a.left, rect_b.left);
clipped_rect.right= MIN(rect_a.right, rect_b.right);
clipped_rect.top= MIN(rect_a.top, rect_b.top);
clipped_rect.bottom= MAX(rect_a.bottom, rect_b.bottom);
bool completely_clipped=
clipped_rect.left>=clipped_rect.right ||
clipped_rect.bottom>=clipped_rect.top
This assumes Y increases as you go up. It should be pretty easy to find a platform specific version of MIN and MAX that are performant.

Related

Rendering Tilemap on the screen correctly

I'm having a strange problem rendering my level based on tilemap correctly.
On the y axis all the tiles are normal and aligned, instead on the x axis they seem to be divided by a space i can't figure out why...
I created a matrix with enum values(from 0 to 2) and i cycled my matrix in a for
loop to render the tile with the current number:
ex. GROUND = 0; etc...
Here is a photo of what it looks like
http://it.tinypic.com/r/ali261/8
Here is the sprite for the tile
http://it.tinypic.com/r/21kggw5/8
i will add the code down here.
for(int y = 0; y < 15; y++)
{
for(int x = 0; x < 20; x++)
{
if(map[y][x] == GROUND)
render(tileTex,x*64 - camera.x,y*64 - camera.y,&gTileSprite[0],0,NULL,SDL_FLIP_NONE);
else if(map[y][x] == UGROUND)
render(tileTex,x*64 - camera.x,y*64 - camera.y,&gTileSprite[1],0,NULL,SDL_FLIP_NONE);
else if(map[y][x] == SKY)
render(tileTex,x*64 - camera.x,y*64 - camera.y,&gTileSprite[2],0,NULL,SDL_FLIP_NONE);
tBox[y][x].x = x*64;
tBox[y][x].y = y*64;
tBox[y][x].w = TILE_WIDTH;
tBox[y][x].h = TILE_HEIGHT;
}
}
Further to the comments above, one must be careful to avoid any blurring along the edges of tiles, since their repetition will make any defects more obvious than if they were viewed in isolation.
Blurring may be introduced in the process of drawing portions of the tilemap to the final/intermediate target, or as seems (and has been confirmed) in this case, the source material may have blurred edges.
Particularly when working with images of such 'low` pixel dimensions, one must be vigilant and ensure that any/all resizing operations are performed in an image-editor without re-sampling.
While bilinear/cubic re-sampling may be desired when blitting the assembled image to the screen, it is never desirable for such re-sampling to happen to the source material.

Relative positioning of CCSprite

So I have a game in Windows Phone's version of cocos2dx. There is a background (CCLayerColor) and in the middle of it I place another CCLayerColor. The problem is that, when moving a layer with sprites inside that middle layer, the movement is done relative to the whole screen, not to the layer in middle.
The code for creating this CClayerColor in the middle of the screen is:
mWheelMachine = WheelMachineView::create(symbolMap, path);
mWheelMachine->setContentSize(CCSize(WHEEL_MACHINE_WIDTH , WHEEL_MACHINE_HEIGHT));//values equaling one third of screen size
mWheelMachine->setPosition(ItemManager::sharedItemManager()->getItemPosition(WHEEL_MACHINE_TAG
addChild(mWheelMachine, THEME_WHEEL_MACHINE_ORDER);//position in the middle of the screen
"WheelMachineView" is a subclass of "CCLayerColor", while create just overrides the correspondent of CCLayerColor.
Inside this class, I have another layer that moves along with its CCSprite objects drawn inside.
unsigned short o;
for (unsigned short i = 0; i < NUMBER_OF_WHEELS; i++)
{
WheelView* wheelLayer = WheelView::create();
wheelLayer->setIsRelativeAnchorPoint(true);
wheelLayer->setAnchorPoint(ccp(0,0));
wheelLayer->setPosition(i * WHEEL_WIDTH, -100 );
wheelLayer->setContentSize(CCSize(WHEEL_WIDTH, WHEEL_HEIGHT)); //large height value, to have room for making an animation with moving symbols
addChild(wheelLayer);
mWheels.push_back(wheelLayer);
/* Get the wheel symbols */
list<Symbol*> wheelSymbols = mWheelModel->getWheelSymbols(i);
/* Index */
o = 0;
for (list<Symbol*>::reverse_iterator it = wheelSymbols.rbegin(); it != wheelSymbols.rend(); it++)
{
CCSprite* symbol = CCSprite::spriteWithSpriteFrameName((*it)->getName().c_str());
symbol->setPosition(ccp(WHEEL_WIDTH / 2, SYMBOL_HEIGHT/2 + (o++ * SYMBOL_HEIGHT) - SYMBOL_HEIGHT));
symbol->setAnchorPoint(ccp(0.5f, 0.5f));
symbol->setScale(SYMBOL_SCALE_FACTOR);
wheelLayer->addChild(symbol, 10, o);
}
}
So, when moving 'wheelLayer' to a position outside the content size of 'mWheelMachine', it will move over the entire screen, thus drawing the symbols outside the middle designated area (mWheelMachine) for that. As it has a larger size than its parent, it draws symbols outside the parent.
Why is this happening? How can I make it to only use mWheelMachine's content size?
You can imagine cclayer is a node, it does not have the concept of size and anchor and set its content size does not have a role on the show's content, it will only affect the control area.
I think you should just hope that some of the pictures to be displayed in a region. Using a mask will be a good solution.

OpenGL Translation - Coordinate

I'm attempting to make a simple game that allows you to jump around the screen and onto the platforms without leaving the screen or falling through the platforms.
So far i have managed to create a little 2D square which i refer to as my sprite or character along with 2 platforms.
I have finally made my sprite move up, down, left and right (no jump yet) but my collision for leaving the screen isn't working how i thought it would...
What i'm doing is using glTranslate(x,y,z) to move my sprite depending on the key input from the keyboard and testing a collision between the sprite and the screen before. Unfortunately i think im getting confused with the coordinates being used for the translation inside my collisionScreen.
Something to note is, i can not take my sprite past the origin of where the sprite was initially drawn ie "0" no matter where or when the sprite is drawn.
It seems that:
void collisionScreen (int x, int y)
{
//stops sprite leaving current screen size
if(x_pos < 0 || x_pos+s1_W > winX)
x_pos -= x_pos;
if(y_pos < 0 || y_pos+s1_H > winY)
y_pos -= y_pos;
}
doesn't actually use the x and y parameters you pass into it.
Perhaps you meant something like this:
void collisionScreen (int x, int y)
{
//stops sprite leaving current screen size
if(x_pos < 0)
x_pos = 0;
else if(x_pos+s1_W > winX)
x_pos = winX - s1_W;
if(y_pos < 0)
y_pos = 0;
else if(y_pos+s1_H > winY)
y_pos = winY - s1_H;
}
Also, your sprite will not pass 0 as that is what this method is designed to do.

2D Platformer Collision Handling

I am trying to create a 2D platformer (Mario-type) game and I am some having some issues with handling collisions properly. I am writing this game in C++, using SDL for input, image loading, font loading, etcetera. I am also using OpenGL via the FreeGLUT library in conjunction with SDL to display graphics.
My method of collision detection is AABB (Axis-Aligned Bounding Box), which is really all I need to start with. What I need is an easy way to both detect which side the collision occurred on and handle the collisions properly. So, basically, if the player collides with the top of the platform, reposition him to the top; if there is a collision to the sides, reposition the player back to the side of the object; if there is a collision to the bottom, reposition the player under the platform.
I have tried many different ways of doing this, such as trying to find the penetration depth and repositioning the player backwards by the penetration depth. Sadly, nothing I've tried seems to work correctly. Player movement ends up being very glitchy and repositions the player when I don't want it to. Part of the reason is probably because I feel like this is something so simple but I'm over-thinking it.
If anyone thinks they can help, please take a look at the code below and help me try to improve on this if you can. I would like to refrain from using a library to handle this (as I want to learn on my own) or the something like the SAT (Separating Axis Theorem) if at all possible. Thank you in advance for your help!
void world1Level1CollisionDetection()
{
for(int i; i < blocks; i++)
{
if (de2dCheckCollision(ball,block[i],0.0f,0.0f)==true)
{
de2dObj ballPrev;
ballPrev.coords[0] = ball.coords[0];
ballPrev.coords[1] = ball.coords[1];
ballPrev.coords[2] = ball.coords[2];
ballPrev.coords[3] = ball.coords[3];
ballPrev.coords[0] -= ball.xspeed;
ballPrev.coords[1] -= ball.yspeed;
ballPrev.coords[2] -= ball.xspeed;
ballPrev.coords[3] -= ball.yspeed;
int up = 0;
int left = 0;
int right = 0;
int down = 0;
if (ballPrev.coords[0] < block[i].coords[0] && ballPrev.coords[2] < block[i].coords[0] && (((ball.coords[1] < block[i].coords[1]) || (ball.coords[3] < ball.coords[1])) || ((ball.coords[1] < block[i].coords[3]) || ball.coords[3] < block[i].coords[3])))
{
left = 1;
}
if (ballPrev.coords[0] > block[i].coords[2] && ballPrev.coords[2] > block[i].coords[2] && (((ball.coords[1] < block[i].coords[1]) || (ball.coords[3] < ball.coords[1])) || ((ball.coords[1] < block[i].coords[3]) || (ball.coords[3] < block[i].coords[3]))))
{
right = 1;
}
if(ballPrev.coords[1] < block[i].coords[1] && block[i].coords[1] < ballPrev.coords[3] && ballPrev.coords[3] < block[i].coords[3])
{
up = 1;
}
if(block[i].coords[1] < ballPrev.coords[1] && ballPrev.coords[1] < block[i].coords[3] && block[i].coords[3] < ballPrev.coords[3])
{
down = 1;
}
cout << left << ", " << right << ", " << up << ", " << down << ", " << endl;
if (left == 1)
{
ball.coords[0] = block[i].coords[0] - 18.0f;
ball.coords[2] = block[i].coords[0] - 2.0f;
}
else if (right == 1)
{
ball.coords[0] = block[i].coords[2] + 2.0f;
ball.coords[2] = block[i].coords[2] + 18.0f;
}
else if (down == 1)
{
ball.coords[1] = block[i].coords[3] + 4.0f;
ball.coords[3] = block[i].coords[3] + 20.0f;
}
else if (up == 1)
{
ball.yspeed = 0.0f;
ball.gravity = 0.0f;
ball.coords[1] = block[i].coords[1] - 17.0f;
ball.coords[3] = block[i].coords[1] - 1.0f;
}
}
if (de2dCheckCollision(ball,block[i],0.0f,0.0f)==false)
{
ball.gravity = -0.5f;
}
}
}
To explain what some of this code means:
The blocks variable is basically an integer that is storing the amount of blocks, or platforms. I am checking all of the blocks using a for loop, and the number that the loop is currently on is represented by integer i.
The coordinate system might seem a little weird, so that's worth explaining.
coords[0] represents the x position (left) of the object (where it starts on the x axis).
coords[1] represents the y position (top) of the object (where it starts on the y axis).
coords[2] represents the width of the object plus coords[0] (right).
coords[3] represents the height of the object plus coords[1] (bottom).
de2dCheckCollision performs an AABB collision detection.
Up is negative y and down is positive y, as it is in most games.
Hopefully I have provided enough information for someone to help me successfully. If there is something I left out that might be crucial, let me know and I'll provide the necessary information. Finally, for anyone who can help, providing code would be very helpful and much appreciated.
Thank you again for your help!
Edit 2: I have updated my code with a new algorithm that checks where the ball was previously before collision. Corner cases work on that single platform correctly now, and when I have a wall of objects, I can slide against it correctly now. The only remaining problem is that there is a small jittering effect that happens when I am on the ground, where the ball is constantly going up and down as if it is being pulled by gravity and then the ball falls back into the object again.
Edit: Here is a URL to an image trying to show the kinds of problems I am having:
http://img8.imageshack.us/img8/4603/collisionproblem.png
In case the explanation in the picture doesn't make too much sense, the ball cannot move left past the corner of an object unless I jump over it. However, the ball can move right, but it gets repositioned to the right of the object while moving, which is not needed. This creates a skipping movement essentially, where it appears as the the ball is skipping over half of the object or so when I move right. If this doesn't make sense, please ask me and I'll try to clarify more.
One problem with your code is that you only detect situations like this:
If the circle happens to be fully inside the block, you don't reposition at all. And that's a problem.
You're trying to think about your simulation as if it were continuous, but keep in mind it's discrete. In general, if you only look at the current state of the ball, you really cannot know which side it collided with. Look at these two possibilities:
The first solution that comes to mind is to look at the last position of the ball as well; more precisely, look at the delta vector. See if the delta vector intersects a wall. If it does, reposition in an axis-aligned direction towards the wall intersected by the delta vector.
Edit: When I said "delta vector", I forgot that you're moving a square and not a single point. So, if you just look at the delta vector of the top-left corner, that's not going to be enough because it may not detect that part of the ball entered a block. Instead, you can look at the delta vectors of all 4 corners.

Making QGraphicsScene bigger when a graphic item is placed against its border

I've made a QGraphicsScene with a mouseClickEvent that lets the user create blue squares inside of it. But I want to make the scene grow when an item is placed against its border so that the user never runs out of space on the graphics scene.
What's the best way to make a graphics scene bigger in this case?
I suggest doing something like the following:
Get the bounding rect of all items in the scene using QGraphicsScene::itemsBoundingRect().
Add some padding around that rect to make sure the bounds of the items won't hit the edge of the view. Something like myRect.adjust(-20, -20, 20, 20) should be sufficient.
Use QGraphicsView::fitInView(myRect, Qt::KeepAspectRatio) to ensure the taken area is within the visible bounds of the view.
That should do it. This code should be called whenever something has changed in the scene. You can use QRectF::intersects() function to find out if the new rect has been placed on the edge of the view.
What's the best way to make a graphics scene bigger in this case?
The GraphicsScene is an infinite coordinate system. Most clients will use itemsBoundingRect() to get an idea how much space is actually used by items in the scene. If you have cleared the scene, you might want to call QGraphicsScene::setSceneRect(QRectF()) to "make it smaller" again.
Hope that helps.
sorry if this is a little bit late(6 years) but I will provide an answer if someone still struggling with this or want another approach.I implement this in mouseReleaseEvent in the custom class derive from QGraphicsObject. Note that I initialize the size of my QGraphicsScene (1000,1000) with the following code.scene->setSceneRect(0,0,1000,1000). So here what my code will do. If the Item(the item is draggable) placed against the border, that border will increase. So here is my code:
void MyItem::mouseReleaseEvent(QgraphicsceneMouseEvent* event){
QRectF tempRect = this->scene()->sceneRect();
if(this->scenePos().y() < this->scene()->sceneRect().top()){
tempRect.adjust(0,-200,0,0);
if(this->scenePos().x() < this->scene()->sceneRect().left()){
tempRect.adjust(-200,0,0,0);
}
else if(this->scenePos().x() + 200> this->scene()->sceneRect().right()){
tempRect.adjust(0,0,200,0);
}
}
else if(this->scenePos().y() + 200 > this->scene()->sceneRect().bottom()){
tempRect.adjust(0,0,0,200);
if(this->scenePos().x() < this->scene()->sceneRect().left()){
tempRect.adjust(-200,0,0,0);
}
else if(this->scenePos().x() + 200> this->scene()->sceneRect().right()){
tempRect.adjust(0,0,200,0);
}
}
else if(this->scenePos().x() < this->scene()->sceneRect().left()){
tempRect.adjust(-200,0,0,0);
if(this->scenePos().y() < this->scene()->sceneRect().top()){
tempRect.adjust(0,-200,0,0);
}
else if(this->scenePos().y() + 200 > this->scene()->sceneRect().bottom()){
tempRect.adjust(0,0,0,200);
}
}
else if(this->scenePos().x() + 200> this->scene()->sceneRect().right()){
tempRect.adjust(0,0,200,0);
if(this->scenePos().y() < this->scene()->sceneRect().top()){
tempRect.adjust(0,-200,0,0);
}
else if(this->scenePos().y() + 200 > this->scene()->sceneRect().bottom()){
tempRect.adjust(0,0,0,200);
}
}
this->scene()->setSceneRect(tempRect);
I know its late, but for anyone looking for python code here:
class Scene(QtWidgets.QGraphicsScene):
def __init__(self):
super(Scene, self).__init__()
self.setSceneRect(0, 0, 2000, 2000)
self.sceneRect().adjust(-20, -20, 20, 20)
self.old_rect = self.itemsBoundingRect()
def adjust(self):
w = self.sceneRect().width()
h = self.sceneRect().height()
x = self.sceneRect().x()
y = self.sceneRect().y()
adjust_factor = 500
adjust_factor2 = 300
smaller = self.is_smaller()
self.old_rect = self.itemsBoundingRect()
if not self.sceneRect().contains(self.old_rect):
self.setSceneRect(-adjust_factor + x, -adjust_factor + y, adjust_factor + w, adjust_factor + h)
if smaller:
self.setSceneRect(adjust_factor2 + x, adjust_factor2 + y, abs(adjust_factor2 - w), abs(adjust_factor2 - h))
def is_smaller(self):
x = self.old_rect.x()
y = self.old_rect.y()
h = self.old_rect.height()
w = self.old_rect.width()
if ((x <= self.itemsBoundingRect().x()) and (y <= self.itemsBoundingRect().y())
and (h > self.itemsBoundingRect().height()) and (w > self.itemsBoundingRect().width())):
return True
return False
Explanation:
use self.sceneRect().contains(self.itemBoundingRect) check whether the itemBoundingRect is within the sceneRect, if its not in the sceneRect then use self.setSceneRect() to increase the sceneRect size
(Note: make sure you add to the previous sceneRect like shown in the above code).
If you also want to decrease the sceneRect. Store the old itemBoundingRect and compare it with the new one, if the new itemSceneRect Rectangle is smaller then decrease the size by some factor (refer to the above code).
Usage:
you may call the adjust method from anywhere you like. But Calling the adjust method from mouseReleaseEvent worked the best for me.
*If you have any suggestions or query you may comment.