You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

739 lines
22 KiB

/***************************************************************************
qsdrvopengl.cpp
-------------------
begin : 01-January-2000
copyright : (C) 2000 by Kamil Dobkowski
email : kamildobk@poczta.onet.pl
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include "qsdrvopengl.h"
#ifdef HAVE_GL
#include "qsconsole.h"
#include "qsaxes3d.h"
#include <qapplication.h>
#include <qpainter.h>
#include <qpixmap.h>
#include <qbitmap.h>
#include <qpicture.h>
#include <qpen.h>
#include <assert.h>
#include <math.h>
/**
* What a mess !
*/
//-------------------------------------------------------------//
QSDrvOpenGL::QSDrvOpenGL()
:QSDrvQt()
{
m_alpha = false;
dpaint = false;
m_shade_walls = true;
m_auto_stroke = true;
m_global_transparency = 0;
m_stroke_lightness = -45;
pasize = 0;
pix = NULL;
pic = NULL;
pgl = NULL;
ppic = NULL;
points = NULL;
m_opaint = NULL;
}
//-------------------------------------------------------------//
QSDrvOpenGL::~QSDrvOpenGL()
{
//cout << " open gl destructor " << endl;
}
//-------------------------------------------------------------//
QSDrvOpenGL *QSDrvOpenGL::copy()
{
QSDrvOpenGL *result = new QSDrvOpenGL();
result->copySettingsFrom( this );
return result;
}
//-------------------------------------------------------------//
void QSDrvOpenGL::copySettingsFrom( const QSDrvOpenGL *drv )
{
QSDrvQt::copySettingsFrom( drv );
setAlpha( drv->alpha() );
setShadeWalls( drv->shadeWalls() );
setGlobalTransparency( drv->globalTransparency() );
setMeshAutoStroke( drv->meshAutoStroke() );
setAutoStrokeLightness( drv->meshAutoStrokeLightness() );
init( drv->parentAxes() );
}
//-------------------------------------------------------------//
QSDrvOpenGL::CNormals QSDrvOpenGL::cNormals() const
{
return VertexNormals;
}
//-------------------------------------------------------------//
QSDrvOpenGL::CColors QSDrvOpenGL::cColors() const
{
return VertexColors;
}
//-------------------------------------------------------------//
QSDrvOpenGL::COrdering QSDrvOpenGL::cOrdering() const
// Nearer first not working good with light enabled because
// further polygons overwrites closer ones ( depth function is LEQUAL )
{
return m_alpha || m_axes3->light() ? FurtherFirst : NearerFirst;
}
//-------------------------------------------------------------//
void QSDrvOpenGL::setAlpha( bool enabled )
{
m_alpha = enabled;
}
//-------------------------------------------------------------//
void QSDrvOpenGL::setShadeWalls( bool enabled )
{
m_shade_walls = enabled;
}
//-------------------------------------------------------------//
void QSDrvOpenGL::setGlobalTransparency( unsigned char value )
{
m_global_transparency = value;
}
//-------------------------------------------------------------//
void QSDrvOpenGL::setMeshAutoStroke( bool enabled )
{
m_auto_stroke = enabled;
}
//-------------------------------------------------------------//
void QSDrvOpenGL::setAutoStrokeLightness( int value )
{
m_stroke_lightness = value;
}
//-------------------------------------------------------------//
void QSDrvOpenGL::glError()
{
int error;
while ( (error=glGetError()) ) {
switch( error ) {
case GL_INVALID_ENUM: QSConsole::write("Open GL Error: GL_INVALID_ENUM"); break;
case GL_INVALID_VALUE: QSConsole::write("Open GL Error: GL_INVALID_VALUE"); break;
case GL_INVALID_OPERATION: QSConsole::write("Open GL Error: GL_INVALID_OPERATION"); break;
case GL_STACK_OVERFLOW: QSConsole::write("Open GL Error: GL_STACK_OVERFLOW"); break;
case GL_STACK_UNDERFLOW: QSConsole::write("Open GL Error: GL_STACK_UNDERFLOW"); break;
case GL_OUT_OF_MEMORY: QSConsole::write("Open GL Error: GL_OUT_OF_MEMORY"); break;
default: QSConsole::write("Open GL Error: UNKNOWN"); break;
}
}
}
//-------------------------------------------------------------//
void QSDrvOpenGL::loadMatrix( const double m[4][4] )
{
GLdouble M[16];
for ( int i=0; i<16; i++ ) M[i] = GLdouble( m[i%4][i/4] );
glLoadMatrixd( M );
}
//-------------------------------------------------------------//
void QSDrvOpenGL::setDC( QPainter *p, double init_dpi, bool delete_painter )
{
QSDrvQt::setDC(p,init_dpi,delete_painter);
}
//-------------------------------------------------------------//
#define MARG 5
void QSDrvOpenGL::init( QSAxes3D *parent )
{
m_axes3 = parent;
t = m_axes3->p3D();
//
// Create context
//
// pdev - is a paint device on which we draw all stuff
// OpenGL doesn't support printing so when the external
// paint device is used pdev is a pixmap, which is next
// flushed to the external device.
// paint - is an additional painter for drawing text etc.
//
QPaintDevice *gl_dev;
m_opaint = m_paint;
if ( m_opaint->device()->isExtDev() ) {
//cout << "KMatplot: Creating oixmap, picture and painter " << endl;
pix = new QPixmap( toInt(m_axes3->canvasRect().size.x),
toInt(m_axes3->canvasRect().size.y) );
gl_dev = pix;
pic = new QPicture();
ppic = new QPainter( pic );
m_paint = ppic;
} else {
m_paint = m_opaint;
gl_dev = m_opaint->device();
}
//
// Context's format.
//
QGLFormat f;
f.setDoubleBuffer( false );
//
//
// Only if device is an external device ?????????????????????????????????????????????????????????????
//
//f.setDirectRendering( FALSE );
pgl = new QGLContext( f, gl_dev );
pgl->create();
pgl->makeCurrent();
//cout << "KMatplot: Checking viewport size. " << endl;
GLint maxSize[2]; glGetIntegerv( GL_MAX_VIEWPORT_DIMS, maxSize );
if ( m_axes3->canvasRect().size.x > maxSize[0] ||
m_axes3->canvasRect().size.y > maxSize[1] ) {
//cout << "KMatplot: Resizing pixmap. " << endl;
QSConsole::write( QObject::tr(
"OpenGL: Trying to set %1, %2 viewport ( probably during printing ). <br>"
"OpenGL: This OpenGL implementation supports only %3, %4 viewports !<br>")
.arg(toInt(m_axes3->canvasRect().size.x))
.arg(toInt(m_axes3->canvasRect().size.y))
.arg(maxSize[0])
.arg(maxSize[1])
);
if ( pix ) {
delete pgl;
pix->resize( maxSize[0], maxSize[1] );
pgl = new QGLContext( f, pix );
pgl->create();
pgl->makeCurrent();
}
//cout << "KMatplot: Resizing done. " << endl;
}
if ( pix ) {
//cout << "KMatplot: Clear pixmap. " << endl;
QPainter p( pix );
p.fillRect( pix->rect(), white );
}
}
//-------------------------------------------------------------//
void QSDrvOpenGL::stopDrawing()
{
//cout << "KMatplot: Finishing. " << endl;
// finish with GL
if ( pgl ) {
pgl->makeCurrent();
glFlush();
glDeleteLists( plist, 1 );
}
//cout << "KMatplot: Deleted gl lists. " << endl;
// finish with QPicture
if ( ppic ) ppic->end();
//cout << "KMatplot: QPicture ended. " << endl;
// draw pixmap on painter
if ( pix ) {
if ( pix->width() != m_axes3->canvasRect().size.x ||
pix->height() != m_axes3->canvasRect().size.y ) {
//cout << "KMatplot: Drawing pixmap.init " << endl;
double scalex = m_axes3->canvasRect().size.x/pix->width();
double scaley = m_axes3->canvasRect().size.y/pix->height();
bool xform = m_opaint->hasWorldXForm();
m_opaint->setWorldXForm( TRUE );
m_opaint->saveWorldMatrix();
m_opaint->scale( scalex, scaley );
// Not working when printing
//pix->setMask( pix->createHeuristicMask() );
//cout << "KMatplot: Drawing scaled pixmap. " << endl;
m_opaint->drawPixmap( (int )floor( 0.5 + 1.0/scalex * m_axes3->canvasRect().pos.x ),
(int )floor( 0.5 + 1.0/scaley * m_axes3->canvasRect().pos.y ),
*pix );
m_opaint->restoreWorldMatrix();
m_opaint->setWorldXForm( xform );
//cout << "KMatplot: Drawing scaled pixmap.done." << endl;
} else {
//cout << "KMatplot: Drawing pixmap. " << endl;
m_opaint->drawPixmap( toInt(m_axes3->canvasRect().pos.x),
toInt(m_axes3->canvasRect().pos.y),
*pix );
}
}
//cout << "KMatplot: Flushing QPicture!. " << endl;
// draw QPicture on painter
if ( pic ) m_opaint->drawPicture( *pic );
//restore oryginal painter
m_paint = m_opaint;
//cout << "KMatplot: Deleting objects !. " << endl;
m_opaint = NULL;
delete ppic; ppic = NULL;
delete pgl; pgl = NULL;
delete pix; pix = NULL;
delete pic; pic = NULL;
delete points; points = NULL;
pasize = 0;
QSDrvQt::stopDrawing();
//cout << "KMatplot: Drawing Finished !. \n\n\n" << endl;
}
//-------------------------------------------------------------//
void QSDrvOpenGL::setCurrentElement( int category, int element )
{
if ( category == QSAxes::DatasetCategory && element == 0 ) {
// Clear the buffer after axis box is drawn, but before the first
// dataset is drawn.Depth test with the axis box is not needed because
// all datasets are clipped already to the axis box
glClear( GL_DEPTH_BUFFER_BIT );
glDisable( GL_CULL_FACE );
//cout << " clearing a depth buffer bit " << endl;
}
QSDrvQt::setCurrentElement( category, element );
}
//-------------------------------------------------------------//
void QSDrvOpenGL::startDrawing()
{
//cout << "KMatplot: Start drawing " << endl;
QSDrvQt::startDrawing();
//
// Activate context
//
pgl->makeCurrent();
axis_mode = m_axes3->axesOnly();
//if ( stage > 0 ) trv = m_global_transparency; else trv = 0;
trv = m_global_transparency;
//
// Dont mess the plot with the axis box.
//
glClear( GL_DEPTH_BUFFER_BIT );
//
// Apply modelview matrix.
//
double MS[4][4];
glMatrixMode( GL_MODELVIEW );
t->copy( MS, t->M );
loadMatrix( MS );
glError();
//
// Apply projection matrix.
//
double PS[4][4];
glMatrixMode( GL_PROJECTION );
t->copy( PS, t->P );
loadMatrix( PS );
glError();
//
// now our plot is drawn in ( -1.0, -1.0, 0.0 ) ( 1.0, 1.0, 1.0 ) box
// an we must rescale its x,y sizes to the viewport, but unfortunately
// the viewpor is given so, that (0,0) is at the top-left corner, and
// OpenGL accepts coordinates starting at the bottom-left corner.
//
//
// Apply viewport matrix.
//
double x;
double y;
double w;
double h;
// the same as canvas size and pos.
t->getViewport( &x,
&y,
&w,
&h,
NULL,
NULL );
glError();
// -1 because of pixmap printing
if ( pix )
glViewport( GLint(0),
GLint(0),
GLsizei(pix->width()-1),
GLsizei(pix->height()-1) );
else
glViewport( GLint(x),
GLint(m_paint->viewport().height()-h-y),
GLsizei(w-1),
GLsizei(h-1) );
glError();
//
//
//
glEnable(GL_POLYGON_OFFSET_FILL);
glPolygonOffset(1.0, 1.0);
glEnable( GL_DEPTH_TEST );
glDepthFunc( GL_LESS );
glShadeModel(GL_SMOOTH);
if ( t->light() ) {
GLfloat params[4];
QSGColor color;
if ( !m_shade_walls ) {
glDisable( GL_LIGHTING );
glDisable( GL_COLOR_MATERIAL );
} else {
glEnable(GL_LIGHTING);
glError();
glEnable( GL_COLOR_MATERIAL );
glError();
}
//
// light settings
//
//glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, 1);
glFrontFace( GL_CW );
glEnable( GL_CULL_FACE );
//glEnable( GL_NORMALIZE );
glLightModeli( GL_LIGHT_MODEL_TWO_SIDE, 1 );
//
// lightness ( ambient light )
//
glEnable(GL_LIGHT0);
// This simply looks good, no special theory.
params[0] = params[1] = params[2] = 0.35 +
(t->ambientLight()+50.0)/64.0 -
(t->directedLight()+50.0)/64.0;
params[3] = 1.0;
glLightModelfv( GL_LIGHT_MODEL_AMBIENT, params );
glError();
//
// light intensity ( directional light )
//
GLfloat ldir[4];
ldir[0] = (GLfloat )t->lvector.x;
ldir[1] = (GLfloat )t->lvector.y;
ldir[2] = (GLfloat )t->lvector.z;
ldir[3] = (GLfloat )0.0;
glLightfv(GL_LIGHT0, GL_POSITION, ldir );
//cout <<" LIGHT VECTOR " << t->lvector.x << "," << t->lvector.y << ","<<t->lvector.z<<endl;
glError();
GLfloat lint[4];
lint[0] = lint[1] = lint[2] = (t->directedLight()+50.0)/48.0;
lint[3] = 1.0;
glLightfv(GL_LIGHT0, GL_DIFFUSE, lint );
glError();
glLightfv(GL_LIGHT0, GL_SPECULAR, lint );
glError();
//glLightfv(GL_LIGHT0, GL_AMBIENT, lint );
//glLightf(GL_LIGHT0, GL_SPOT_CUTOFF, 80.0 );
//
// top
//
glColorMaterial( GL_FRONT, GL_AMBIENT_AND_DIFFUSE );
//
// bottom
//
color = m_bottom_fill.color;
params[0] = color.r/255.0;
params[1] = color.g/255.0;
params[2] = color.b/255.0;
params[3] = (color.a-trv)/255.0;
glMaterialfv( GL_BACK, GL_AMBIENT_AND_DIFFUSE, params );
glError();
} else {
glDisable( GL_LIGHTING );
glDisable( GL_COLOR_MATERIAL );
}
if ( m_alpha ) {
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
}
// only one time
//if ( stage == 0 )
plist = glGenLists(1);
glError();
//cout << "KMatplot: start drawing done. " << endl;
}
//-------------------------------------------------------------//
void QSDrvOpenGL::setClipping( bool enabled )
{
QSDrvQt::setClipping( enabled );
if ( enabled ) {
// x y z w >= 0
double cp1[4] = { 0.0, 0.0, 1.0, 0.0 }; // z >= 0
double cp2[4] = { 0.0, 0.0, -1.0, 1.0 }; // z <= 1
double cp3[4] = { 1.0, 0.0, 0.0, 0.0 }; // x >= 0
double cp4[4] = { -1.0, 0.0, 0.0, 1.0 }; // x <= 1
double cp5[4] = { 0.0, 1.0, 0.0, 0.0 }; // y >= 0
double cp6[4] = { 0.0, -1.0, 0.0, 1.0 }; // y <= 1
glClipPlane( GL_CLIP_PLANE0, cp1 );
glClipPlane( GL_CLIP_PLANE1, cp2 );
glClipPlane( GL_CLIP_PLANE2, cp3 );
glClipPlane( GL_CLIP_PLANE3, cp4 );
glClipPlane( GL_CLIP_PLANE4, cp5 );
glClipPlane( GL_CLIP_PLANE5, cp6 );
glEnable( GL_CLIP_PLANE0 );
glEnable( GL_CLIP_PLANE1 );
glEnable( GL_CLIP_PLANE2 );
glEnable( GL_CLIP_PLANE3 );
glEnable( GL_CLIP_PLANE4 );
glEnable( GL_CLIP_PLANE5 );
} else {
glDisable( GL_CLIP_PLANE0 );
glDisable( GL_CLIP_PLANE1 );
glDisable( GL_CLIP_PLANE2 );
glDisable( GL_CLIP_PLANE3 );
glDisable( GL_CLIP_PLANE4 );
glDisable( GL_CLIP_PLANE5 );
}
}
//-------------------------------------------------------------//
void QSDrvOpenGL::clearCanvas( const QSGFill& f, const QSPt2&, const QSPt2& )
{
pgl->makeCurrent();
glClear( GL_DEPTH_BUFFER_BIT );
if ( f.style != QSGFill::Transparent ) {
glClearColor( f.color.r / 255.0,
f.color.g / 255.0,
f.color.b / 255.0,
f.color.a / 255.0 );
glClear( GL_COLOR_BUFFER_BIT );
}
lcolor[0] = GLubyte(0);
lcolor[1] = GLubyte(0);
lcolor[2] = GLubyte(0);
lcolor[3] = GLubyte(0);
}
//-------------------------------------------------------------//
#define EPS 1e-15
void QSDrvOpenGL::drawPoly3( const QSPt3f pts[], int npoints,
const QSPt3f *normals, const QSGFill *colors, const bool *edges, int edgeAutoColor )
{
int i;
pgl->makeCurrent();
if ( !axis_mode && colors[0].style != QSGFill::Transparent ) {
glPolygonMode( GL_FRONT_AND_BACK, GL_FILL );
glBegin( GL_POLYGON );
for ( i=0; i<npoints; i++ ) {
glColor4ub( colors[i].color.r,
colors[i].color.g,
colors[i].color.b,
(GLubyte )max(int(colors[i].color.a)-trv,0));
glNormal3d( normals[i+1].x,
normals[i+1].y,
normals[i+1].z );
if ( i == 0 ||
fabs(pts[i].x-pts[i-1].x) > EPS ||
fabs(pts[i].y-pts[i-1].y) > EPS ||
fabs(pts[i].z-pts[i-1].z) > EPS )
glVertex3d( pts[i].x, pts[i].y, pts[i].z );
}
glEnd();
}
if ( lcolor[3] ) {
glPolygonMode( GL_FRONT_AND_BACK, GL_LINE );
glBegin( GL_POLYGON );
if ( !m_auto_stroke ) glColor4ubv( lcolor );
for ( i=0; i<npoints; i++ ) {
if ( m_auto_stroke ) {
int r = colors[i].color.r+m_stroke_lightness;
int g = colors[i].color.g+m_stroke_lightness;
int b = colors[i].color.b+m_stroke_lightness;
if ( m_stroke_lightness > 0 ) {
r = min(r,255);
g = min(g,255);
b = min(b,255);
}
else
if ( m_stroke_lightness < 0 ) {
r = max(r,0);
g = max(g,0);
b = max(b,0);
}
glColor4ub( (GLubyte )r, (GLubyte )g, (GLubyte )b, colors[i].color.a );
}
glNormal3d( normals[i+1].x,
normals[i+1].y,
normals[i+1].z );
if ( i == 0 ||
fabs(pts[i].x-pts[i-1].x) > EPS ||
fabs(pts[i].y-pts[i-1].y) > EPS ||
fabs(pts[i].z-pts[i-1].z) > EPS )
glVertex3d( pts[i].x, pts[i].y, pts[i].z );
}
glEnd();
}
}
//-------------------------------------------------------------//
void QSDrvOpenGL::drawLine3( const QSPt3f& begin, const QSPt3f& end, const QSPt3f norm[2] )
{
if ( lcolor[3] ) {
pgl->makeCurrent();
glBegin( GL_LINES );
glColor4ubv( lcolor );
// first
if ( norm )
glNormal3d( norm[0].x,
norm[0].y,
norm[0].z );
glVertex3d( begin.x, begin.y, begin.z );
// end
if ( norm )
glNormal3d( norm[1].x,
norm[1].y,
norm[1].z );
glVertex3d( end.x, end.y, end.z );
//cout << " GL line from "<<begin.x<<","<<begin.y<<","<<begin.z<<" - "<<end.x<<","<<end.y<<","<<end.z<<endl;
glEnd();
}
}
//-------------------------------------------------------------//
void QSDrvOpenGL::setFill( const QSGFill &f )
// Obsolete. All fill setting is done in drawPoly3()
{
glColor4ub( f.color.r,
f.color.g,
f.color.b,
f.color.a );
// if stage = 2
QSDrvQt::setFill( f );
}
//-------------------------------------------------------------//
void QSDrvOpenGL::setLine( const QSGLine &l )
{
glLineWidth( l.width?toPixels(l.width):0.1 );
lcolor[0] = GLubyte(l.color.r);
lcolor[1] = GLubyte(l.color.g);
lcolor[2] = GLubyte(l.color.b);
lcolor[3] = ( l.style == QSGLine::Invisible ? 0 : GLubyte(l.color.a) );
// if stage == 2
QSDrvQt::setLine( l );
}
//-------------------------------------------------------------//
#endif // HAVE_GL
//glEnable( GL_LINE_SMOOTH );
//glShadeModel( GL_FLAT );
//glEnable( GL_LINE_SMOOTH );
//glEnable( GL_LINE_SMOOTH );
//glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
//glHint (GL_LINE_SMOOTH_HINT, GL_DONT_CARE);
//glPolygonMode( GL_FRONT_AND_BACK, GL_FILL );
//glLineWidth( 0.5 ) ;
/*
if ( stage == 0 && !m_shade_walls ) {
glDisable( GL_LIGHTING );
glDisable( GL_COLOR_MATERIAL );
} else {
glEnable(GL_LIGHTING);
glEnable( GL_COLOR_MATERIAL );
}
*/