I wrote a piece of code a year back for rendering editable 2D Shapes using C# + OpenTK. Each shape has its own vertices and editing the Shape directly changes the vertex values.
I have a CollectVertices() in the display_callback_func which iterates all the Shapes and combine the vertex information in big arrays and draw them at once using the snippet below:
GL.EnableClientState(ArrayCap.VertexArray);
GL.EnableClientState(ArrayCap.ColorArray);
// draw fills (triangles)
GL.ColorPointer<byte>(3, ColorPointerType.UnsignedByte, 0, fillColor);
GL.VertexPointer<double>(2, VertexPointerType.Double, 0, fillArray);
GL.DrawArrays(BeginMode.Triangles, 0, fillArray.Length / 2);
// draw lines
GL.ColorPointer<byte>(3, ColorPointerType.UnsignedByte, 0, lineColor);
GL.VertexPointer<double>(2, VertexPointerType.Double, 0, lineArray);
GL.DrawArrays(BeginMode.Lines, 0, lineArray.Length / 2);
GL.DisableClientState(ArrayCap.ColorArray);
GL.DisableClientState(ArrayCap.VertexArray);
One year later today, I want to introduce a new struct Transform to the Shape object. It will contain a Vector2 Center and Matrix Orientation. Editing the Shape now will change values in Transform instead (although scaling will still be done on vertex data, as it should logically be so).
What is a neat way, with minimal changes to the above snippet, to include the transformation matrix?
Related
I have many Esri Grid files (https://en.wikipedia.org/wiki/Esri_grid#ASCII) and I would like to render them in 3D without losing precision, I am using OpenSceneGraph.
The problem is this grids are around 1000x1000 (or more) points, so when I extract the vertices, then compute the triangles to create the geometry, I end up having millions of them and the interaction with the scene is impossible (frame rate drops to 0).
I've tried several approches:
Triangle list
Basically, as I read the file, I fill an array with 3 vertices per triangle (this leads to duplication);
osg::ref_ptr<osg::Geode> l_pGeodeSurface = new osg::Geode;
osg::ref_ptr<osg::Geometry> l_pGeometrySurface = new osg::Geometry;
osg::ref_ptr<osg::Vec3Array> l_pvTrianglePoints = osg::Vec3Array;
osg::ref_ptr<osg::Vec3Array> l_pvOriginalPoints = osg::Vec3Array;
... // Read the file and fill l_pvOriginalPoints
for(*triangle inside the file*)
{
... // Compute correct triangle indices (l_iP1, l_iP2, l_iP3)
// Push triangle vertices inside the array
l_pvTrianglePoints->push_back(l_pvOriginalPoints->at(l_iP1));
l_pvTrianglePoints->push_back(l_pvOriginalPoints->at(l_iP2));
l_pvTrianglePoints->push_back(l_pvOriginalPoints->at(l_iP3));
}
l_pGeometrySurface->setVertexArray(l_pvTrianglePoints);
l_pGeometrySurface->addPrimitiveSet(new osg::DrawArrays(GL_TRIANGLES, 0, 3, l_pvTrianglePoints->size()));
Indexed triangle list
Same as before, but the array contains the every vertices just once and I create a second array of indices (basically i tell osg how to build triangles, no duplication)
osg::ref_ptr<osg::Geode> l_pGeodeSurface = new osg::Geode;
osg::ref_ptr<osg::Geometry> l_pGeometrySurface = new osg::Geometry;
osg::ref_ptr<osg::DrawElementsUInt> l_pIndices = new osg::DrawElementsUInt(osg::PrimitiveSet::TRIANGLES, *number of indices*);
osg::ref_ptr<osg::Vec3Array> l_pvOriginalPoints = osg::Vec3Array;
... // Read the file and fill l_pvOriginalPoints
for(i = 0; i < *number of indices*; i++)
{
... // Compute correct triangle indices (l_iP1, l_iP2, l_iP3)
// Push vertices indices inside the array
l_pIndices->at(i) = l_iP1;
l_pIndices->at(i+1) = l_iP2;
l_pIndices->at(i+2) = l_iP3;
}
l_pGeometrySurface->setVertexArray(l_pvOriginalPoints );
l_pGeometrySurface->addPrimitiveSet(l_pIndices.get());
Instancing
this was a bit of an experiment, since I've never used shaders, I tought I could instance a single triangle, then manipulate its coordinates in a vertex shader for every triangle in my scene, using transformation matrices (passing the matrices as a uniform array, one for triangle). I ended up with too many uniforms just with a grid 20x20.
I used these links as a reference:
https://learnopengl.com/Advanced-OpenGL/Instancing,
https://books.google.it/books?id=x_RkEBIJeFQC&pg=PT265&lpg=PT265&dq=osg+instanced+geometry&source=bl&ots=M8ii8zn8w7&sig=ACfU3U0_92Z5EGCyOgbfGweny4KIUfqU8w&hl=en&sa=X&ved=2ahUKEwj-7JD0nq7qAhUXxMQBHcLaAiUQ6AEwAnoECAkQAQ#v=onepage&q=osg%20instanced%20geometry&f=false
None of the above solved my issue, what else can I try? Am I missing something in terms of rendering techinques? I thought it was fairly simple task, but I'm kind of stuck.
I feel like you should consider taking a step back. If you're visualizing GIS-based terrain data, osgEarth is really designed for doing this and has fairly efficient LOD tools for large terrains. Do you need the data always represented at maximum full LOD or are you looking for dynamic LOD to improve frame rate?
Depending on your goals and requirements you might want to look at some more advanced terrain rendering techniques, like rightfield tracing, etc. If the terrain is always static, you can precompute quadtrees and Signed Distance Functions and trace against the heightfield.
I'm having difficulty getting the right orientation from my objects within a scene. The objects are defined in standard Cartesian coordinates in the same units as I define the scene.
I then define my scenes matrix with the following code:
void SVIS_SetLookAt (double eyePos[3], double center[3], double up[3])
{
// Determine the new n
double vN[3] = {eyePos[0] - center[0], eyePos[1] - center[1], eyePos[2] - center[2]};
// Don't I need to normalize the above?
// Determine the new up by crossing witht he Up Vector.
double vU[3];
MATH_crossproduct(up, vN, vU);
MATH_NormalizeVector(vN);
MATH_NormalizeVector(vU);
// Determine V by crossing n and u...
double vV[3];
MATH_crossproduct(vN, vU, vV);
MATH_NormalizeVector(vV);
// Create the model view matrix.
double modelView[16] = {
vU[0], vV[0], vN[0], 0,
vU[1], vV[1], vN[1], 0,
vU[2], vV[2], vN[2], 0,
// -MATH_Dotd(eyePos, vU), -MATH_Dotd(eyePos, vV), -MATH_Dotd(eyePos, vN), 1
0, 0, 0, 1
};
// Load the modelview matrix. The model view matrix shoudl already be active.
glLoadMatrixd(modelView);
}
I am attempting to display n-1 objects such that each object is facing the object in front of it, excluding the first object which is not displayed. So for each object, I define the up, right, and forward vectors as such:
lal_to_ecef(curcen, pHits->u); // up vector is our position normalized
MATH_subtractVec3D((SVN_VEC3D*) prevcenter, (SVN_VEC3D*) curcen, (SVN_VEC3D*) pHits->f);
MATH_NormalizeVector(pHits->u);
MATH_NormalizeVector(pHits->f);
MATH_crossproduct(pHits->u, pHits->f, pHits->r);
MATH_NormalizeVector(pHits->r);
MATH_crossproduct(pHits->f, pHits->r, pHits->u);
MATH_NormalizeVector(pHits->u);
I then go on to display each object with the following code:
double p[3] = {pHits->cen[0] - position[0],
pHits->cen[1] - position[1],
pHits->cen[2] - position[2]};
glPushMatrix();
SVIS_LookAt(pHits->u, pHits->f, pHits->r, p);
glCallList(G_svisHitsListId);
glPopMatrix();
void SVIS_LookAt (double u[3], double f[3], double l[3], double pos[3])
{
double model[16] = {
l[0], u[0], f[0], 0,
l[1], u[1], f[1], 0,
l[2], u[2], f[2], 0,
pos[0], pos[1], pos[2], 1
};
glMultMatrixd(model);
}
I would expect this to work for any object such that the output would be whatever was defined in the Cartesian coordinate system would be present at the given point oriented such that it was pointed at the proceeding object with 0,1,0 and 0,-1,0 from the defined object would be aligned vertically on the screen. What I am seeing instead (by using simple rectangle as the object to be displayed) is that the objects are consistently rotated about the forward axis.
Can anyone point out what I am doing wrong here?
[Edit]
I've displayed an axis grid without translating by taking the three vectors multiplying a scalar and adding/subtracting it to the centre point. Doing this, the axis align up as I would expect. Overlaying the object described above shows the object to not be aligned the same way. Is there a relationship between the object space forward, right, and up vectors and the desired world-space vectors that I am missing? Am I simply completely off the mark with regards to my rotation translation matrix?
You are conflicted here; part of that matrix is transposed and part of
it is correct... you have the 4th column correct but your top-left 3x3
matrix is transposed. Each column of the 3x3 matrix (row in that array
of 16 double) is supposed to be one of your axes. It should be:
l[0],l[1],l[2],0, u[0],u[1],u[2],0, f[0],f[1],f[2],0,
pos[0],pos[1],pos[2],1. – Andon M. Coleman
This was dead on. Thanks Andon.
Building an entirely new modelview matrix from scratch using a 'lookat' implementation for each object is, frankly, crazy (or will at least drive you crazy). What you're doing is tantamount to trying to build a scene by having set of objects which are always in a fixed location, and constantly repositioning a camera to catch them from different angles.
A lookat style function should be called once to set up the camera (the view portion of the modelview matrix) position, and subsequently you should be using the matrix stack to position objects within the scene (the model portion of the modelview matrix). That's why it's called the modelview matrix, and not just the view matrix.
In code terms, it would look something like this
// Set up camera position
SVIS_LookAt(....);
for (int i = 0; i < n; ++i) {
glPushMatrix();
// move the object to it's location relative to the world coordinate system
glTranslate(...);
// rotate the object to have the correct orientation
glRotate(...);
// render the geometry
glCallList(...);
glPopMatrix();
}
Of course this assumes that everything has it's position defined in world coordinates. If you have a hierarchy of objects, then you would need to descend into an objects children between the glCallList and glPopMatrix in order to have their locations applied relative to their parent object.
I have a question about applying transformations to textures/vertices (I'm an OpenGL learner). I've got a situation where I'm using SharpGL's DrawText function. You can specify the X and Y coordinate but I want to rotate the text so that it's oriented vertically. The function doesn't take an angle though. The way that SharpGL writes text is that it gets an array of bitmaps that it writes to the screen.
No matter what I try, my text always comes out horizontal.
The gl.DrawText function does the following (I dug into the source code).
gl.MatrixMode(OpenGL.GL_PROJECTION);
gl.PushMatrix();
gl.LoadIdentity();
int[] viewport = new int[4];
gl.GetInteger(OpenGL.GL_VIEWPORT, viewport);
gl.Ortho(0, width, 0, height, -1, 1);
// Create the appropriate modelview matrix.
gl.MatrixMode(OpenGL.GL_MODELVIEW);
gl.PushMatrix();
gl.LoadIdentity();
gl.Color(r, g, b);
gl.RasterPos(x, y);
gl.PushAttrib(OpenGL.GL_LIST_BIT | OpenGL.GL_CURRENT_BIT |
OpenGL.GL_ENABLE_BIT | OpenGL.GL_TRANSFORM_BIT);
gl.Color(r, g, b);
gl.Disable(OpenGL.GL_LIGHTING);
gl.Disable(OpenGL.GL_TEXTURE_2D);
gl.Disable(OpenGL.GL_DEPTH_TEST);
gl.RasterPos(x, y);
// Set the list base.
gl.ListBase(fontBitmapEntry.ListBase);
// Create an array of lists for the glyphs.
var lists = text.Select(c => (byte) c).ToArray();
// Call the lists for the string.
gl.CallLists(lists.Length, lists);
gl.Flush();
Is there a way that I can make transformation calls apply to any drawing that has already been made and had the associated matrices popped off?
If SharpGL is using glBitmap() behind the scenes you'll have to render the text to a texture (glReadPixels()/PBO/FBO) then render a (rotated) quad with that texture.
I have managed to rotate a rectangle in OpenGL (C++) just fine. I am making a program that tests two rectangles for collision using the "separated axis theorem". To use the theorem, I need the x and y coordinates of each corner of the rectangle, but my problem is that, although I call glRotatef(...), the coordinates of the corners of the rectangle are not changed to the values that they are rotated too, but the rectangle rotates as it should. How can I update the coordinates of my rectangle after glRotatef is called?
Code:
// float r1.x[4] and r1.y[4] hold the x and y position of each of the 4 corners, starting with the upper left (r1.x[0], r1.y[0])
glLoadIdentity();
glTranslatef((r1.x[0] + r1.x[2]) / 2, (r1.y[1] + r1.y[3]) / 2, 0); // Translates matrix to center of rectangle
glRotatef(r1.angle, 0, 0, 1);
glTranslatef(-((r1.x[0] + r1.x[2]) / 2), -((r1.y[1] + r1.y[3]) / 2), 0); // Translates back
r1.angle++;
glBegin(GL_QUADS);
glVertex2f(r1.x[0], r1.y[0]);
glVertex2f(r1.x[1], r1.y[1]);
glVertex2f(r1.x[2], r1.y[2]);
glVertex2f(r1.x[3], r1.y[3]);
glEnd();
Transformation calls in OpenGL (like glTranslatef and glRotatef) update an internal transformation matrix that get multiplied by the points you provide before getting drawn on the screen. OpenGL does not at all touch your data.
In general, that is what you want. You have a model that is constant in time, but it gets transformed around.
If however, you do need to update your data, you need to create your own transformation matrix, manually multiply it and then draw with a clean transformation matrix (with glLoadIdentity)
You could get a little help from OpenGL though, as you can get the transformation matrix from OpenGL, but I don't recommend this. The math is not that hard and you'd appreciate learning how to do it.
How can i flip, ot mirror up/left of the image OBJECT, not the screen in the onDraw method?
I tried scale(1, -1, 1) that not worked
my code:
opengl.selectVertex(vertexname)
.translate(x-1, y, -9);
if (opengl.getPathModifier(vertexname).getAngle()>-180 &&
opengl.getPathModifier(vertexname).getAngle()<0 ) {
opengl.selectVertex(vertexname).scale(-scale,scale,1);
} else {
opengl.selectVertex(vertexname).scale(scale,scale,1);
}
opengl.rotate(opengl.getPathModifier(vertexname).getAngle()+90, 0, 0, 1);
Not 100% sure what you're asking here, but I think what you want to do is transform the projection matrix. If you want to flip so that things on the left appear on the right then you need to Scale the projection matrix by (-1, 1, 1). If you want things at the top to appear at the bottom you need to scale by (1,-1, 1) and if you want both you can scale (-1, -1, 1).
Edit based on extra information: If all you want to do is display the object exactly the same, but with texture flipped, you need to change the texture coordinates of the vertices in the objects - flip the texture coordinates by replacing the old u texture coordinate with 1-u.
If you are using glFrustum(l,r,b,t,n,f) then change it yo glFrustum(l,r,t,b,n,f), in case you want a vertical flip. This apply also to glOrtho function.