I want to find normals for height map data. I am using gl_triangles in my code for indices. How would I find normals for this?
Given a triangle (vert1, vert2, vert3) its normal is ((vert2 - vert1).cross(vert3 - vert1)).normalize().
For smooth, per-vertex normals: Foreach vertex, sum together the face normals for each triangle that vertex is a part of, then normalize the sum.
EDIT: Example:
#include <GL/glut.h>
#include <vector>
#include <cmath>
#include <Eigen/Core>
#include <Eigen/Geometry>
using namespace std;
using namespace Eigen;
typedef Matrix< Vector3f, Dynamic, Dynamic > VecMat;
// given a matrix of heights returns a matrix of vertices
VecMat GetVerts( const MatrixXf& hm )
{
VecMat verts( hm.rows(), hm.cols() );
for( int col = 0; col < hm.cols(); ++col )
for( int row = 0; row < hm.rows(); ++row )
verts( row, col ) = Vector3f( col, row, hm( row, col ) );
return verts;
}
VecMat GetNormals( const VecMat& hm )
{
VecMat normals( hm );
for( int col = 0; col < hm.cols(); ++col )
for( int row = 0; row < hm.rows(); ++row )
{
Vector3f sum( Vector3f::Zero() );
const Vector3f& cur = hm( row, col );
if( row+1 < hm.rows() && col+1 < hm.cols() )
sum += ( hm( row+0, col+1 ) - cur ).cross( hm( row+1, col+0 ) - cur ).normalized();
if( row+1 < hm.rows() && col > 0 )
sum += ( hm( row+1, col+0 ) - cur ).cross( hm( row+0, col-1 ) - cur ).normalized();
if( row > 0 && col > 0 )
sum += ( hm( row+0, col-1 ) - cur ).cross( hm( row-1, col+0 ) - cur ).normalized();
if( row > 0 && col+1 < hm.cols() )
sum += ( hm( row-1, col+0 ) - cur ).cross( hm( row+0, col+1 ) - cur ).normalized();
normals( row, col ) = sum.normalized();
}
return normals;
}
// returns an index array for a GL_TRIANGLES heightmap
vector< unsigned int > GetIndices( int rows, int cols )
{
vector< unsigned int > indices;
for( int col = 1; col < cols; ++col )
for( int row = 1; row < rows; ++row )
{
// Eigen default storage order is column-major
// lower triangle
indices.push_back( (col-1) * rows + (row-1) );
indices.push_back( (col-0) * rows + (row-1) );
indices.push_back( (col-1) * rows + (row-0) );
// upper triangle
indices.push_back( (col-1) * rows + (row-0) );
indices.push_back( (col-0) * rows + (row-1) );
indices.push_back( (col-0) * rows + (row-0) );
}
return indices;
}
VecMat heightmap;
VecMat normals;
vector< unsigned int > indices;
void init()
{
// wavy heightmap
MatrixXf hm( 64, 64 );
for( int col = 1; col < hm.cols(); ++col )
for( int row = 1; row < hm.rows(); ++row )
{
float x = ( col - ( hm.cols() / 2.0f ) ) / 2.0f;
float y = ( row - ( hm.rows() / 2.0f ) ) / 2.0f;
hm( row, col ) = cos( sqrt( x * x + y * y ) );
}
heightmap = GetVerts( hm );
heightmap.array() -= Vector3f( hm.cols() / 2.0f, hm.rows() / 2.0f, 0 );
for( int col = 0; col < hm.cols(); ++col )
for( int row = 0; row < hm.rows(); ++row )
heightmap( row, col ).array() *= Vector3f( 1 / 4.0f, 1 / 4.0f, 1.0f ).array();
normals = GetNormals( heightmap );
indices = GetIndices( heightmap.rows(), heightmap.cols() );
}
void display()
{
glEnable( GL_DEPTH_TEST );
glEnable( GL_CULL_FACE );
glShadeModel( GL_SMOOTH );
glEnable( GL_LIGHTING );
GLfloat global_ambient[] = { 0.0, 0.0, 0.0, 1.0 };
glLightModelfv( GL_LIGHT_MODEL_AMBIENT, global_ambient );
glEnable( GL_COLOR_MATERIAL );
glColorMaterial( GL_FRONT, GL_AMBIENT_AND_DIFFUSE );
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
glMatrixMode( GL_PROJECTION );
glLoadIdentity();
double w = glutGet( GLUT_WINDOW_WIDTH );
double h = glutGet( GLUT_WINDOW_HEIGHT );
gluPerspective( 60, w / h, 1, 100 );
glMatrixMode( GL_MODELVIEW );
glLoadIdentity();
gluLookAt( 8, 8, 8, 0, 0, 0, 0, 0, 1 );
// spinning light
glEnable( GL_LIGHT0 );
float angle = 20 * ( glutGet( GLUT_ELAPSED_TIME ) / 1000.0f ) * (3.14159f / 180.0f);
float x = cos( -angle ) * 6;
float y = sin( -angle ) * 6;
GLfloat light_position[] = { x, y, 2, 1.0 };
glLightfv( GL_LIGHT0, GL_POSITION, light_position );
glDisable( GL_LIGHTING );
glPointSize( 5 );
glBegin(GL_POINTS);
glColor3ub( 255, 255, 255 );
glVertex3fv( light_position );
glEnd();
glEnable( GL_LIGHTING );
glColor3ub(255,0,0);
glEnableClientState( GL_VERTEX_ARRAY );
glEnableClientState( GL_NORMAL_ARRAY );
glVertexPointer( 3, GL_FLOAT, sizeof( Vector3f ), heightmap(0,0).data() );
glNormalPointer( GL_FLOAT, sizeof( Vector3f ), normals(0,0).data() );
glDrawElements( GL_TRIANGLES, indices.size(), GL_UNSIGNED_INT, &indices[0] );
glDisableClientState( GL_VERTEX_ARRAY );
glDisableClientState( GL_NORMAL_ARRAY );
glutSwapBuffers();
}
void timer( int extra )
{
glutPostRedisplay();
glutTimerFunc( 16, timer, 0 );
}
int main( int argc, char **argv )
{
glutInit( &argc, argv );
glutInitDisplayMode( GLUT_RGBA | GLUT_DEPTH | GLUT_DOUBLE );
glutInitWindowSize( 640, 480 );
glutCreateWindow( "Heightmap" );
init();
glutDisplayFunc( display );
glutTimerFunc( 0, timer, 0 );
glutMainLoop();
return 0;
}
Related
I am asking for some help with drawing a flat terrain using triangle strips. If this is the correct way to do it?
So far i managed to create vertex and index arrays, debugged it couple of times and it seems to generate correct values.
At first I tried drawing with degenerate triangles, but I read somewhere that it's an outdated way and I should rather switch to a primitive restart option.
NOTE THAT THIS NOT THE EXACT WAY MY PROJECT IS SET UP... I just copied the necessery code
Here is the code for terrain generation:
this->terrainSize = width * height;
unsigned int numRows = height - 1; //!NUMBER OF ROWS OF TRIANGLE STRIPS
unsigned int numColumns = width - 1; //!Number of columns of strips
unsigned int numVerticiesPerRow = 2 * width; //!NUMBER OF VERTICIES PER EVERY ROW
unsigned int numIndPerRow = numColumns * 2 + 2; //!Number of indices per row
unsigned int numOfStrips = numRows - 1; //!Number of splits, after every row
this->numVertices = terrainSize * 8;
this->numIndices = numRows * numIndPerRow + numOfStrips; //!total number of indices
std::vector<float> vertexArray (numVertices);
float* vertexPointer = vertexArray.data();
float* vertexPointer2 = vertexArray.data();
for( int row = 0; row < height; row++ ) //!Z AXIS
{
for( int col = 0; col < width; col++ ) //!X AXIS
{
float x = (float) ( col - width / 2 );
float z = (float) ( row - height / 2 );
//?Positions
*vertexPointer = x; ++vertexPointer; //?1
*vertexPointer = 0.0f; ++vertexPointer; //?2
*vertexPointer = z; ++vertexPointer; //?3
//?Normals (lightning)
*vertexPointer = 0.0f; ++vertexPointer; //?4
*vertexPointer = 1.0f; ++vertexPointer; //?5
*vertexPointer = 0.0f; ++vertexPointer; //?6
//?Texture Coordinates
*vertexPointer = x; ++vertexPointer; //?7
*vertexPointer = z; ++vertexPointer; //?8
}
}
//?GENERATE INDICIES
std::vector<unsigned int> indexArray (numIndices);
unsigned int* indexPointer = indexArray.data();
unsigned int* indexPointer2 = indexArray.data();
for( int row = 0; row < height - 1; row++ ) //!Z AXIS
{
for( int col = 0; col < width; col++ ) //!X AXIS
{
*indexPointer = ( row * height ) + col; ++indexPointer;
*indexPointer = (( row + 1 )* height ) + col; ++indexPointer;
}
if( row < height - 2 ) **//!Add 0xFFFF on the end of each row**
{
*indexPointer = 0xFFFF; ++indexPointer;
}
}
//Pack the data into vertex struct
unsigned int i;
for( i = 0; i < this->numVertices; i=i+0 )
{
Vertex vertex;
glm::vec3 vector;
vector.x = vertexArray[i++];
vector.y = vertexArray[i++];
vector.z = vertexArray[i++];
vertex.Position = vector;
vector.x = vertexArray[i++];
vector.y = vertexArray[i++];
vector.z = vertexArray[i++];
vertex.Normal = vector;
glm::vec2 vec;
vec.x = vertexArray[i++];
vec.y = vertexArray[i++];
vertex.TexCords = vec;
this->vertexStruct.push_back( vertex );
}
for( i = 0; i < this->numIndices; i++ )
{
this->indexVector.push_back( indexArray[i] );
}
//Bind VertexArray
VAO.Create();
//Bind VertexBuffer
VBO.Create( vertexArray, numVertices);
//Bind IndexBuffer
IBO.Create( indexArray, numIndices );
//? DEFINE HOW SHOULD THE GPU SHOULD READ THE DATA FROM VERTEX BUFFER
//Position
glVertexAttribPointer( 0, 3, GL_FLOAT, GL_FALSE, sizeof( Vertex ), (GLvoid*) offsetof( Vertex, Position ) );
glEnableVertexAttribArray( 0 );
//Normal
glVertexAttribPointer( 1, 3, GL_FLOAT, GL_FALSE, sizeof( Vertex ), (GLvoid*) offsetof( Vertex, Normal ) );
glEnableVertexAttribArray( 1 );
//Texcoord
glVertexAttribPointer( 2, 2, GL_FLOAT, GL_FALSE, sizeof( Vertex ), (GLvoid*) offsetof( Vertex, TexCords ) );
glEnableVertexAttribArray( 2 );
glPolygonMode( GL_FRONT_AND_BACK, GL_LINE ); //?ENABLE WIREFRAME
glEnable( GL_PRIMITIVE_RESTART );
glPrimitiveRestartIndex( 0xFFFF );
glDrawElements( GL_TRIANGLE_STRIP, this->numVertices, GL_UNSIGNED_INT, this->indexArray );
glPolygonMode( GL_FRONT_AND_BACK, GL_FILL ); //?DISABLE WIREFRAME
I am trying to call a function, only when "spacebar" is pressed. Its key code is 32.
#include <GL/glut.h>
#include <GL/glu.h>
#include <GL/gl.h>
#include <cmath>
#include <iostream>
#include <string.h>
using namespace std;
GLuint window, View1;
void Display_Mid_Ellipse()
{
double xc=0.0, yc=0.0, rx=140.0, ry=200.0;
double rxSq = rx * rx;
double rySq = ry * ry;
double x = 0, y = ry, p;
double px = 0, py = 2 * rxSq * y;
glClear(GL_COLOR_BUFFER_BIT);
glColor3f( 1 ,0, 0);
glBegin(GL_POINTS);
glVertex2d(xc+x,yc-y);
glVertex2d(xc-x,yc-y);
glVertex2d(xc-x,yc+y);
glEnd();
p = rySq - (rxSq * ry) + (0.25 * rxSq);
while (px < py)
{
x++;
px = px + 2 * rySq;
if (p < 0)
p = p + rySq + px;
else
{
y--;
py = py - 2 * rxSq;
p = p + rySq + px - py;
}
glBegin(GL_POINTS);
glVertex2d(xc+x,yc+y);
glVertex2d(xc+x,yc-y);
glVertex2d(xc-x,yc-y);
glVertex2d(xc-x,yc+y);
glEnd();
}
p = rySq*(x+0.5)*(x+0.5) + rxSq*(y-1)*(y-1) - rxSq*rySq;
while (y > 0)
{
y--;
py = py - 2 * rxSq;
if (p > 0)
p = p + rxSq - py;
else
{
x++;
px = px + 2 * rySq;
p = p + rxSq - py + px;
}
glBegin(GL_POINTS);
glVertex2d(xc+x,yc+y);
glVertex2d(xc+x,yc-y);
glVertex2d(xc-x,yc-y);
glVertex2d(xc-x,yc+y);
glEnd();
glFlush();
}
} //end_MIDPOINT_ELLIPSE
void init () {
glClear(GL_COLOR_BUFFER_BIT);
glClearColor(0.3,0.3,0.3,0.7);
gluOrtho2D(-500.0,500.0,-500.0,500.0);
}
void View1Display()
{
glutSetWindow(View1);
gluOrtho2D(-500.0,500.0,-500.0,500.0);
Display_Mid_Ellipse();
}
void Display()
{
glutSetWindow(window);
glClear(GL_COLOR_BUFFER_BIT);
//View1Display();
}
void keyFunc(unsigned char ch, int x, int y){
if(ch==32){
View1Display();
}
}
int main (int argc, char *argv[]) {
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
glutInitWindowSize(750, 750);
glutInitWindowPosition(0, 0);
window=glutCreateWindow("Circle and ellipse");
init();
View1 = glutCreateSubWindow(window,20,40, 340,340);
glutDisplayFunc(Display);
glutKeyboardFunc(keyFunc);
glutMainLoop();
return 0;
}
When i call the same View1Display from the Display() function, it works well.
I'm confused. I have tried the alternatives I could but could not find a solution. Please help.
Thanks
Callbacks are registered for the active window (creating a window implicitly activates it) so you can register separate display callbacks for the toplevel window and your subwindow.
Don't call your subwindow display function from the keyboard callback. Toggle a shared variable & use glutPostWindowRedisplay() to force a redraw instead.
If you're going to use GLUT_SINGLE make sure to glFlush() to force updates out to the framebuffer.
All together:
#include <GL/glut.h>
#include <cmath>
using namespace std;
void Display_Mid_Ellipse()
{
double xc = 0.0, yc = 0.0, rx = 140.0, ry = 200.0;
double rxSq = rx * rx;
double rySq = ry * ry;
double x = 0, y = ry, p;
double px = 0, py = 2 * rxSq * y;
glClear( GL_COLOR_BUFFER_BIT );
glColor3f( 1, 0, 0 );
glBegin( GL_POINTS );
glVertex2d( xc + x, yc - y );
glVertex2d( xc - x, yc - y );
glVertex2d( xc - x, yc + y );
glEnd();
p = rySq - ( rxSq * ry ) + ( 0.25 * rxSq );
while( px < py )
{
x++;
px = px + 2 * rySq;
if( p < 0 )
p = p + rySq + px;
else
{
y--;
py = py - 2 * rxSq;
p = p + rySq + px - py;
}
glBegin( GL_POINTS );
glVertex2d( xc + x, yc + y );
glVertex2d( xc + x, yc - y );
glVertex2d( xc - x, yc - y );
glVertex2d( xc - x, yc + y );
glEnd();
}
p = rySq*( x + 0.5 )*( x + 0.5 ) + rxSq*( y - 1 )*( y - 1 ) - rxSq*rySq;
while( y > 0 )
{
y--;
py = py - 2 * rxSq;
if( p > 0 )
p = p + rxSq - py;
else
{
x++;
px = px + 2 * rySq;
p = p + rxSq - py + px;
}
glBegin( GL_POINTS );
glVertex2d( xc + x, yc + y );
glVertex2d( xc + x, yc - y );
glVertex2d( xc - x, yc - y );
glVertex2d( xc - x, yc + y );
glEnd();
glFlush();
}
} //end_MIDPOINT_ELLIPSE
bool displayEllipse = false;
void View1Display()
{
glClearColor( 0.3, 0.3, 0.3, 0.7 );
glClear( GL_COLOR_BUFFER_BIT );
glMatrixMode( GL_PROJECTION );
glLoadIdentity();
gluOrtho2D( -500.0, 500.0, -500.0, 500.0 );
glMatrixMode( GL_MODELVIEW );
glLoadIdentity();
if( displayEllipse )
Display_Mid_Ellipse();
glFlush();
}
void Display()
{
glClearColor( 0.0, 0.0, 0.0, 1.0 );
glClear( GL_COLOR_BUFFER_BIT );
glFlush();
}
GLuint window;
GLuint View1;
void keyFunc( unsigned char ch, int x, int y )
{
if( ch == 32 )
{
displayEllipse = !displayEllipse;
glutPostWindowRedisplay( View1 );
}
}
int main( int argc, char *argv[] )
{
glutInit( &argc, argv );
glutInitDisplayMode( GLUT_SINGLE | GLUT_RGB );
glutInitWindowSize( 750, 750 );
glutInitWindowPosition( 0, 0 );
window = glutCreateWindow( "Circle and ellipse" );
glutDisplayFunc( Display );
glutKeyboardFunc( keyFunc );
View1 = glutCreateSubWindow( window, 20, 40, 340, 340 );
glutDisplayFunc( View1Display );
glutMainLoop();
return 0;
}
I try in 3D but i am a beginner, so i try do with 2D first and value of z = 0. I have an array of points with values random in array points[] using std::vector. I have functions Distance(...) and CaculateF(...) to caculate new value for points[] and store in array pnew[]. I need draw points[] and move them to the value of pnew[], but i only know drawing random points in array points[] first, i can't move them exactly to values in pnew[]. Can anybody help me?!
#include<stdlib.h>
#include<glut.h>
#include<iostream>
#include<conio.h>
#include<math.h>
#include<omp.h>
#include<time.h>
#include<Windows.h>
#include<vector>
using namespace std;
struct Point
{
float x, y , z;
float vx, vy, vz;
unsigned long m;
unsigned char r, g, b, a;
};
vector< Point > points, pnew;
void animation_points( int value )
{
// move all points left each frame
for( size_t i = 0; i < points.size(); ++i )
{
points[i].x -= 1;
// wrap point around if it's moved off
// the edge of our 100x100 area
if( points[i].x < -50 )
{
points[i].x = 100 + points[i].x;
}
}
glutPostRedisplay();
glutTimerFunc(30, animation_points, 1);
}
void display(void)
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(-50, 50, -50, 50, -1, 1);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
// draw
glColor3ub( 255, 255, 255 );
glEnableClientState( GL_VERTEX_ARRAY );
glEnableClientState( GL_COLOR_ARRAY );
glVertexPointer( 2, GL_FLOAT, sizeof(Point), &points[0].x );
glColorPointer( 4, GL_UNSIGNED_BYTE, sizeof(Point), &points[0].r );
glPointSize( 3.0 );
glDrawArrays( GL_POINTS, 0, points.size() );
glDisableClientState( GL_VERTEX_ARRAY );
glDisableClientState( GL_COLOR_ARRAY );
glFlush();
glutSwapBuffers();
}
void reshape(int w, int h)
{
glViewport(0, 0, w, h);
}
//Distance between i and j
float Distance(float x1,float y1, float z1, float x2, float y2, float z2)
{
return (sqrt(pow(x1-x2,2) + pow(y1-y2,2) + pow(z1-z2,2)));
}
//Value of F on Ox, Oy, Oz
Point CalculateF(double d, Point a, Point b, int dt)
{
Point F;
float vnewx, vnewy, vnewz, xnew , ynew, znew;
float G = 6.6742*pow(10,-11);
float Fx = (G*a.m*b.m/pow(d,2)*(a.x-b.x)/d);
float Fy = (G*a.m*b.m/pow(d,2)*(a.y-b.y)/d);
float Fz = (G*a.m*b.m/pow(d,2)*(a.z-b.z)/d);
vnewx = a.vx + Fx*dt/a.m;
vnewy = a.vy + Fy*dt/a.m;
vnewz = a.vz + Fz*dt/a.m;
xnew = a.x + a.x*dt;
ynew = a.y + a.y*dt;
znew = a.z + a.z*dt;
F.x = xnew;
F.y = ynew;
F.z = znew;
F.vx = vnewx;
F.vy = vnewy;
F.vz = vnewz;
F.m = a.m;
return F;
}
int main(int argc, char **argv)
{
// begin
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGBA | GLUT_DEPTH | GLUT_DOUBLE);
glutInitWindowSize(640,480);
glutCreateWindow("N - body");
glutDisplayFunc(display);
glutReshapeFunc(reshape);
// animation points
//glutTimerFunc(30, animation_points, 1);
////
int n, t;
cout<<"\n Number of body: ";
cin>>n;
// move after time t
cout<<"\n\n Time: ";
cin>>t;
t *= 3600;
// random points
for( int i = 0; i < n; ++i )
{
Point pt;
pt.x = -50 + (rand() % 100);
pt.y = -50 + (rand() % 100);
pt.z = 0;
pt.r = rand() % 255;
pt.g = rand() % 255;
pt.b = rand() % 255;
pt.a = 255;
points.push_back(pt);
}
glutMainLoop();
float d;
//#pragma omp parallel default(shared) private(i,j)
for (int i = 0 ; i < n ; i++)
{
//#pragma omp for schedule(static)
for (int j = 0 ; j < n ; j++)
{
d = Distance(points[i].x, points[i].y,points[i].z, points[j].x, points[j].y, points[j].z);
if (d!=0)
points[i] = CalculateF(d,points[i], points[j], t);
}
pnew.push_back(points[i]);
}
return 0;
}
You need to store the initial and the target positions of your points in arrays, then interpolate between them in the rendering code. To do that, you determine how much time has passed, compute a double lambda in the range 0.0 to 1.0 from the time, then draw the points at the position p_start + lambda * (p_target - p_start).
I'm implementing video capture rotation with bilinear interpolation like warpAffine() does in OpenCV library. But so far I have got some problems:
1.I'm getting some artifacts during rotations. Here are samples of border, 90 rotation and 360 degrees artifacts
https://www.dropbox.com/sh/oe51ty0cy695i3o/hcAzwmAk6z
2.I can't change resolution of my capture using
capture.set(CV_CAP_PROP_FRAME_WIDTH, 1280 )
capture.set(CV_CAP_PROP_FRAME_HEIGHT, 720 )
Both of them return false value.
I use LifeCam Cinema.
Here is my code:
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <cmath>
#include <ctime>
#include <iostream>
#include <cstdlib>
using namespace cv;
using namespace std;
const double M_PI = 3.14159265359;
void print_help( const char* prg );
Mat rotate( Mat& in, int angle , Point2f rotationCenter );
inline uchar int2uchar( int color ) {
return (uchar)( color < 0 ? 0 : color > 255 ? 255 : color );
}
void print_help( const char* prg ) {
cout << "Report:" << endl;
cout << "Application : " << prg << endl;
cout << "Can't access capture device" << endl;
}
// rotation with bilinear interpolation
Mat rotate( Mat& in, int angle , Point2f rotationCenter ) {
// Note : added Scalar(0) for unused pixels to be black
Mat out( in.size(), in.type(), Scalar(0) );
float in_radians = (float)( angle * M_PI / 180 );
float sinAngle = (float)( sin( in_radians ) );
float cosAngle = (float)( cos( in_radians ) );
for ( int col(0); col < in.cols; ++col ) {
for ( int row(0); row < in.rows; ++row ) {
// already around rotationCenter
// x' = x * cos(angle) - y * sin(angle)
float temp_columns( ( col - rotationCenter.x ) * (cosAngle) -
( row - rotationCenter.y ) * (sinAngle) +
rotationCenter.x );
// y' = x * sin(angle) + y * cos(angle)
float temp_rows ( ( col - rotationCenter.x ) * (sinAngle) +
( row - rotationCenter.y ) * (cosAngle) +
rotationCenter.y );
float max_col( ceil (temp_columns) );
float min_col( floor(temp_columns) );
float max_row( ceil (temp_rows) );
float min_row( floor(temp_rows) );
// clip all irrelevant parts
if ( max_col >= in.cols || max_row >= in.rows ||
min_col < 0 || min_row < 0 ) {
// don't draw
continue;
}
float deltaCol( temp_columns - min_col );
float deltaRow( temp_rows - min_row );
// left top, right top, left bottom and right bottom
Vec3b q12( in.at < Vec3b >( (int)min_row, (int)min_col ) );
Vec3b q22( in.at < Vec3b >( (int)min_row, (int)max_col ) );
Vec3b q11( in.at < Vec3b >( (int)max_row, (int)min_col ) );
Vec3b q21( in.at < Vec3b >( (int)max_row, (int)max_col ) );
// R1 - linear interpolation of bottom neighborhoods
double blueR1 ( ( 1 - deltaCol ) * q11[0] + deltaCol * q21[0] );
double greenR1 ( ( 1 - deltaCol ) * q11[1] + deltaCol * q21[1] );
double redR1 ( ( 1 - deltaCol ) * q11[2] + deltaCol * q21[2] );
// R2 - linear interpolation of top neighborhoods
double blueR2 ( ( 1 - deltaCol ) * q12[0] + deltaCol * q22[0] );
double greenR2 ( ( 1 - deltaCol ) * q12[1] + deltaCol * q22[1] );
double redR2 ( ( 1 - deltaCol ) * q12[2] + deltaCol * q22[2] );
// P - linear interpolation of R1 and R2
int blue ( (int)ceil( ( 1 - deltaRow ) * blueR2 + deltaRow * blueR1 ) );
int green( (int)ceil( ( 1 - deltaRow ) * greenR2 + deltaRow * greenR1 ) );
int red ( (int)ceil( ( 1 - deltaRow ) * redR2 + deltaRow * redR1 ) );
// Vec3b stands for 3-channel value, each channel is a byte
out.at < Vec3b >( row, col )[ 0 ] = int2uchar(blue);
out.at < Vec3b >( row, col )[ 1 ] = int2uchar(green);
out.at < Vec3b >( row, col )[ 2 ] = int2uchar(red);
}
}
return out;
}
int main( int ac, char ** av ) {
if ( ac < 2 ) {
print_help( av[ 0 ] );
return -1;
}
// In degrees
int step = 1, angle = 90;
VideoCapture capture;
// doesn't work properly
if ( capture.set(CV_CAP_PROP_FRAME_WIDTH, 1280 ) &&
capture.set(CV_CAP_PROP_FRAME_HEIGHT, 720 ) ) {
cout << "Resolution : "
<< capture.get(CV_CAP_PROP_FRAME_WIDTH )
<< " x "
<< capture.get(CV_CAP_PROP_FRAME_HEIGHT )
<< endl;
} else {
cout << "There's some problem with VideoCapture::set()" << endl;
}
capture.open( atoi( av[ 1 ] ) );
while ( !capture.isOpened( ) ) {
print_help( av[ 0 ] );
cout << "Capture device " << atoi( av[ 1 ] ) << " failed to open!" << endl;
cout << "Connect capture device to PC\a" << endl;
system("pause");
cout << endl;
capture.open( atoi( av[ 1 ] ) );
}
cout << "Device " << atoi( av[ 1 ] ) << " is connected" << endl;
string original("Original");
string withInterpolation("With Bilinear Interpolation");
namedWindow( original, CV_WINDOW_AUTOSIZE );
namedWindow( withInterpolation, CV_WINDOW_AUTOSIZE);
Mat frame;
for ( ;; ) {
capture >> frame;
if ( frame.empty( ) )
break;
createTrackbar("Rotate", withInterpolation, &angle, 360, 0);
imshow( original, frame );
char key = ( char ) waitKey( 2 );
switch ( key ) {
case '+':
angle += step;
break;
case '-':
angle -= step;
break;
case 27:
case 'q':
return 0;
break;
}
Mat result;
Point2f rotationCenter( (float)( frame.cols / 2.0 ),
(float)( frame.rows / 2.0 ) );
result = rotate( frame, angle, rotationCenter );
// Note : mirror effect
// 1 says, that given frame will be flipped horizontally
flip(result,result, 1);
imshow( withInterpolation, result );
// test to compare my bilinear interpolation and of OpenCV
Mat temp;
warpAffine( frame, temp,
getRotationMatrix2D( rotationCenter, angle, (double)(1.0) ),
frame.size(), 1, 0 );
string openCVInterpolation("OpenCV Bilinear Interpolation");
namedWindow( openCVInterpolation, CV_WINDOW_AUTOSIZE );
createTrackbar("Rotate", openCVInterpolation, &angle, 360, 0);
flip(temp,temp, 1);
imshow( openCVInterpolation, temp );
}
return 0;
}
Addressing your second issue - setting Lifecam resolution using OpenCV
I found that the Lifecam dashboard was perhaps interfering with OpenCV Videocapture calls. If you uninstall Lifecam using Programs & Features from the control panel, the calls
capture.set(CV_CAP_PROP_FRAME_WIDTH, 1280)
capture.set(CV_CAP_PROP_FRAME_HEIGHT, 720)
will work fine.
I have been trying to implement 2D tile based water into my game. I started out making the tiles appear on screen etc. I have a drawing function that draws each tile type.
The problem I'm having is that when I call this function the Tiles that are water don't change position. Which makes me believe that this code isn't functioning properly. This code is called on every loop. This should update the masses of all the water tiles. For some reason nothing is happening. The water is staying in its original positions.
My tiles are in a vector of the tile class is just
Tiles()
{
TileProp // the type of tile (GROUND,AIR,WATER)
Mass
NewMass
}
void App::SimulateCompression()
{
float Flow = 0;
float remainingmass = 0;
int ID = 0;
//Calculate and apply flow for each block
for (int X = 0; X < MAP_WIDTH; X++)
{
for(int Y = 0; Y < MAP_HEIGHT; Y++)
{
//Skip inert ground blocks
if(TileList[ID].TileProp == TILE_GROUND) continue;
//Custom push-only flow
Flow = 0;
remainingmass = TileList[ID].Mass;
if(remainingmass <= 0) continue;
//The block below this one
if(TileList[Rect2Lin(TILE_SIZE,X,(Y-1))].TileProp != TILE_GROUND)
{
Flow = GetStableWaterState(remainingmass + TileList[Rect2Lin(TILE_SIZE,X,(Y-1))].Mass /*mass[x][y-1]*/) - TileList[Rect2Lin(TILE_SIZE,X,(Y-1))].Mass;
if(Flow > MinFlow){Flow *= 0.5; /*leads to smoother flow*/}
int tempA = Min(MaxSpeed, remainingmass);
if(Flow > tempA){Flow = tempA;}
if(Flow < 0){Flow = 0;}
TileList[ID].NewMass -= Flow;
TileList[Rect2Lin(TILE_SIZE,X,(Y-1))].NewMass += Flow;
remainingmass -= Flow;
}
if(remainingmass <= 0) continue;
//Left
if(TileList[Rect2Lin(TILE_SIZE,(X - 1),Y)].TileProp != TILE_GROUND)
{
//Equalize the amount of water in this block and it's neighbour
Flow = (TileList[ID].Mass - TileList[Rect2Lin(TILE_SIZE,(X - 1),Y)].Mass)/4;
if(Flow > MinFlow){Flow *= 0.5;}
if(Flow > remainingmass){Flow = remainingmass;}
if(Flow < 0){Flow = 0;}
TileList[ID].NewMass -= Flow;
TileList[Rect2Lin(TILE_SIZE,(X - 1),Y)].NewMass += Flow;
remainingmass -= Flow;
}
if(remainingmass <= 0) continue;
//Right
if(TileList[Rect2Lin(TILE_SIZE,(X + 1),Y)].TileProp != TILE_GROUND)
{
//Equalize the amount of water in this block and it's neighbour
Flow = (TileList[ID].Mass - TileList[Rect2Lin(TILE_SIZE,(X + 1),Y)].Mass)/4;
if(Flow > MinFlow){Flow *= 0.5;}
if(Flow > remainingmass){Flow = remainingmass;}
if(Flow < 0){Flow = 0;}
TileList[ID].NewMass -= Flow;
TileList[Rect2Lin(TILE_SIZE,(X + 1),Y)].NewMass += Flow;
remainingmass -= Flow;
}
if(remainingmass <= 0) continue;
//Up. Only compressed water flows upwards
if(TileList[Rect2Lin(TILE_SIZE,X,(Y + 1))].TileProp != TILE_GROUND)
{
Flow = remainingmass - GetStableWaterState(remainingmass + TileList[Rect2Lin(TILE_SIZE,X,(Y + 1))].Mass);
if (Flow > MinFlow){Flow *= 0.5;}
int tempB = Min(MaxSpeed, remainingmass);
if(Flow > tempB){Flow = tempB;}
if(Flow < 0){Flow = 0;}
TileList[ID].NewMass -= Flow;
TileList[Rect2Lin(TILE_SIZE,X,(Y + 1))].NewMass += Flow;
remainingmass -= Flow;
}
ID++;
}
}
ID = 0;
//Copy the new mass values
for (int X = 0; X < MAP_WIDTH; X++)
{
for (int Y = 0; Y < MAP_HEIGHT; Y++)
{
TileList[ID].Mass = TileList[ID].NewMass;
ID++;
}
}
ID = 0;
for(int X = 0; X < MAP_WIDTH; X++)
{
for(int Y = 0; Y < MAP_HEIGHT; Y++)
{
//Skip ground blocks
if(TileList[ID].TileProp == TILE_GROUND) continue;
//Flag/unflag water blocks
if(TileList[ID].Mass > MinMass)
{
TileList[ID].TileProp = TILE_WATER;
}else
{
TileList[ID].TileProp = TILE_AIR;
}
ID++;
}
}
//Remove any water that has left the map
for(int X = 0; X < MAP_WIDTH; X++)
{
TileList[X].Mass = 0;
TileList[Rect2Lin(TILE_SIZE,X,MAP_HEIGHT - 1)].Mass = 0;
}
for(int Y = 0; Y < MAP_HEIGHT; Y++)
{
TileList[Rect2Lin(TILE_SIZE,0,Y)].Mass = 0;
TileList[Rect2Lin(TILE_SIZE,(MAP_WIDTH - 1),Y)].Mass = 0;
}
}
Ok, so ID is going to 0 because after ID hits 34 It just leaves the two nested for loops... Why would it do that?
//Calculate and apply flow for each block
for (int X = 0; X < MAP_WIDTH; X++)
{
for(int Y = 0; Y < MAP_HEIGHT; Y++)
{
//Skip inert ground blocks
if(TileList[ID].TileProp == TILE_GROUND) continue;
...
ID++;
}
}
TileList[34] is probably a ground tile. At which point you hit that first if over and over (since you never get to the ID++ at the very end of the loop) until you have exhausted the for-loops.
Try this:
//Calculate and apply flow for each block
for (int X = 0; X < MAP_WIDTH; X++)
{
for(int Y = 0; Y < MAP_HEIGHT; Y++)
{
int ID = Rect2Lin(TILE_SIZE,X,Y));
//Skip inert ground blocks
if(TileList[ID].TileProp == TILE_GROUND) continue;
...
}
}
EDIT:
Ok, this works on my system:
#include <GL/glut.h>
#include <vector>
using namespace std;
// simple Eigen::Matrix work-alike
template< typename T >
class Matrix
{
public:
Matrix( const size_t rows, const size_t cols )
: mStride( cols )
, mHeight( rows )
, mStorage( rows * cols )
{}
T& operator()( const size_t row, const size_t col )
{
return mStorage[ row * mStride + col ];
}
const T& operator()( const size_t row, const size_t col ) const
{
return mStorage[ row * mStride + col ];
}
size_t rows() const { return mHeight; }
size_t cols() const { return mStride; }
private:
vector< T > mStorage;
size_t mStride;
size_t mHeight;
};
struct Cell
{
enum Type{ AIR, GROUND, WATER };
Cell()
: mType( AIR )
, mMass( 0 )
, mNewMass( 0 )
{}
Type mType;
float mMass;
float mNewMass;
};
const float MaxMass = 1.0f;
const float MinMass = 0.0001f;
const float MaxCompress = 0.02f;
const float MaxSpeed = 1.0f;
const float MinFlow = 0.01f;
//Take an amount of water and calculate how it should be split among two
//vertically adjacent cells. Returns the amount of water that should be in
//the bottom cell.
float get_stable_state_b( float total_mass )
{
if ( total_mass <= 1 )
{
return 1;
}
else if ( total_mass < 2*MaxMass + MaxCompress )
{
return (MaxMass*MaxMass + total_mass*MaxCompress)/(MaxMass + MaxCompress);
}
else
{
return (total_mass + MaxCompress)/2;
}
}
template< typename T >
T constrain( const T& val, const T& minVal, const T& maxVal )
{
return max( minVal, min( val, maxVal ) );
}
typedef Matrix< Cell > State;
void stepState( State& cur )
{
for( size_t y = 1; y < cur.rows()-1; ++y )
{
for( size_t x = 1; x < cur.cols()-1; ++x )
{
Cell& center = cur( y, x );
// Skip inert ground blocks
if( center.mType == Cell::GROUND )
continue;
// Custom push-only flow
float Flow = 0;
float remaining_mass = center.mMass;
if( remaining_mass <= 0 )
continue;
// The block below this one
Cell& below = cur( y-1, x );
if( below.mType != Cell::GROUND )
{
Flow = get_stable_state_b( remaining_mass + below.mMass ) - below.mMass;
if( Flow > MinFlow )
{
//leads to smoother flow
Flow *= 0.5;
}
Flow = constrain( Flow, 0.0f, min(MaxSpeed, remaining_mass) );
center.mNewMass -= Flow;
below.mNewMass += Flow;
remaining_mass -= Flow;
}
if ( remaining_mass <= 0 )
continue;
// Left
Cell& left = cur( y, x-1 );
if ( left.mType != Cell::GROUND )
{
// Equalize the amount of water in this block and it's neighbour
Flow = ( center.mMass - left.mMass ) / 4;
if ( Flow > MinFlow )
{
Flow *= 0.5;
}
Flow = constrain(Flow, 0.0f, remaining_mass);
center.mNewMass -= Flow;
left.mNewMass += Flow;
remaining_mass -= Flow;
}
if ( remaining_mass <= 0 )
continue;
// Right
Cell& right = cur( y, x+1 );
if ( right.mType != Cell::GROUND )
{
// Equalize the amount of water in this block and it's neighbour
Flow = ( center.mMass - right.mMass ) / 4;
if ( Flow > MinFlow )
{
Flow *= 0.5;
}
Flow = constrain(Flow, 0.0f, remaining_mass);
center.mNewMass -= Flow;
right.mNewMass += Flow;
remaining_mass -= Flow;
}
if ( remaining_mass <= 0 )
continue;
// The block above this one
Cell& above = cur( y+1, x );
if( above.mType != Cell::GROUND )
{
Flow = remaining_mass - get_stable_state_b( remaining_mass + above.mMass );
if( Flow > MinFlow )
{
//leads to smoother flow
Flow *= 0.5;
}
Flow = constrain( Flow, 0.0f, min(MaxSpeed, remaining_mass) );
center.mNewMass -= Flow;
above.mNewMass += Flow;
remaining_mass -= Flow;
}
}
}
for( size_t y = 0; y < cur.rows(); ++y )
{
for( size_t x = 0; x < cur.cols(); ++x )
{
cur( y, x ).mMass = cur( y, x ).mNewMass;
}
}
for( size_t y = 0; y < cur.rows(); ++y )
{
for( size_t x = 0; x < cur.cols(); ++x )
{
Cell& center = cur( y, x );
if( center.mType == Cell::GROUND )
{
center.mMass = center.mNewMass = 0.0f;
continue;
}
if( center.mMass > MinMass )
{
center.mType = Cell::WATER;
}
else
{
center.mType = Cell::AIR;
center.mMass = 0.0f;
}
}
}
// Remove any water that has left the map
for( size_t x = 0; x < cur.cols(); ++x )
{
cur( 0, x ).mMass = 0;
cur( cur.rows()-1, x ).mMass = 0;
}
for( size_t y = 0; y < cur.rows(); ++y )
{
cur( y, 0 ).mMass = 0;
cur( y, cur.cols()-1 ).mMass = 0;
}
}
void showState( const State& state )
{
glPolygonMode( GL_FRONT, GL_LINE );
glBegin( GL_QUADS );
glColor3ub( 0, 0, 0 );
for( size_t y = 0; y < state.rows(); ++y )
{
for( size_t x = 0; x < state.cols(); ++x )
{
glVertex2f( x+0, y+0 );
glVertex2f( x+1, y+0 );
glVertex2f( x+1, y+1 );
glVertex2f( x+0, y+1 );
}
}
glEnd();
glPolygonMode( GL_FRONT, GL_FILL );
glBegin( GL_QUADS );
for( size_t y = 0; y < state.rows(); ++y )
{
for( size_t x = 0; x < state.cols(); ++x )
{
if( state( y, x ).mType == Cell::AIR )
continue;
float height = 1.0f;
if( state( y, x ).mType == Cell::GROUND )
{
glColor3ub( 152, 118, 84 );
}
else
{
glColor3ub( 0, 135, 189 );
height = min( 1.0f, state( y, x ).mMass );
}
glVertex2f( x+0, y );
glVertex2f( x+1, y );
glVertex2f( x+1, y + height );
glVertex2f( x+0, y + height );
}
}
glEnd();
}
State state( 20, 20 );
void mouse( int button, int button_state, int x, int y )
{
float pctX = (float)x / glutGet( GLUT_WINDOW_WIDTH );
float pctY = 1.0f - ( (float)y / glutGet( GLUT_WINDOW_HEIGHT ) );
size_t cellX = pctX * state.cols();
size_t cellY = pctY * state.rows();
Cell& cur = state( cellY, cellX );
if( button_state == GLUT_UP )
return;
if( button == GLUT_LEFT_BUTTON )
{
cur.mType = ( cur.mType == Cell::GROUND ? Cell::AIR : Cell::GROUND );
cur.mMass = cur.mNewMass = 0.0f;
}
if( button == GLUT_RIGHT_BUTTON )
{
cur.mType = Cell::WATER;
cur.mMass = cur.mNewMass = 1.0f;
}
}
void display()
{
static bool firstTime = true;
if( firstTime )
{
firstTime = false;
for( size_t y = 0; y < state.rows(); ++y )
{
for( size_t x = 0; x < state.cols(); ++x )
{
state( y, x ).mType = (Cell::Type)( rand() % 3 );
state( y, x ).mMass = 1.0f;
state( y, x ).mNewMass = 1.0f;
}
}
}
glClearColor( 1, 1, 1, 1 );
glClear( GL_COLOR_BUFFER_BIT );
glMatrixMode( GL_PROJECTION );
glLoadIdentity();
glOrtho( 0, state.cols(), 0, state.rows(), -1, 1);
glMatrixMode( GL_MODELVIEW );
glLoadIdentity();
stepState( state );
showState( state );
glutSwapBuffers();
}
void timer(int extra)
{
glutPostRedisplay();
glutTimerFunc(16, timer, 0);
}
int main( int argc, char **argv )
{
glutInit( &argc, argv );
glutInitDisplayMode( GLUT_RGBA | GLUT_DOUBLE );
glutInitWindowSize( 640, 480 );
glutCreateWindow( "Cells" );
glutDisplayFunc( display );
glutMouseFunc( mouse );
glutTimerFunc(0, timer, 0);
glutMainLoop();
return 0;
}