I load a 3D-model from a file and need to see it all on my screen. All vertices should be on the screen within the main window. Then I rotate and zoom the model and at some point I would like to fit the model to the window again. So, I have written the function OptimiseView (see below) which multiplies the view matrix by each vertex position and then calculates minimum and maximum coordinates of the screen plane.
The above multiplication takes a lot of time. My shader does the same multiplication but I can't manage to store minimum and maximum coordinates in the shader (GPU) and return these values back to the program (CPU) after processing the last vertex.
Is this possible at all? How do CAD-systems implement this Fit View (Optimise View) feature? How does a space mouse (e.g. 3Dconnexion) work in this relation?
I am currently using C++ and OpenGL.
void OptimiseView(glm::mat4& view, glm::mat4* proj, int trianglesNumber, float* positions)
{
if ((rotAngleX != 0) || (rotAngleY != 0) || (mouseScroll != 0))
{
float minX, maxX, minY, maxY, minZ, maxZ;
float centreX, centreY;
float largestDimension;
int triangleCount{ 0 };
glm::vec4 vertexPosition;
if (trianglesNumber < 1)
{
minX = maxX = minY = maxY = minZ = maxZ = 0;
}
else
{
vertexPosition = view * glm::vec4(
positions[0],
positions[1],
positions[2],
1.0f);
minX = maxX = vertexPosition.x;
minY = maxY = vertexPosition.y;
minZ = maxZ = vertexPosition.z;
}
while (triangleCount < trianglesNumber)
{
for (int initialPosition : {0, 3, 9})
{
vertexPosition = view * glm::vec4(
positions[triangleCount * 9 * 2 + initialPosition],
positions[triangleCount * 9 * 2 + initialPosition + 1],
positions[triangleCount * 9 * 2 + initialPosition + 2],
1.0f);
if (vertexPosition.x < minX) minX = vertexPosition.x;
if (vertexPosition.x > maxX) maxX = vertexPosition.x;
if (vertexPosition.y < minY) minY = vertexPosition.y;
if (vertexPosition.y > maxY) maxY = vertexPosition.y;
if (vertexPosition.z < minZ) minZ = vertexPosition.z;
if (vertexPosition.z > maxZ) maxZ = vertexPosition.z;
}
triangleCount++;
}
centreX = minX + (maxX - minX) / 2.0f;
centreY = minY + (maxY - minY) / 2.0f;
largestDimension = ((maxX - minX) >= (maxY - minY)) ? (maxX - minX) : (maxY - minY);
minX = centreX - largestDimension / 2.0f;
maxX = centreX + largestDimension / 2.0f;
minY = centreY - largestDimension / 2.0f;
maxY = centreY + largestDimension / 2.0f;
*proj = glm::ortho(minX, maxX, minY, maxY, -minZ, -maxZ);
}
}
I have also tried to use global variables in the shader but they seem to store values during processing a single vertex only.
I know the Fit View feature already works in different CAD-systems and am just wondering what approach would be the best.
Thanks.
After more deep research, I found the OpenGL thing which is called 'Transform Feedback'. The best practical manual and the answer to my question is here.
More information is here.
It will also be useful to read the book of Sam Buss '3D Computer Graphics: A mathematical approach with OpenGL.' It contains a lot of examples on using different OpenGL functions.
Related
I'm using this binary pack code. I'm using its GrowingPacker approach. It works fine, but I have a hard time figuring out the dimensions of the final pack, I mean the bounding box of final result like (xMin, xMax, yMin, yMax). What is the best approach to find it?
Can I use the final values of x, y, w, h for this.root to compute the bounding box?
I'm not sure if it is correct, but currently I'm using a loop like that loop to compute the bounding box of final pack (I'm using C++):
#include <cfloat> // For FLT_MAX
float minX = FLT_MAX;
float minY = FLT_MAX;
float maxW = - FLT_MAX;
float maxH = - FLT_MAX;
for (const Block &block : blocks) {
if (block.fit) {
if (block.fit->x < minX)
minX = block.fit->x;
if (block.fit->y < minY)
minY = block.fit->y;
if (block.fit->w > maxW)
maxW = block.fit->w;
if (block.fit->h > maxH)
maxH = block.fit->h;
}
}
To calculate the bounding box that encompasses all the bounding boxes in your list of blocks, you need find the minimum and maximum X and Y and then use that to calculate the width and height of the overall bounding box.
Calculating the maximum width and height of the smaller boxes won't work because that will be the size of the widest and highest of the individual smaller boxes, which won't necessarily be the dimensions of the overall bounding box that encompasses all of them.
float minX = FLT_MAX;
float minY = FLT_MAX;
float maxX = - FLT_MAX;
float maxY = - FLT_MAX;
for (const Block &block : blocks) {
if (block.fit) {
if (block.fit->x < minX)
minX = block.fit->x;
if (block.fit->y < minY)
minY = block.fit->y;
if (block.fit->x + block.fit->w > maxX)
maxX = block.fit->x + block.fit->w;
if (block.fit->y + block.fit->h > maxY)
maxY = block.fit->y + block.fit->h;
}
}
// now that you have the min/max of x and y, calculate overall w and h:
float w = maxX > minX ? maxX - minX : 0.0f;
float h = maxY > minY ? maxY - minY : 0.0f;
What I'm trying to achieve is a sprite moving to another sprite in a 2D environment. I started with the basic Mx = Ax - Bx deal. But I noticed that the closer to the target the sprite gets, the more it slows down. So I tried to create a percentage/ratio based on the velocity then each x and y gets their percent of a speed allowance, however, it's acting very strangely and only seems to work if Mx and My are positive
Here's the code extract:
ballX = ball->GetX();
ballY = ball->GetY();
targX = target->GetX();
targY = target->GetY();
ballVx = (targX - ballX);
ballVy = (targY - ballY);
percentComp = (100 / (ballVx + ballVy));
ballVx = (ballVx * percentComp)/10000;
ballVy = (ballVy * percentComp)/10000;
The /10000 is to slow the sprites movement
Assuming you want the sprite to move at a constant speed, you can do a linear fade on both the X and Y position, like this:
#include <stdio.h>
int main(int, char **)
{
float startX = 10.0f, startY = 20.0f;
float endX = 35.0f, endY = -2.5f;
int numSteps = 20;
for (int i=0; i<numSteps; i++)
{
float percentDone = ((float)i)/(numSteps-1);
float curX = (startX*(1.0f-percentDone)) + (endX*percentDone);
float curY = (startY*(1.0f-percentDone)) + (endY*percentDone);
printf("Step %i: percentDone=%f curX=%f curY=%f\n", i, percentDone, curX, curY);
}
return 0;
}
Thanks for the responses, I got it working now but normalising the vectors instead of the whole percent thing, here's what I have now:
ballX = ball->GetX();
ballY = ball->GetY();
targX = target->GetX();
targY = target->GetY();
ballVx = (targX - ballX);
ballVy = (targY - ballY);
vectLength = sqrt((ballVx*ballVx) + (ballVy*ballVy));
ballVx = (ballVx / vectLength)/10;
ballVy = (ballVy / vectLength)/10;
I'm working on some code to place isometric CCTMXTiledMap onto a CCLayerPanZoom control and then convert a touch location into ISO tilemap co-ordinates. This all works perfectly well for me, so long as the scale of the CClayerPanZoom is 1 (i.e. if I don't zoom in or zoom out). I can pan the map around and still calculate the correct iso tile co-oridinates. However, as soon as I zoom the tiled map in or out the iso cordinates returned by my code are completely wrong. Please see below for my code to calculate the iso co-ordinates from the touch location.
-(CGPoint) tilePosFromLocation:(CGPoint)location tileMap:(CCTMXTiledMap*)thisTileMap panZoom:(CCLayerPanZoom*)thisPanZoom
{
float midScale = (thisPanZoom.minScale + thisPanZoom.maxScale) / 2.0;
float newScale = (thisPanZoom.scale <= midScale) ? thisPanZoom.maxScale : thisPanZoom.minScale;
if (thisPanZoom.scale < 1)
{
newScale = newScale + thisPanZoom.scale;
}
else
{
newScale = newScale - thisPanZoom.scale;
}
CGFloat deltaX = (location.x - thisPanZoom.anchorPoint.x * (thisPanZoom.contentSize.width / CC_CONTENT_SCALE_FACTOR()) ) * (newScale);
CGFloat deltaY = (location.y - thisPanZoom.anchorPoint.y * (thisPanZoom.contentSize.height / CC_CONTENT_SCALE_FACTOR()) ) * (newScale);
CGPoint position = ccp((thisPanZoom.position.x - deltaX) , (thisPanZoom.position.y - deltaY) );
float halfMapWidth = thisTileMap.mapSize.width * 0.5f;
float mapHeight = thisTileMap.mapSize.height;
float tileWidth = thisTileMap.tileSize.width / CC_CONTENT_SCALE_FACTOR() * newScale;
float tileHeight = thisTileMap.tileSize.height / CC_CONTENT_SCALE_FACTOR() * newScale;
CGPoint tilePosDiv = CGPointMake(position.x / tileWidth, position.y / tileHeight );
float inverseTileY = tilePosDiv.y - (mapHeight * CC_CONTENT_SCALE_FACTOR()) * newScale; //mapHeight + tilePosDiv.y;
float posX = (int)(tilePosDiv.y - tilePosDiv.x + halfMapWidth);
float posY = (int)(inverseTileY + tilePosDiv.x - halfMapWidth + mapHeight);
// make sure coordinates are within isomap bounds
posX = MAX(0, posX);
posX = MIN(thisTileMap.mapSize.width - 1, posX);
posY = MAX(0, posY);
posY = MIN(thisTileMap.mapSize.height - 1, posY);
return CGPointMake(posX, posY);
}
Can anyone offer any insight into where I'm going wrong with this?
Thanks,
Alan
I have attempted to produce an algorithm that uses world coordinates and a bounding box structure to
detect collision between two bounding boxes. I really don't know what I'm doing, but I thought the code below would work. My issue is that it only detects collision if the bounding boxes are on the exact same x,y,z position.
BOOL AABB::isCollidedWith(AABB* bb)
{
if(bb == NULL) return FALSE;
float radX1,radX2;
float radY1,radY2;
float radZ1,radZ2;
float arr[12];
//please note that all the mins are set to 0
//and all the maxes are set to 1
radX1 = (bb->maxX - bb->minX) / 2;
radX2 = (this->maxX - this->minX) / 2;
radY1 = (bb->maxY - bb->minY) / 2;
radY2 = (this->maxY - this->minY) / 2;
radZ1 = (bb->maxZ - bb->minZ) / 2;
radZ2 = (this->maxZ - this->minZ) / 2;
//bb coords
arr[1] = bb->bbX - radX1;
arr[2] = bb->bbX + radX1;
arr[3] = bb->bbY - radY1;
arr[4] = bb->bbY + radY1;
arr[5] = bb->bbZ - radZ1;
arr[6] = bb->bbZ + radZ1;
//this coords
arr[7] = this->bbX - radX2;
arr[8] = this->bbX + radX2;
arr[9] = this->bbY - radY2;
arr[10] = this->bbY + radY2;
arr[11] = this->bbZ - radZ2;
arr[12] = this->bbZ + radZ2;
if(arr[2] >= arr[7] && arr[1] <= arr[8])
{
if(arr[4] >= arr[9] && arr[3] <= arr[10])
{
if(arr[6] >= arr[11] && arr[5] <= arr[12])
{
this->collided = TRUE;
OutputDebugStringA("Collided!\n");
return TRUE;
}
}
}
}
Structures I am comparing:
AABB* aabb1 = new AABB(0.0f,0.0f,0.0f,1.0f,1.0f,1.0f,0.0f,0.0f,0.0f);
AABB* aabb2 = new AABB(0.0f,0.0f,0.0f,1.0f,1.0f,1.0f,0.0f,0.0f,0.0f);
aabb2->isCollidedWith(aabb1);
Constructor snippet :
Also note that the last three parameters dictate the x,y,z cords of the bounding box
AABB::AABB(float minx,float maxx,float miny,float maxy,float minz,float maxz,float x,float y,float z)
{
this->minX = minx;
this->maxX = maxx;
this->minY = miny;
this->maxY = maxy;
this->minZ = minz;
this->maxZ = maxz;
Any help,criticism, or advice would help.
As you are creating the boxes with minX=0.0 and maxX=0.0, the bbX coordinate must be the same for the boxes to collide (because radX = 0). The same goes for minZ=maxZ=1.0.
Note the order of parameters in your constructor: it's minX, maxX, minY, maxY, minZ, maxZ and not minX, minY, minZ, maxX, maxY, maxZ (I guess you supposed the second order and wanted to define a box of 1.0 x 1.0 x 1.0 dimensions).
Simple Error! I ignored the way the parameters were listed, causing the issue.
Also, I had subtract 0.5 from each member of the array "arr" to find the center of AABB.
this is the formular but i dont know how to implement it. can someone please help
rectangle::rectangle() //rectangle constructor
{
bl.real() = 0; //bottom
bl.imag() = 0; //left
tr.real() = 1; //top
tr.imag() = 1; //right
}
complex<double> rectangle::get_bl() const
{
return bl;
}
complex<double> rectangle::get_tr() const
{
return tr;
}
void rectangle::rotate(double angle)
{
//not sure how to do it tr = tr.real() * cos(angle) + tr.imag() *cos(angle);
}
main
rectangle r;
r.rotate(90);
expected output (not 100% sure)
0 0 -1 1
Move your shape to (0, 0) temporarily (formula assumes you are rotating about origin, so move the bottom-left corner to (0, 0)).
Apply formula.
Move it back.
if (tr.real() < bl.real()) {
float tempX = tr.real() - bl.real();
float tempY = tr.imag() - bl.imag();
} else {
float tempX = bl.real() - tr.real();
float tempY = bl.imag() - tr.imag();
}
tr.real() = tempX * cos(theta) - tempY * sin(theta)
tr.imag() = tempx * sin(theta) + tempY * cos(theta)
The formula is basically saying:
new_x = shape.point[i].x*cos(angle) - shape.point[i].y*sin(angle)
new_y = shape.point[i].x*sin(angle) + shape.point[i].y*cos(angle)
shape.point[i].x = new_x
shape.point[i].y = new_y
angle is in radians, to convert from degrees to radians use
degree*pi/180 where pi is the constant 3.14...
you will need to do this for each point on the shape to fully rotate the shape by the desired degree.
This formula also assumes that the points are centered around (0,0), i.e. the center of the shape is (0,0) and all points are relative to that center.
One tip, if applicable, try and store shapes as points, going clockwise from the 0th point. for instance, this rectangle will be:
point[0] = {-1, 1}
point[1] = { 1, 1}
point[2] = { 1,-1}
point[3] = {-1,-1}
To convert from tl, br to points you will need to do something similar to:
point[0] = {tl.x, tl.y}
point[1] = {br.x, tl.y}
point[2] = {br.x, br.y}
point[3] = {tl.x, br.y}