Tuesday, March 5, 2013

Selection Box

In order to create a selection box, I want a very simple green box. It appears when you press the left mouse button, expands to where your pointer is and knows how to create the box no matter whether your second corner your mouse pointer makes is above/below or to the left or right of your starting point.

To begin, I made a quad. Well actually, I made a set of four lines that made a square and made it unit size. Simple enough.

The input manager handling the UI takes in the mouse input. Wherever the mouse clicks, if it is a left mouse button press then the position is recorded. As long as the button is held down, the box is displayed and the position of the mouse represents the second corner of the box. When the mouse button is released, the box disappears (if it was a left mouse button release).

Okay, so we've the box and it appears where we click. But if we drag we could go and make any of the four other possible corners of a rectangle. The second corner could be the top left, top right, bottom left or bottom right. But that's important. We want to draw our box at the bottom left and then scale it outward to the top right corner so that it is the right size.

Then my next problem is calculating the bottom left corner. Well, if we simply took the lowest x and lowest y, it would be flat wrong. Unfortunately this is isometric coordinates. But, if instead we translated our two corners into the form of

(x,y) => ((x+y) , (y-x))

Now you have the isometric style x and y coordinates that matches what the player sees. As you move right, x+y increases. As you move up, y-x would increase. It is correct... but only enough to determine which is more "left" and which is more "down". You'll notice some weird division going on, it has to do with the fact that you've got a square rotated on the side and I'm trying to determine the length and thus scale the unit sized quad I had before correctly. Also don't be a foolbag like I was and use Ogre::Entity->scale instead of properly using Ogre::Entity->setScale. Yeah, results are not good if you use the wrong method. Hurr.

std::pair<float,float> firstCorner = std::pair<float,float>(m_firstBoxCorner.first + m_firstBoxCorner.second, m_firstBoxCorner.second - m_firstBoxCorner.first);
std::pair<float,float> secondCorner = std::pair<float,float>(m_mousePos.first + m_mousePos.second, m_mousePos.second - m_mousePos.first);
  
float x = m_firstBoxCorner.first;
float y = m_firstBoxCorner.second;
  
float width = 0;
float height = 0;
    
if(firstCorner.first < secondCorner.first)
{
 width = Ogre::Math::Abs((firstCorner.first - secondCorner.first) / Ogre::Math::Sqrt(2));
}
else
{
 width = Ogre::Math::Abs(firstCorner.first - secondCorner.first) / 2;
}

if(firstCorner.second < secondCorner.second)
{
 height = Ogre::Math::Abs((firstCorner.second - secondCorner.second) / Ogre::Math::Sqrt(2));
}
else
{
 height = Ogre::Math::Abs(firstCorner.second - secondCorner.second) / 2;
}

if (secondCorner.first < firstCorner.first)
{
 //move left
 x -= width;
 y -= width;
}

if (secondCorner.second < firstCorner.second)
{
 //move down
 x += height;
 y -= height;
}

Ogre::SceneNode* pSelectionBoxSceneNode = m_selectionBoxEntity->getParentSceneNode();
pSelectionBoxSceneNode->setPosition(Ogre::Vector3(x, y, 0)); 
pSelectionBoxSceneNode->setScale(Ogre::Vector3(width, height, 1));
pSelectionBoxSceneNode->setVisible(true); //hope this call isn't expensive 

Notice that when I get the x,y coordinate it is a bit inaccurate (perhaps off by 1). I might also be able to take the x,y from the original points based on who was more to the left and bottom but I'll have to convince myself of that later.

What do we do? We start our box at the bottom left. Then we scale it out according to the width and height. We use the translated points for that.

Ogre::SceneNode* pSelectionBoxSceneNode = m_selectionBoxEntity->getParentSceneNode();
pSelectionBoxSceneNode->setPosition(Ogre::Vector3(x, y, 0)); 
pSelectionBoxSceneNode->setScale(Ogre::Vector3(width, height, 1));
pSelectionBoxSceneNode->setVisible(true); //hope this call isn't expensive

Then we can use these values to scale our box.

pSelectionBoxSceneNode->setScale(Ogre::Vector3(width, height, 1));

And remember that we needed to rotate our square 45 degrees along the z axis otherwise it'll be an expanding diamond instead of a square!

Ogre::Quaternion boxRotationZ = Ogre::Quaternion(Ogre::Degree(45), Ogre::Vector3::UNIT_Z);
pSceneNode->rotate(boxRotationZ);

Voila

Given what you see here, you might have asked... gee does your selection code from before work? Yeah probably not. Some testing of it last night revealed lots of issues. I believe the above code works much better so I'm going to do the same thing when picking which squares to look at. I'll work on that more later, for now I've made it "work", but the square picking at the edges are terribly inaccurate.

No comments:

Post a Comment