|
|
f2b8c4 |
/*
|
|
|
f2b8c4 |
......... 2015 Ivan Mahonin
|
|
|
f2b8c4 |
|
|
|
f2b8c4 |
This program is free software: you can redistribute it and/or modify
|
|
|
f2b8c4 |
it under the terms of the GNU General Public License as published by
|
|
|
f2b8c4 |
the Free Software Foundation, either version 3 of the License, or
|
|
|
f2b8c4 |
(at your option) any later version.
|
|
|
f2b8c4 |
|
|
|
f2b8c4 |
This program is distributed in the hope that it will be useful,
|
|
|
f2b8c4 |
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
f2b8c4 |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
f2b8c4 |
GNU General Public License for more details.
|
|
|
f2b8c4 |
|
|
|
f2b8c4 |
You should have received a copy of the GNU General Public License
|
|
|
f2b8c4 |
along with this program. If not, see <http: licenses="" www.gnu.org="">.</http:>
|
|
|
f2b8c4 |
*/
|
|
|
f2b8c4 |
|
|
|
f2b8c4 |
#include <cmath></cmath>
|
|
|
f2b8c4 |
|
|
|
f2b8c4 |
#include <fstream></fstream>
|
|
|
f2b8c4 |
#include <iostream></iostream>
|
|
|
f2b8c4 |
#include <vector></vector>
|
|
|
f2b8c4 |
#include <map></map>
|
|
|
f2b8c4 |
|
|
|
f2b8c4 |
#include <assert.h></assert.h>
|
|
|
f2b8c4 |
|
|
|
f2b8c4 |
#include <gl gl.h=""></gl>
|
|
|
f2b8c4 |
#include <gl glext.h=""></gl>
|
|
|
f2b8c4 |
#include <gl glx.h=""></gl>
|
|
|
f2b8c4 |
|
|
|
f2b8c4 |
|
|
|
f2b8c4 |
#define GLX_CONTEXT_MAJOR_VERSION_ARB 0x2091
|
|
|
f2b8c4 |
#define GLX_CONTEXT_MINOR_VERSION_ARB 0x2092
|
|
|
f2b8c4 |
typedef GLXContext (*GLXCREATECONTEXTATTRIBSARBPROC)(Display*, GLXFBConfig, GLXContext, Bool, const int*);
|
|
|
f2b8c4 |
|
|
|
f2b8c4 |
|
|
|
f2b8c4 |
using namespace std;
|
|
|
f2b8c4 |
|
|
|
f2b8c4 |
|
|
|
f2b8c4 |
typedef pair<float, float=""> vec2;</float,>
|
|
|
f2b8c4 |
typedef vector<vec2> contour;</vec2>
|
|
|
f2b8c4 |
|
|
|
f2b8c4 |
void save_rgba(const void *buffer, int width, int height, const string &filename) {
|
|
|
f2b8c4 |
// create file
|
|
|
f2b8c4 |
ofstream f(filename.c_str(), ofstream::out | ofstream::trunc | ofstream::binary);
|
|
|
f2b8c4 |
|
|
|
f2b8c4 |
// write header
|
|
|
f2b8c4 |
unsigned char targa_header[] = {
|
|
|
f2b8c4 |
0, // Length of the image ID field (0 - no ID field)
|
|
|
f2b8c4 |
0, // Whether a color map is included (0 - no colormap)
|
|
|
f2b8c4 |
2, // Compression and color types (2 - uncompressed true-color image)
|
|
|
f2b8c4 |
0, 0, 0, 0, 0, // Color map specification (not need for us)
|
|
|
f2b8c4 |
0, 0, // X-origin
|
|
|
f2b8c4 |
0, 0, // Y-origin
|
|
|
f2b8c4 |
(unsigned char)(width & 0xff), // Image width
|
|
|
f2b8c4 |
(unsigned char)(width >> 8),
|
|
|
f2b8c4 |
(unsigned char)(height & 0xff), // Image height
|
|
|
f2b8c4 |
(unsigned char)(height >> 8),
|
|
|
f2b8c4 |
32, // Bits per pixel
|
|
|
f2b8c4 |
0 // Image descriptor (keep zero for capability)
|
|
|
f2b8c4 |
};
|
|
|
f2b8c4 |
f.write((char*)targa_header, sizeof(targa_header));
|
|
|
f2b8c4 |
|
|
|
f2b8c4 |
// write data
|
|
|
f2b8c4 |
int line_size = 4*width;
|
|
|
f2b8c4 |
const char *end = (char*)buffer;
|
|
|
f2b8c4 |
const char *current = end + height*line_size;
|
|
|
f2b8c4 |
while(current > end) {
|
|
|
f2b8c4 |
current -= line_size;
|
|
|
f2b8c4 |
f.write(current, line_size);
|
|
|
f2b8c4 |
}
|
|
|
f2b8c4 |
}
|
|
|
f2b8c4 |
|
|
|
f2b8c4 |
void save_viewport(const string &filename) {
|
|
|
f2b8c4 |
cout << filename << endl;
|
|
|
f2b8c4 |
glFinish();
|
|
|
f2b8c4 |
int vp[4] = {};
|
|
|
f2b8c4 |
glGetIntegerv(GL_VIEWPORT, vp);
|
|
|
f2b8c4 |
char *buffer = new char[vp[2]*vp[3]*4];
|
|
|
f2b8c4 |
glReadPixels(vp[0], vp[1], vp[2], vp[3], GL_BGRA, GL_UNSIGNED_BYTE, buffer);
|
|
|
f2b8c4 |
save_rgba(buffer, vp[2], vp[3], filename);
|
|
|
f2b8c4 |
}
|
|
|
f2b8c4 |
|
|
|
f2b8c4 |
void build_contour(contour &c) {
|
|
|
f2b8c4 |
const float min_segment_length = 0.001f;
|
|
|
f2b8c4 |
const float rounds = 10.f;
|
|
|
f2b8c4 |
const float rounds2 = 1.f;
|
|
|
f2b8c4 |
|
|
|
f2b8c4 |
contour back;
|
|
|
f2b8c4 |
|
|
|
f2b8c4 |
float angle = 360.f;
|
|
|
f2b8c4 |
float offset = 0.25f/(rounds + 1.f);
|
|
|
f2b8c4 |
|
|
|
f2b8c4 |
// go front
|
|
|
f2b8c4 |
while(true) {
|
|
|
f2b8c4 |
float radius = angle/360.f/(rounds + 1.f);
|
|
|
f2b8c4 |
float step = min_segment_length*180.f/M_PI/radius;
|
|
|
f2b8c4 |
if (radius > 1.f - 2.f*offset) break;
|
|
|
f2b8c4 |
|
|
|
f2b8c4 |
float fr = radius + offset;
|
|
|
f2b8c4 |
float fx = fr*sinf(angle/180.f*M_PI);
|
|
|
f2b8c4 |
float fy = fr*cosf(angle/180.f*M_PI);
|
|
|
f2b8c4 |
|
|
|
f2b8c4 |
float br = radius - offset;
|
|
|
f2b8c4 |
float bx = br*sinf(angle/180.f*M_PI);
|
|
|
f2b8c4 |
float by = br*cosf(angle/180.f*M_PI);
|
|
|
f2b8c4 |
|
|
|
f2b8c4 |
c.push_back(vec2(fx, fy));
|
|
|
f2b8c4 |
back.push_back(vec2(bx, by));
|
|
|
f2b8c4 |
|
|
|
f2b8c4 |
angle += step;
|
|
|
f2b8c4 |
}
|
|
|
f2b8c4 |
|
|
|
f2b8c4 |
float max_angle = angle;
|
|
|
f2b8c4 |
|
|
|
f2b8c4 |
while(true) {
|
|
|
f2b8c4 |
float radius = max_angle/360.f/(rounds + 1.f)
|
|
|
f2b8c4 |
+ (max_angle-angle)/360.f/rounds2;
|
|
|
f2b8c4 |
float step = min_segment_length*180.f/M_PI/radius;
|
|
|
f2b8c4 |
if (radius < 1.f/(rounds + 1.f))
|
|
|
f2b8c4 |
break;
|
|
|
f2b8c4 |
|
|
|
f2b8c4 |
float fr = radius + offset;
|
|
|
f2b8c4 |
float fx = fr*sinf(angle/180.f*M_PI);
|
|
|
f2b8c4 |
float fy = fr*cosf(angle/180.f*M_PI);
|
|
|
f2b8c4 |
|
|
|
f2b8c4 |
float br = radius - offset;
|
|
|
f2b8c4 |
float bx = br*sinf(angle/180.f*M_PI);
|
|
|
f2b8c4 |
float by = br*cosf(angle/180.f*M_PI);
|
|
|
f2b8c4 |
|
|
|
f2b8c4 |
c.push_back(vec2(fx, fy));
|
|
|
f2b8c4 |
back.push_back(vec2(bx, by));
|
|
|
f2b8c4 |
|
|
|
f2b8c4 |
angle += step;
|
|
|
f2b8c4 |
}
|
|
|
f2b8c4 |
|
|
|
f2b8c4 |
|
|
|
f2b8c4 |
// go back
|
|
|
f2b8c4 |
c.reserve(c.size() + back.size() + 1);
|
|
|
f2b8c4 |
for(contour::reverse_iterator ri = back.rbegin(); ri != back.rend(); ++ri)
|
|
|
f2b8c4 |
c.push_back(*ri);
|
|
|
f2b8c4 |
|
|
|
f2b8c4 |
// close
|
|
|
f2b8c4 |
c.push_back(c.front());
|
|
|
f2b8c4 |
|
|
|
f2b8c4 |
cout << c.size() << " vertices" << endl;
|
|
|
f2b8c4 |
}
|
|
|
f2b8c4 |
|
|
|
f2b8c4 |
void draw_contour_strip(const contour &c) {
|
|
|
f2b8c4 |
glBegin(GL_TRIANGLE_STRIP);
|
|
|
f2b8c4 |
for(contour::const_iterator i = c.begin(); i != c.end(); ++i) {
|
|
|
f2b8c4 |
glVertex2f(i->first, i->second);
|
|
|
f2b8c4 |
glVertex2f(-1.f, i->second);
|
|
|
f2b8c4 |
}
|
|
|
f2b8c4 |
glEnd();
|
|
|
f2b8c4 |
}
|
|
|
f2b8c4 |
|
|
|
f2b8c4 |
|
|
|
f2b8c4 |
void draw_contour(const contour &c, bool even_odd, bool invert) {
|
|
|
f2b8c4 |
glPushAttrib(GL_ALL_ATTRIB_BITS);
|
|
|
f2b8c4 |
glEnable(GL_STENCIL_TEST);
|
|
|
f2b8c4 |
|
|
|
f2b8c4 |
// render mask
|
|
|
f2b8c4 |
glClear(GL_STENCIL_BUFFER_BIT);
|
|
|
f2b8c4 |
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
|
|
|
f2b8c4 |
glStencilFunc(GL_ALWAYS, 0, 0);
|
|
|
f2b8c4 |
if (even_odd) {
|
|
|
f2b8c4 |
glStencilOp(GL_KEEP, GL_KEEP, GL_INCR_WRAP);
|
|
|
f2b8c4 |
} else {
|
|
|
f2b8c4 |
glStencilOpSeparate(GL_FRONT, GL_INCR_WRAP, GL_INCR_WRAP, GL_INCR_WRAP);
|
|
|
f2b8c4 |
glStencilOpSeparate(GL_BACK, GL_DECR_WRAP, GL_DECR_WRAP, GL_DECR_WRAP);
|
|
|
f2b8c4 |
}
|
|
|
f2b8c4 |
draw_contour_strip(c);
|
|
|
f2b8c4 |
|
|
|
f2b8c4 |
// fill mask
|
|
|
f2b8c4 |
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
|
|
|
f2b8c4 |
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
|
|
|
f2b8c4 |
if (!even_odd && !invert)
|
|
|
f2b8c4 |
glStencilFunc(GL_NOTEQUAL, 0, -1);
|
|
|
f2b8c4 |
if (!even_odd && invert)
|
|
|
f2b8c4 |
glStencilFunc(GL_EQUAL, 0, -1);
|
|
|
f2b8c4 |
if ( even_odd && !invert)
|
|
|
f2b8c4 |
glStencilFunc(GL_EQUAL, 1, 1);
|
|
|
f2b8c4 |
if ( even_odd && invert)
|
|
|
f2b8c4 |
glStencilFunc(GL_EQUAL, 0, 1);
|
|
|
f2b8c4 |
|
|
|
f2b8c4 |
glBegin(GL_TRIANGLE_STRIP);
|
|
|
f2b8c4 |
glVertex2d(-1.f, -1.f);
|
|
|
f2b8c4 |
glVertex2d( 1.f, -1.f);
|
|
|
f2b8c4 |
glVertex2d(-1.f, 1.f);
|
|
|
f2b8c4 |
glVertex2d( 1.f, 1.f);
|
|
|
f2b8c4 |
glEnd();
|
|
|
f2b8c4 |
|
|
|
f2b8c4 |
glPopAttrib();
|
|
|
f2b8c4 |
}
|
|
|
f2b8c4 |
|
|
|
f2b8c4 |
class test_wrapper {
|
|
|
f2b8c4 |
private:
|
|
|
f2b8c4 |
string filename;
|
|
|
f2b8c4 |
clock_t t;
|
|
|
f2b8c4 |
test_wrapper(const test_wrapper&): t() { }
|
|
|
f2b8c4 |
test_wrapper& operator= (const test_wrapper&) { return *this; }
|
|
|
f2b8c4 |
public:
|
|
|
f2b8c4 |
test_wrapper(const string &filename): filename(filename), t(clock()) { }
|
|
|
f2b8c4 |
~test_wrapper() {
|
|
|
f2b8c4 |
glFinish();
|
|
|
f2b8c4 |
cout << 1000.0*(double)(clock() - t)/(double)(CLOCKS_PER_SEC) << " ms" << endl;
|
|
|
f2b8c4 |
cout << filename << endl;
|
|
|
f2b8c4 |
save_viewport(filename);
|
|
|
f2b8c4 |
glClear(GL_COLOR_BUFFER_BIT);
|
|
|
f2b8c4 |
}
|
|
|
f2b8c4 |
};
|
|
|
f2b8c4 |
|
|
|
f2b8c4 |
void test() {
|
|
|
f2b8c4 |
contour c;
|
|
|
f2b8c4 |
build_contour(c);
|
|
|
f2b8c4 |
|
|
|
f2b8c4 |
glColor4f(0.f, 0.f, 1.f, 1.f);
|
|
|
f2b8c4 |
|
|
|
f2b8c4 |
{
|
|
|
f2b8c4 |
test_wrapper t("test_contour.tga");
|
|
|
f2b8c4 |
glBegin(GL_LINE_STRIP);
|
|
|
f2b8c4 |
for(contour::const_iterator i = c.begin(); i != c.end(); ++i)
|
|
|
f2b8c4 |
glVertex2f(i->first, i->second);
|
|
|
f2b8c4 |
glEnd();
|
|
|
f2b8c4 |
}
|
|
|
f2b8c4 |
|
|
|
f2b8c4 |
{
|
|
|
f2b8c4 |
test_wrapper t("test_contour_fill.tga");
|
|
|
f2b8c4 |
draw_contour(c, false, false);
|
|
|
f2b8c4 |
}
|
|
|
f2b8c4 |
|
|
|
f2b8c4 |
{
|
|
|
f2b8c4 |
test_wrapper t("test_contour_fill_invert.tga");
|
|
|
f2b8c4 |
draw_contour(c, false, true);
|
|
|
f2b8c4 |
}
|
|
|
f2b8c4 |
|
|
|
f2b8c4 |
{
|
|
|
f2b8c4 |
test_wrapper t("test_contour_evenodd.tga");
|
|
|
f2b8c4 |
draw_contour(c, true, false);
|
|
|
f2b8c4 |
}
|
|
|
f2b8c4 |
|
|
|
f2b8c4 |
{
|
|
|
f2b8c4 |
test_wrapper t("test_contour_evenodd_invert.tga");
|
|
|
f2b8c4 |
draw_contour(c, true, true);
|
|
|
f2b8c4 |
}
|
|
|
f2b8c4 |
}
|
|
|
f2b8c4 |
|
|
|
f2b8c4 |
int main() {
|
|
|
f2b8c4 |
// open display (we will use default display and screen 0)
|
|
|
f2b8c4 |
Display *display = XOpenDisplay(NULL);
|
|
|
f2b8c4 |
assert(display);
|
|
|
f2b8c4 |
|
|
|
f2b8c4 |
// choose config
|
|
|
f2b8c4 |
int config_attribs[] = {
|
|
|
f2b8c4 |
GLX_DOUBLEBUFFER, False,
|
|
|
f2b8c4 |
GLX_RED_SIZE, 8,
|
|
|
f2b8c4 |
GLX_GREEN_SIZE, 8,
|
|
|
f2b8c4 |
GLX_BLUE_SIZE, 8,
|
|
|
f2b8c4 |
GLX_ALPHA_SIZE, 8,
|
|
|
f2b8c4 |
GLX_DEPTH_SIZE, 24,
|
|
|
f2b8c4 |
GLX_STENCIL_SIZE, 8,
|
|
|
f2b8c4 |
GLX_ACCUM_RED_SIZE, 8,
|
|
|
f2b8c4 |
GLX_ACCUM_GREEN_SIZE, 8,
|
|
|
f2b8c4 |
GLX_ACCUM_BLUE_SIZE, 8,
|
|
|
f2b8c4 |
GLX_ACCUM_ALPHA_SIZE, 8,
|
|
|
f2b8c4 |
GLX_DRAWABLE_TYPE, GLX_PBUFFER_BIT,
|
|
|
f2b8c4 |
None };
|
|
|
f2b8c4 |
int nelements = 0;
|
|
|
f2b8c4 |
GLXFBConfig *configs = glXChooseFBConfig(display, 0, config_attribs, &nelements);
|
|
|
f2b8c4 |
assert(configs != NULL && nelements > 0);
|
|
|
f2b8c4 |
GLXFBConfig config = configs[0];
|
|
|
f2b8c4 |
assert(config);
|
|
|
f2b8c4 |
|
|
|
f2b8c4 |
// create pbuffer
|
|
|
f2b8c4 |
int pbuffer_width = 1024;
|
|
|
f2b8c4 |
int pbuffer_height = 1024;
|
|
|
f2b8c4 |
int pbuffer_attribs[] = {
|
|
|
f2b8c4 |
GLX_PBUFFER_WIDTH, pbuffer_width,
|
|
|
f2b8c4 |
GLX_PBUFFER_HEIGHT, pbuffer_height,
|
|
|
f2b8c4 |
None };
|
|
|
f2b8c4 |
GLXPbuffer pbuffer = glXCreatePbuffer(display, config, pbuffer_attribs);
|
|
|
f2b8c4 |
assert(pbuffer);
|
|
|
f2b8c4 |
|
|
|
f2b8c4 |
// create context
|
|
|
f2b8c4 |
int context_attribs[] = {
|
|
|
f2b8c4 |
GLX_CONTEXT_MAJOR_VERSION_ARB, 2,
|
|
|
f2b8c4 |
GLX_CONTEXT_MINOR_VERSION_ARB, 1,
|
|
|
f2b8c4 |
None };
|
|
|
f2b8c4 |
GLXCREATECONTEXTATTRIBSARBPROC glXCreateContextAttribsARB = (GLXCREATECONTEXTATTRIBSARBPROC) glXGetProcAddress((const GLubyte*)"glXCreateContextAttribsARB");
|
|
|
f2b8c4 |
GLXContext context = glXCreateContextAttribsARB(display, config, NULL, True, context_attribs);
|
|
|
f2b8c4 |
assert(context);
|
|
|
f2b8c4 |
|
|
|
f2b8c4 |
// make context current
|
|
|
f2b8c4 |
glXMakeContextCurrent(display, pbuffer, pbuffer, context);
|
|
|
f2b8c4 |
|
|
|
f2b8c4 |
// set view port
|
|
|
f2b8c4 |
glViewport(0, 0, pbuffer_width, pbuffer_height);
|
|
|
f2b8c4 |
|
|
|
f2b8c4 |
// do something
|
|
|
f2b8c4 |
test();
|
|
|
f2b8c4 |
|
|
|
f2b8c4 |
// deinitialization
|
|
|
f2b8c4 |
glXMakeContextCurrent(display, None, None, NULL);
|
|
|
f2b8c4 |
glXDestroyContext(display, context);
|
|
|
f2b8c4 |
glXDestroyPbuffer(display, pbuffer);
|
|
|
f2b8c4 |
XCloseDisplay(display);
|
|
|
f2b8c4 |
|
|
|
f2b8c4 |
cout << "done" << endl;
|
|
|
f2b8c4 |
return 0;
|
|
|
f2b8c4 |
}
|