I’m trying to convert a 2D map created in my map editor to a 3D plotting with OpenGL. This is my map generated in my map editor:
Those vertices are relative to my Cartesian origin world coordinate (top up of the picture) and I’m applying this formula to convert it to an OpenGL object coordinate:
World size: 800x600
x = (X / 800) -0.5
y = (Y / 600) -0.5
Getting this result:
(First object face)
−0.48625, 0.068333333
0.12625, 0.07
0.12875, −0.481666667
−0.4875, −0.486666667
Plotting this vertex buffer in OpenGL, I got a very weird result. So how can I get a 3D model from those vertex positions? Like this picture:
I’m rendering OpenGL in triangles mode and using this example as the start point: https://github.com/JoeyDeVries/LearnOpenGL/blob/master/src/1.getting_started/7.4.camera_class/camera_class.cpp
Using the conversion formula + the Earcut tessellation (https://github.com/mapbox/earcut.hpp), I've finally got this rectangle rendering correctly inside OpenGL. With two planes with just the Z-axis different, now the problem is how to render its laterals since Earcut just works with 2D coordinates...
If I get it right you got some planar 2D polygon and what to add some constant thickness to it (as a 3D mesh). That is doable fairly easily. As you correctly assumed you need to triangulate first. So you should have this input:
table of points pnt[pnts]
list of all points of your object.
polygon pol[pols]
(circumference of your object)
just ordered list of points indexes referencing to table of points
triangulation result fac[facs]
ordered list of 3 point indexes representing all the triangles.
Now to make a mesh from it we need to do this:
copy all the points and extrude them by some translation.
all this new points will be added to current pnt[pnts]
table. Do not forget to remember original table size pnts0
as it will be needed later.
copy/reverse the triangulation.
The opposite side of the triangulated polygon will be the same just in reverse polygon winding. So just copy it to fac[facs]
as new triangles in reverse index order ... Do not forget to add the original point table size to all the new faces. This will use the new points ... From the images of yours you got to this point already.
create the missing side faces.
For this we can exploit the original polygon. As we just copied the points then we know that pnt[3*i]
is opposite to pnt[pnts0+3*i]
. So we just create triangle faces joining the opposite edges of the polygon.
Here small C++ example I busted for this right now:
//---------------------------------------------------------------------------
#include <vcl.h>
#include <math.h>
#pragma hdrstop
#include "Unit1.h"
#include "gl_simple.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------
const int N=128;
int pnts=6*3; // 3* number of points
float pnt[N]= // x,y per each point
{
-0.5,-0.5,0.0, // 6 ------ 9
-0.4, 0.0,0.0, // + +
-0.5,+0.5,0.0, // 3 12
+0.5,+0.5,0.0, // + +
+0.4, 0.0,0.0, // 0 ----- 15
+0.5,-0.5,0.0,
};
int pol[N]={ 0,3,6,9,12,15 }, pols=6; // original polygon (3*pnt index), number of its vertexes
int fac[N]= // triangulation result (3*pnt index)
{
0,3,15,
3,12,15,
3,6,12,
6,9,12,
}, facs=4*3; // number of triangles*3
//---------------------------------------------------------------------------
void extrude(float dz)
{
int i,i0,pnts0=pnts;
// copy and reverse triangulation
for (i=0;i<facs;i++)
fac[facs+facs-1-i]=fac[i]+pnts; facs+=facs;
// duplicate points
for (i=0;i<pnts;i++) pnt[pnts0+i]=pnt[i]; pnts+=pnts;
// extrude points
for (i= 2;i<pnts0;i+=3) pnt[i]-=dz;
for ( ;i<pnts ;i+=3) pnt[i]+=dz;
// side faces
for (i0=pols-1,i=0;i<pols;i0=i,i++)
{
fac[facs]=pol[i ]+pnts0; facs++;
fac[facs]=pol[i ]; facs++;
fac[facs]=pol[i0]; facs++;
fac[facs]=pol[i0]+pnts0; facs++;
fac[facs]=pol[i ]+pnts0; facs++;
fac[facs]=pol[i0]; facs++;
}
}
//---------------------------------------------------------------------------
void gl_draw()
{
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
glDisable(GL_TEXTURE_2D);
glEnable(GL_DEPTH_TEST);
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
glEnable(GL_CULL_FACE);
glFrontFace(GL_CCW);
glEnable(GL_COLOR_MATERIAL);
/*
glPolygonMode(GL_FRONT,GL_FILL);
glPolygonMode(GL_BACK,GL_LINE);
glDisable(GL_CULL_FACE);
*/
// set view
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glTranslatef(0.0,0.0,-5.0);
static float ang=0.0;
glRotatef(ang,0.2,0.7,0.1); ang+=5.0; if (ang>=360.0) ang-=360.0;
// render mesh
float *p0,*p1,*p2,n[3],a[3],b[3],c;
glColor3f(0.7,0.7,0.7);
glBegin(GL_TRIANGLES);
for (int i=0;i+3<=facs;i+=3)
{
// points
p0=pnt+fac[i+0];
p1=pnt+fac[i+1];
p2=pnt+fac[i+2];
// compute normal
a[0]=p1[0]-p0[0]; a[1]=p1[1]-p0[1]; a[2]=p1[2]-p0[2];
b[0]=p2[0]-p1[0]; b[1]=p2[1]-p1[1]; b[2]=p2[2]-p1[2];
n[0]=(a[1]*b[2])-(a[2]*b[1]);
n[1]=(a[2]*b[0])-(a[0]*b[2]);
n[2]=(a[0]*b[1])-(a[1]*b[0]);
c=1.0/sqrt((n[0]*n[0])+(n[1]*n[1])+(n[2]*n[2]));
n[0]*=c; n[1]*=c; n[2]*=c;
// render
glNormal3fv(n);
glVertex3fv(p0);
glVertex3fv(p1);
glVertex3fv(p2);
}
glEnd();
// glFlush();
glFinish();
SwapBuffers(hdc);
}
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner):TForm(Owner)
{
// Init of program
gl_init(Handle); // init OpenGL
extrude(0.2);
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormDestroy(TObject *Sender)
{
// Exit of program
gl_exit();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormPaint(TObject *Sender)
{
// repaint
gl_draw();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormResize(TObject *Sender)
{
// resize
gl_resize(ClientWidth,ClientHeight);
}
//---------------------------------------------------------------------------
void __fastcall TForm1::tim_redrawTimer(TObject *Sender)
{
gl_draw();
}
//---------------------------------------------------------------------------
It is VCL based so ignore all the VCL stuff and port the events you want/need and GL context stuff to your style of programing. The only important stuff here are:
the tables pnt,fac,pol
which holds the input and latter also output. The extrude(dz)
will create the mesh (call it just once!) and gl_draw will render the tables as mesh (Using the old style GL api for simplicity).
For the GL stuff I used my gl_simple.h
which you can find in this related QA:
Here is preview of the code above:
the choppynes is due to my GIF capture the rendering is smooth. I used static allocation and on the run normal computation so the code is simple and easy to understand. Of coarse for the real deal you need to implement dynamic lists and VAO/VBO ... if you want good performance