/***********************************************************************
*
* ELMER, A Computational Fluid Dynamics Program.
*
* Copyright 1st April 1995 - , Center for Scientific Computing,
* Finland.
*
* All rights reserved. No part of this program may be used,
* reproduced or transmitted in any form or by any means
* without the written permission of CSC.
*
* Address: Center for Scientific Computing
* Tietotie 6, P.O. BOX 405
* 02101 Espoo, Finland
* Tel. +358 0 457 2001
* Telefax: +358 0 457 2302
* EMail: Jari.Jarvinen@csc.fi
************************************************************************/
/***********************************************************************
Program: ELMER Front
Module: ecif_renderer_ogl.cpp
Language: C++
Date: 01.10.98
Version: 1.00
Author(s): Martti Verho
Revisions:
Abstract: Implementation
NOTE: Platform specific (WIN32, UNIX) parts are implemented
in WIN32/Unix specific *.hpp files which are included later
in this file depending on the macro value WIN32.
************************************************************************/
#include "ecif_body.h"
#include "ecif_bodyElement.h"
#include "ecif_control.h"
#include "ecif_geometry.h"
#include "ecif_mesh.h"
#include "ecif_model.h"
#include "ecif_modelObject.h"
#include "ecif_renderer_OGL.h"
#include "ecif_userinterface.h"
#include "ecif_timer.h"
static char oglWinClass [] = "ModelWindow";
GLParam::GLParam()
{
lineWidthCoordAxis = 1.0f;
lineWidthThin = 0.0f;
lineWidthNormal = 1.0f;
lineWidthSelected = 3.0f;
};
void Callback
Renderer_OGL::errorCallback(GLenum errorCode)
{
const GLubyte *estring;
estring = gluErrorString(errorCode);
strstream strm;
strm << "OpenGL Error: \n" << estring << ends;
theControlCenter->getUI()->showMsg(strm.str());
}
// Constructor
Renderer_OGL::Renderer_OGL(Hinst app_instance, ecif_modelDimension dim)
{
appInstance = app_instance;
if (dim == ECIF_2D) {
is2D = true;
is2DSimulation = true;
} else {
is2D = false;
is2DSimulation = false;
}
useOrthoProjection = true;
visible = false;
createData();
// Init environment
attachRenderer();
setRendererInfo();
init();
// Displaylist for printing digits
makeRasterFont();
}
Renderer_OGL::~Renderer_OGL()
{
destroyWindow(rendererDisplay, rendererWindow);
deleteData();
}
// Method attaches the renderer object with a window.
void
Renderer_OGL::attachRenderer()
{
if (status == HAS_NO_WINDOW){
// Create GL-window.
WindowInfo winfo;
createGLWindow(appInstance, "Model", 0, 0, winX, winY, winfo);
rendererDisplay = winfo.display;
rendererWindow = winfo.window;
status = HAS_WINDOW;
}
}
void
Renderer_OGL::bodySelectionHits()
{
if ( selectionHits == 0 ) {
return;
}
int picked_body_id = NO_INDEX;
int picked_lr_id = NO_INDEX;
GLuint tot_min_z = 0xffffffff;
GLuint tot_max_z = 0;
int record_start = 0;
for (int i = 0; i < selectionHits; i++) {
int nof_names = selectionBuffer[record_start];
if (nof_names == 0) {
break;
}
GLuint min_z = selectionBuffer[record_start + 1];
GLuint max_z = selectionBuffer[record_start + 2];
int body_id = selectionBuffer[record_start + 3];
int lr_id = selectionBuffer[record_start + 4];
// NOTE: 3 covers: nofNames, minZ, maxZ and
// nof_names covers all ids found for pick
//
record_start += 3 + nof_names;
if ( min_z < tot_min_z ) {
tot_min_z = min_z;
picked_body_id = body_id;
picked_lr_id = lr_id;
}
}
if ( picked_body_id != NO_INDEX ) {
model->bodySelected(this, picked_body_id, picked_lr_id);
}
selectionHits = 0;
}
void
Renderer_OGL::boundarySelectionHits()
{
if ( selectionHits == 0) {
return;
}
int max_nof_names = -1;
int picked_bd1_id = NO_INDEX;
int picked_bd2_id = NO_INDEX;
int picked_lr1_id = NO_INDEX;
int picked_lr2_id = NO_INDEX;
int picked_bndr_id = NO_INDEX;
bool vertex_picked = false;
GLuint tot_min_z = 0xffffffff;
GLuint tot_max_z = 0;
int record_start = 0;
for (int i = 0; i < selectionHits; i++) {
int nof_names = selectionBuffer[record_start];
if (nof_names == 0) {
break;
}
bool higer_found = false;
if ( is2D && nof_names == 4 ) {
vertex_picked = true;
} else if ( nof_names == 5 ) {
vertex_picked = true;
}
GLuint min_z = selectionBuffer[record_start + 1];
GLuint max_z = selectionBuffer[record_start + 2];
if ( max_z > tot_max_z ) {
tot_max_z = max_z;
higer_found = true;
}
// First parent
//
if ( picked_bd1_id == NO_INDEX || higer_found ) {
picked_bd1_id = selectionBuffer[record_start + 3];
picked_lr1_id = selectionBuffer[record_start + 4];
if ( nof_names > 2 ) {
picked_bndr_id = selectionBuffer[record_start + 2 + nof_names];
}
}
// Second parent
//
if ( nof_names > 2 &&
picked_bd1_id != selectionBuffer[record_start + 3] &&
picked_bndr_id == selectionBuffer[record_start + 2 + nof_names] &&
picked_bd2_id == NO_INDEX
) {
picked_bd2_id = selectionBuffer[record_start + 3];
picked_lr2_id = selectionBuffer[record_start + 4];
}
// If a deeper level name encountered
if ( nof_names > max_nof_names ) {
max_nof_names = nof_names;
picked_bndr_id = selectionBuffer[record_start + 2 + nof_names];
}
// NOTE: 3 covers: nofNames, minZ, maxZ and
// nof_names covers all ids found for pick
//
record_start += 3 + nof_names;
}
// If picked found
if ( picked_bndr_id != NO_INDEX ) {
if ( vertex_picked ) {
//model->setCurrentAnchorPoint(picked_bndr_id);
}
model->boundarySelected(this, picked_bndr_id,
picked_bd1_id, picked_lr1_id,
picked_bd2_id, picked_lr2_id);
}
selectionHits = 0;
}
void
Renderer_OGL::boundaryEdgeSelectionHits()
{
// Nothing so far!
return;
}
void
Renderer_OGL::boundaryVertexSelectionHits()
{
// Nothing so far!
return;
}
// Clean buffers
void
Renderer_OGL::clean()
{
// Background color to white. Clear buffers.
glClearColor(1.0f, 1.0f, 1.0f, 0.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
}
// Clear screen
void
Renderer_OGL::clear()
{
clean();
draw();
}
// Constructor
void
Renderer_OGL::createData()
{
displayListIds = new IdList;
tesselator = gluNewTess();
tesselatorPoints = new Point3List;
gluTessCallback(tesselator, (GLenum)GLU_BEGIN, (GLvoid (Callback*)()) &glBegin);
gluTessCallback(tesselator, (GLenum)GLU_VERTEX, (GLvoid (Callback*)()) &glVertex3dv);
gluTessCallback(tesselator, (GLenum)GLU_END, &glEnd);
gluTessCallback(tesselator, (GLenum)GLU_ERROR, (GLvoid (Callback*) ()) &errorCallback);
pointBuffer = new double*[MAX_NOF_NODES];
selectionBufferSize = 1024;
selectionBuffer = new GLuint[selectionBufferSize];
selectionHits = 0;
Color4 current_color = {0.0f, 0.0f, 0.0f, 1.0f}; // black
Color4 mesh_color = {0.0f, 0.0f, 0.0f, 1.0f}; // black
Color4 select_color = {1.0f, 1.0f, 1.0f, 1.0f}; // white
// Set initial values colors
for (int i = 0; i < 4; i++) {
currentColor[i] = current_color[i];
meshColor[i] = mesh_color[i];
selectColor[i] = select_color[i];
//selectColor[i] = float(colorValues[0][i]) / MAX_NOF_COLOR_LEVELS;
}
inBoxDrawMode = false;
inLineDrawMode = false;
inMeshEditingMode = false;
inMeshPickingMode = false;
inVectorDrawMode = true;
mkState = 0;
// Initial window size
winX = 450;
winY = 400;
winZ = 2; // (-1,1);
// Initially no rotation priorities, so rotation
// by the mouse movements in the screen is for X,Y axis
rotateAxisX = false;
rotateAxisY = false;
rotateAxisZ = false;
// Timers
doubleClickTimer = new Timer;
mouseMoveTimer = new Timer;
doubleClickTimer->start();
mouseMoveTimer->start();
}
void
Renderer_OGL::deleteData()
{
delete[] pointBuffer;
gluDeleteTess(tesselator);
deleteDisplayLists();
delete displayListIds; // container itself
deleteTesselatorPoints();
delete tesselatorPoints; // container itself
delete doubleClickTimer;
delete mouseMoveTimer;
}
void
Renderer_OGL::deleteDisplayList(int list_id)
{
glDeleteLists(list_id, 1);
}
void
Renderer_OGL::deleteDisplayLists()
{
while ( !displayListIds->empty() ) {
deleteDisplayList(displayListIds->back());
displayListIds->pop_back();
}
}
void
Renderer_OGL::deleteTesselatorPoints()
{
while ( !tesselatorPoints->empty() ) {
Point3* point = tesselatorPoints->back();
delete[] point;
tesselatorPoints->pop_back();
}
}
// *** This function does the actual painting of the OpenGL-window.
void Callback
Renderer_OGL::display(Renderer* renderer)
{
#if 0
static int counter = 0;
strstream strm;
strm << "Now doing display " << ++counter << endl << ends;
theUI->showMsg(strm.str());
#endif
renderer->clean();
renderer->transform_scene();
renderer->drawAllBodies();
renderer->drawCoordinateAxis(0, 0);
renderer->draw();
}
void
Renderer_OGL::displayRenderer()
{
attachRenderer();
status = SHOW;
paintRenderer();
}
// **************************
// *** Model drawing loop ***
// **************************
// Draw bodies as filled surfaces or
// filled boundary mesh elements
void
Renderer_OGL::drawAllBodies()
{
bool is_drawing_cad = false;
bool is_drawing_mesh = false;
flagName geometries[2];
short nof_geometries = 0;
if (model->getFlagValue(DRAW_SOURCE_CAD)) {
geometries[nof_geometries] = DRAW_SOURCE_CAD;
nof_geometries++;
is_drawing_cad = true;
}
if (model->getFlagValue(DRAW_SOURCE_MESH)) {
geometries[nof_geometries] = DRAW_SOURCE_MESH;
nof_geometries++;
is_drawing_mesh = true;
}
if ( nof_geometries == 0) return;
// Possible selection hits
if ( selectionHits > 0 ) {
if ( model->getFlagValue(DRAW_TARGET_BODIES) ) {
bodySelectionHits();
} else if ( model->getFlagValue(DRAW_TARGET_BOUNDARIES) ) {
boundarySelectionHits();
} else if ( model->getFlagValue(DRAW_TARGET_BOUNDARY_EDGES) ) {
boundaryEdgeSelectionHits();
} else if ( model->getFlagValue(DRAW_TARGET_BOUNDARY_VERTICES) ) {
boundaryVertexSelectionHits();
}
selectionHits = 0;
}
setDrawMode();
bool draw_done = false;
// Init possible mesh elements for renderering
if (is_drawing_mesh) {
model->resetMeshRendered();
model->resetMeshEdgesSelected();
}
//---Draw all selected geometries
short geometry = 0;
while ( geometry < nof_geometries ) {
//glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
//--Start body loop
objectDrawingMode dmode;
objectDrawingState dstate;
int index = 0;
while (true) {
Body* body = model->getBody(index++);
if (body==NULL) break;
dmode = body->getDrawMode();
dstate = body->getDrawState();
if ( dmode == DM_HIDDEN) continue;
// If we draw both Cad and Mesh, we draw Cad
// as wireframe bodies to make mesh visible!
if ( (nof_geometries == 2 && geometries[geometry] == DRAW_SOURCE_CAD) ||
model->getFlagValue(DRAW_TARGET_BOUNDARIES)
) {
dmode = DM_WIREFRAME;
}
//-Set body color etc.
// Normal drawing mode
if ( dstate == DM_NORMAL || model->getFlagValue(DRAW_TARGET_BOUNDARIES) ) {
body->getColor(currentColor);
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, currentColor);
//glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, currentColor);
//glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 30.0);
// Selected drawing mode
} else {
glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, selectColor);
}
//-Draw body
//glEnable(GL_BLEND);
body->draw(this, geometries[geometry], dmode);
//glDisable(GL_BLEND);
} //End body loop
geometry++;
} // End geometries loop
//---Draw element labels
// NOTE This must done last (not to be overwritten!)
bool only_active = true;
bool draw_sub_labels = true;
int index = 0;
while (true) {
BodyElement* be = model->getBoundary(index++, only_active);
if (be==NULL) break;
Body* bd1 = model->getBodyById(be->getParentId(1));
Body* bd2 = model->getBodyById(be->getParentId(2));
bool bd1_draw = true; // Parent body1 draw flag
bool bd2_draw = true; // Parent body2 draw flag
bool be_draw = true; // Boundary draw flag
if ( bd1 == NULL || DM_HIDDEN == bd1->getDrawMode() ) {
bd1_draw = false;
}
if ( bd2 == NULL || DM_HIDDEN == bd2->getDrawMode() ) {
bd2_draw = false;
}
// A selected element is not 'hidden'
if ( DM_HIDDEN == be->getDrawMode() &&
DS_SELECTED != be->getDrawState()
) {
be_draw = false;
}
// Draw label (if element not hidden)
if ( be_draw && (bd1_draw || bd2_draw) ) {
be->drawLabel(this, draw_sub_labels);
}
}
// Just a test!!!
//model->drawCurrentPickVector();
}
// Draw bodies as bulk mesh elements
void
Renderer_OGL::drawAllMeshBodies()
{
if ( !model->getFlagValue(GEOMETRY_TYPE_MESH) ) {
return;
}
// Non-filled
//glPolygonMode(GL_FRONT, GL_FILL);
//glPolygonMode(GL_BACK, GL_FILL);
glDisable(GL_DEPTH_TEST);
glDisable(GL_LINE_SMOOTH);
glLineWidth(glParam.lineWidthNormal);
//-Use black color
Color4f meshColor = {0.0f, 0.0f, 0.0f, 1.0f};
glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, meshColor);
#if 0
if ( glIsList(500) ) {
glCallList(500);
return;
}
glNewList(500, GL_COMPILE );
#endif
#if 0
model->drawMesh();
return;
#endif
model->resetMeshRendered();
//---Start body loop
int index = 0;
while (true) {
Body* body = model->getBody(index++);
if (body==NULL) break;
// Set body color.
//Color4f color;
//body->getColor(color);
//glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color);
//glColor4fv(color);
if (DM_HIDDEN != body->getDrawMode()) {
body->draw(this, DRAW_SOURCE_MESH, DM_NORMAL);
}
}
#if 0
glEndList();
#endif
//End body loop
//glEnable(GL_LINE_SMOOTH);
}
void
Renderer_OGL::mouse_clck_action(int mk_state, int x1, int y1)
{
if ( mk_state & MK_CONTROL )
theControlCenter->getRenderer()->processSelection(MOUSE_CTRL_CLCK, mk_state, x1, y1);
//else if ( mk_state & MK_SHIFT)
// theControlCenter->getRenderer()->processSelection(MOUSE_SHFT_CLCK, mk_state, x1, y1);
}
void
Renderer_OGL::mouse_dblclck_action(int mk_state, int x1, int y1)
{
if ( mk_state & MK_CONTROL )
theControlCenter->getRenderer()->processSelection(MOUSE_CTRL_DBL_CLCK, mk_state, x1, y1);
else
theControlCenter->getRenderer()->processSelection(MOUSE_DBL_CLCK, mk_state, x1, y1);
}
void
Renderer_OGL::mouse_move_action(int mk_state, int x1, int x2, int y1, int y2)
{
static int curr_axis = -1;
static int prev_pos_x = 0;
static int prev_pos_y = 0;
int amount;
int axis;
int code;
int del_x = x2 - x1;
int del_y = y2 - y1;
int pos_x, pos_y;
char* time_buffer;
double time;
int decimal, sign;
switch (mk_state) {
case MK_SHIFT:
#if 0
// Time in milliseconds
time = mouseMoveTimer->getLapTime();
//time_buffer = _fcvt(time, 10, &decimal, &sign);
//theUI->showMsg(time_buffer);
if (time > 0.05) {
mouseMoveTimer->stop();
theControlCenter->getRenderer()->processSelection(MOUSE_SHFT_MOVE, mk_state, x1, y1);
mouseMoveTimer->start();
}
#endif
#if 1
break; // Pure shift+move does not select any more, it is now shift+button+move
theControlCenter->getRenderer()->processSelection(MOUSE_SHFT_MOVE, mk_state, x1, y1);
#endif
break;
// Selection (shift+left down) or
// Move (translate) (only left down)
case MK_LBUTTON | MK_SHIFT:
case MK_RBUTTON | MK_SHIFT:
case MK_LBUTTON :
if ( mk_state & MK_SHIFT ) {
theControlCenter->getRenderer()->processSelection(MOUSE_SHFT_MOVE, mk_state, x1, y1);
}
else {
theControlCenter->getRenderer()->translate(del_x, del_y, 0);
}
break;
// Rotation (right button down)
case MK_RBUTTON:
// If some priority is set, we use it
axis = -1;
theControlCenter->getRenderer()->getRotateAxis(axis);
if ( axis != -1 ) {
curr_axis = axis;
}
pos_x = (del_x < 0)?-del_x:del_x;
pos_y = (del_y < 0)?-del_y:del_y;
// No axis set
if ( axis == -1 ) {
// Change axis if mouse direction is changed relevantly!
if ( abs(pos_y - pos_x) > 1.05 * abs(prev_pos_y - prev_pos_x) ) {
curr_axis = (pos_x >= pos_y)?1:0;
}
amount = (curr_axis == 1)?del_x:-del_y;
// Axis set
} else {
amount = (curr_axis == 1)?del_x:-del_y;
}
theControlCenter->getRenderer()->rotate(curr_axis, amount);
break;
// Scale (both buttons down)
case (MK_LBUTTON | MK_RBUTTON):
case MK_MBUTTON:
theControlCenter->getRenderer()->scale(del_y);
break;
default:
break;
}
prev_pos_x = pos_x;
prev_pos_y = pos_y;
}
void
Renderer_OGL::startDisplayList(int list_id)
{
//glNewList(list_id, GL_COMPILE );
glNewList(list_id, GL_COMPILE_AND_EXECUTE);
storeDisplayListId(list_id);
}
void
Renderer_OGL::startDrawingCadBody()
{
if (!is2D) {
return;
}
gluBeginPolygon(tesselator);
}
void
Renderer_OGL::startDrawingCadBodyElementLoop(bool is_first_loop)
{
if (!is2D) {
return;
}
if (is_first_loop)
gluNextContour(tesselator, (GLenum)GLU_EXTERIOR);
else
gluNextContour(tesselator, (GLenum)GLU_INTERIOR);
}
void
Renderer_OGL::startDrawingCadSurface()
{
if (!is2D) {
glDepthFunc(GL_LEQUAL);
}
}
void
Renderer_OGL::startDrawingMeshSurface()
{
if (!is2D) {
glDepthFunc(GL_LEQUAL);
}
//glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, meshColor);
}
void
Renderer_OGL::startDrawingMeshSurfaceEdges()
{
if (!is2D) {
glDepthFunc(GL_LEQUAL);
}
glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, meshColor);
}
void
Renderer_OGL::stopDisplayList(int list_id)
{
glEndList();
}
void
Renderer_OGL::stopDrawingCadBody()
{
if (!is2D) {
return;
}
glFlush();
gluEndPolygon(tesselator);
}
void
Renderer_OGL::stopDrawingCadBodyElementLoop()
{
if (!is2D) {
return;
}
}
void
Renderer_OGL::stopDrawingCadSurface()
{
}
void
Renderer_OGL::stopDrawingMeshSurface()
{
//glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, currentColor);
}
void
Renderer_OGL::stopDrawingMeshSurfaceEdges()
{
glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, currentColor);
}
void
Renderer_OGL::storeDisplayListId(int id)
{
displayListIds->push_back(id);
}
void
Renderer_OGL::storeTesselatorPoint(Point3* point)
{
tesselatorPoints->push_back(point);
}
void
Renderer_OGL::drawCoordinateAxis(int x_pos, int y_pos)
{
if ( winX == 0 || winY == 0 )
return;
static double len = 0.15; // length of the axis
static double fac = 1.05; // for the position of the labels
static double pos = -1.0 + len; // Position from the SW-corner
static double origo[3] = {0.0, 0.0, 0.0};
static double axis_x[3] = {1.0, 0.0, 0.0};
static double axis_y[3] = {0.0, 1.0, 0.0};
static double axis_z[3] = {0.0, 0.0, 1.0};
static float color_x[4] = {1.0f, 0.0f, 0.0f, 1.0f}; // Red
static float color_y[4] = {0.0f, 1.0f, 0.0f, 1.0f}; // Green
static float color_z[4] = {0.0f, 0.0f, 1.0f, 1.0f}; // Blue
static float color_lbl[4] = {0.0f, 0.0f, 0.0f, 1.0f}; // Black
static char label_x[21];
static char label_y[21];
static char label_z[21];
model->getCoordinateLabels(20, label_x, label_y, label_z);
// Correct y length by the window aspct ratio
double len_y = len * (double(winX) / winY);
axis_x[0] = len;
axis_y[1] = len_y;
axis_z[2] = len;
// Save current transformation and projection matrix
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();
glTranslated(pos, pos, 0.0);
// Use model rotation matrix for coordinates
rotate(0);
glLineWidth(glParam.lineWidthCoordAxis);
glDisable(GL_DEPTH_TEST);
glBegin(GL_LINES);
// x-axis
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color_x);
glVertex3dv(origo);
glVertex3dv(axis_x);
// y-axis
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color_y);
glVertex3dv(origo);
glVertex3dv(axis_y);
// z-axis
if (!is2D) {
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color_z);
glVertex3dv(origo);
glVertex3dv(axis_z);
}
glEnd();
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color_lbl);
glRasterPos3d(axis_x[0] * fac, 0.0, 0.0);
printString(label_x);
glRasterPos3d(0.0, axis_y[1] * fac, 0.0);
printString(label_y);
if (!is2DSimulation) {
glRasterPos3d(0.0, 0.0, axis_z[2] * fac);
printString(label_z);
}
glMatrixMode(GL_MODELVIEW);
glPopMatrix();
glMatrixMode(GL_PROJECTION);
glPopMatrix();
}
void
Renderer_OGL::drawElementLabel(char* lbl, Point3& lbl_p)
{
#if 0
//-Test if label is invisible
static GLfloat fb_buffer[4];
glFeedbackBuffer(4, GL_3D, fb_buffer);
glRenderMode(GL_FEEDBACK);
glBegin(GL_POINTS);
glVertex3d(lbl_p[0], lbl_p[1], lbl_p[2]);
glEnd();
int buffer_hits = glRenderMode(GL_RENDER);
if ( buffer_hits == 0) {
return;
}
#endif
//-Store state
glPushAttrib(GL_LIGHTING_BIT);
//-Use black color for the label
static Color4f label_color = {0.0f, 0.0f, 0.0f, 1.0f};
glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, label_color);
//-Label position.
glRasterPos3d(lbl_p[0], lbl_p[1], lbl_p[2]);
//-Render label
//glDisable(GL_DEPTH_TEST);
printString(lbl);
//glEnable(GL_DEPTH_TEST);
//-Restore state
glPopAttrib();
}
void
Renderer_OGL::drawLine(objectDrawingMode dmode, objectDrawingState dstate, short direction,
const Point3* start, const Point3* end, int elem_id)
{
// We must take into account bodyelement's orientation within body!.
// Here direction == 1 means ccw-order.
// Calling functions must take care of this ***!!!***
const Point3* p1;
const Point3* p2;
if (direction == 1) {
p1 = start;
p2 = end;
} else {
p2 = start;
p1 = end;
}
// Just a line, no trimming
if ( !is2D ||
dstate == DS_SELECTED ||
dmode == DM_WIREFRAME ||
model->getFlagValue(DRAW_TARGET_EDGES) ||
model->getFlagValue(DRAW_SOURCE_MESH)
) {
if (dstate == DS_SELECTED)
glLineWidth(glParam.lineWidthSelected);
else
glLineWidth(glParam.lineWidthNormal);
if ( dmode == DM_INTRA_LAYER ) {
glColor4d(1.0, 1.0, 1.0, 1.0);
}
glBegin(GL_LINES);
glVertex3dv(*p1);
glVertex3dv(*p2);
glEnd();
// Tesselating a polygon
} else if ( is2D && dmode == DM_NORMAL ) {
// NOTE: Tesselator object does not make a copy of the data!
double* point = new double[3];
point[0] = (*p1)[0];
point[1] = (*p1)[1];
point[2] = (*p1)[2];
gluTessVertex(tesselator, point, point);
// Drawing as a polygon (only first vertex!)
} else {
if ( dmode == DM_INTRA_LAYER )
glLineWidth(glParam.lineWidthThin);
glVertex3dv(*p1);
}
}
// Draw one mesh boundary element
//
// NOTE 1: Line element is the only element type where
// possible middle node (ie. for 203) is drawn. This means that
// middle nodes are seen in wireframe modes, because bulk elements
// are finally drawn usin there edges (line elements). When drawing shaded
// geometry (surfaces), we do not use the middle nodes.
//
// NOTE 2: Possible center node is never drawn!
// In wireframe mode this would need special handling, because we draw in
// practice only line elements and they know nothing about the center node, which
// is strictly a bulk element level concept! To draw the center node, bulk element
// should consequently handle it directly!
void
Renderer_OGL::drawMeshElement(int elem_type,
const int* node_ids,
const Point3* nodeNormals,
const Point3* nodes,
short direction,
bool selected)
{
// Vertex
// ======
if (elem_type == 101 ) {
drawMeshVertexElement(elem_type, node_ids, nodes, selected);
// Lines
// =====
} else if (elem_type >= 202 && elem_type < 300) {
if (nodeNormals != NULL) {
double* n = (double*)nodeNormals;
glNormal3d(n[0], n[1], n[2]);
}
drawMeshLineElement(elem_type, node_ids, nodes, selected);
// Triangles
// =========
} else if (elem_type >= 303 && elem_type < 400) {
drawMeshTriangleElement(elem_type, node_ids, nodes, nodeNormals, direction, selected);
// Quadrilaterals
// ==============
} else if (elem_type >= 404 && elem_type < 500) {
drawMeshQuadriElement(elem_type, node_ids, nodes, nodeNormals, direction, selected);
}
}
// Draw one mesh boundary element
void
Renderer_OGL::drawMeshBoundaryElement(int elem_type, const int* elem_nodes,
objectDrawingState dstate,
const Point3* nodeNormals,
const Point3* node_data,
short direction)
{
static double color[4];
// Set highlite color mode
if (dstate == DS_SELECTED) {
glGetDoublev(GL_CURRENT_COLOR, color);
color[3] = 0.5;
glColor4dv(color);
}
drawMeshElement(elem_type, elem_nodes, nodeNormals, node_data, direction);
// Reset color mode
if (dstate == DS_SELECTED) {
color[3] = 1.0;
glColor4dv(color);
}
}
// Draw one mesh bulk element
void
Renderer_OGL::drawMeshBulkElement(int elem_type, const int* elem_nodes,
objectDrawingState dstate,
const Point3* node_data)
{
if (dstate == DS_SELECTED) {
glPolygonMode(GL_FRONT, GL_FILL);
}
drawMeshElement(elem_type, elem_nodes, NULL, node_data);
if (dstate == DS_SELECTED) {
glPolygonMode(GL_FRONT, GL_LINE);
}
}
void
Renderer_OGL::drawMeshLineElement(int elem_type, const int* node_ids, const Point3* nodes,
bool selected)
{
if (selected) {
glLineWidth(glParam.lineWidthSelected);
}
glBegin(GL_LINE_STRIP);
// Three nodes
if ( elem_type == 203 ) {
glVertex3dv(nodes[node_ids[0]]);
glVertex3dv(nodes[node_ids[2]]);
glVertex3dv(nodes[node_ids[1]]);
// Two nodes
} else {
glVertex3dv(nodes[node_ids[0]]);
glVertex3dv(nodes[node_ids[1]]);
}
glEnd();
// Draw possible middle point
if ( elem_type == 203 ) {
glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, meshColor);
glPointSize(1.5f);
glBegin(GL_POINTS);
glVertex3dv(nodes[node_ids[2]]);
glEnd();
glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, currentColor);
glPointSize(1.0f);
}
if (selected) {
glLineWidth(glParam.lineWidthNormal);
}
}
void
Renderer_OGL::drawMeshTriangleElement(int elem_type, const int* node_ids,
const Point3* nodes, const Point3* nodeNormals,
short direction, bool selected)
{
// Set select color alpha
if (selected) {
//glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, selectColor);
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, selectColor);
}
//glBegin(GL_TRIANGLES);
glBegin(GL_POLYGON);
for (int i = 0; i < 3; i++) {
if ( nodeNormals != NULL ) {
glNormal3dv(nodeNormals[i]);
}
glVertex3dv(nodes[node_ids[i]]);
}
glEnd();
// Reset select color alpha
if (selected) {
//glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, currentColor);
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, currentColor);
}
// For debugging, draw normal vectors!
#if 0
if ( nodeNormals != NULL ) {
glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, meshColor);
drawVector((double*)nodes[node_ids[0]], (double*)nodeNormals[0], 0.05 * modelLength[0]);
drawVector((double*)nodes[node_ids[1]], (double*)nodeNormals[1], 0.05 * modelLength[0]);
drawVector((double*)nodes[node_ids[2]], (double*)nodeNormals[2], 0.05 * modelLength[0]);
glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, currentColor);
}
#endif
}
void
Renderer_OGL::drawMeshQuadriElement(int elem_type, const int* node_ids,
const Point3* nodes, const Point3* nodeNormals,
short direction, bool selected)
{
// Set select color alpha
if (selected) {
//glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, selectColor);
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, selectColor);
}
glBegin(GL_POLYGON);
for (int i = 0; i < 4; i++) {
if ( nodeNormals != NULL ) {
glNormal3dv(nodeNormals[i]);
}
glVertex3dv(nodes[node_ids[i]]);
}
glEnd();
// Reset select color alpha
if (selected) {
//glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, currentColor);
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, currentColor);
}
// For debugging, draw normal vectors!
#if 0
if ( nodeNormals != NULL ) {
glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, meshColor);
drawVector((double*)nodes[node_ids[0]], (double*)nodeNormals[0], 0.05 * modelLength[0]);
drawVector((double*)nodes[node_ids[1]], (double*)nodeNormals[1], 0.05 * modelLength[0]);
drawVector((double*)nodes[node_ids[2]], (double*)nodeNormals[2], 0.05 * modelLength[0]);
drawVector((double*)nodes[node_ids[3]], (double*)nodeNormals[3], 0.05 * modelLength[0]);
glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, currentColor);
}
#endif
}
void
Renderer_OGL::drawMeshVertexElement(int elem_type, const int* node_ids, const Point3* nodes,
bool selected)
{
glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, meshColor);
if (selected) {
glPointSize(5.0f);
} else {
glPointSize(1.5f);
}
glBegin(GL_POINTS);
glVertex3dv(nodes[node_ids[0]]);
glEnd();
glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, currentColor);
glPointSize(1.0f);
}
void
Renderer_OGL::drawNurbsCrv(objectDrawingMode dmode, objectDrawingState dstate, short direction,
ecif_NurbsCurve& data, int elem_id)
{
static GLfloat knPt[ecif_MAX_NOF_KNOTS];
static GLfloat ctPt[3 * ecif_MAX_NOF_CPOINTS];
// Tables for knot- and control-points.
// NOTE: Memory for these arries must be allocated
// in continues blocks for OpenGL. So no two-phase
// dynamic allocation doesn't work !!!###!!!
// Knot-points are already in the [0,1] domain. A new dynamically allocated
// structure would be needed only if we have to change their order. However they
// are in double-format, but we must feed them as GLfloat into OPenGL, so
// we will copy them inot a new structure.
//GLfloat* knPt = new GLfloat[data.nof_knots];
// Control points must be transformed into [0,1] domain for trimming!
//GLfloat (*ctPt)[3] = new GLfloat[data.nof_cpoints][3];
// Trimming curve must be counter-clockwise creature in OPenGL.
// We must take into account bodyelement's orientation within body!.
// Here direction == 1 means ccw-order.
// Calling functions must take care of this ***!!!***
//
int i;
// *** Copy knot-points and adjust the order (if necessary)
for (i = 0; i < data.nofKnots; i++) {
if (direction == 1)
// original values
knPt[i] = GLfloat(data.knots[i]);
else
// order is reversed (read backwards and subtract 1.
knPt[i] = GLfloat(1 - data.knots[(data.nofKnots - 1) - i]);
}
// *** Adjust control-points into (0,1) domain (and revere order if necessary).
int pos = (direction == 1) ? 0 : data.nofCpoints - 1;
int incr = (direction == 1) ? 1 : -1;
// Set control points according to the direction
for (i = 0; i < data.nofCpoints; i++, pos += incr) {
int t_cpos = 3 * i;
ctPt[t_cpos + 0] = data.cpoints[pos][0];
ctPt[t_cpos + 1] = data.cpoints[pos][1];
ctPt[t_cpos + 2] = 0.0f;
//ctPt[i][2] = data.cpoints[pos][2]);
}
GLUnurbsObj* nrb = gluNewNurbsRenderer();
gluBeginCurve(nrb);
gluNurbsCurve(nrb, data.nofKnots, knPt, 3, ctPt,
data.degree + 1, GL_MAP1_VERTEX_3);
gluEndCurve(nrb);
gluDeleteNurbsRenderer(nrb);
}
// Draw geometry point (vertex)
void
Renderer_OGL::drawPoint(objectDrawingMode dmode, objectDrawingState dstate, const Point3* point)
{
//-Use black color
static Color4f vertex_color = {0.0f, 0.0f, 0.0f, 1.0f};
//---Rendering
glPushAttrib(GL_LIGHTING_BIT);
glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, vertex_color);
if (dstate == DS_SELECTED) {
glPointSize(5.0f);
} else {
glPointSize(2.5f);
}
glBegin(GL_POINTS);
glVertex3dv(*point);
glEnd();
glPopAttrib();
glPointSize(1.0f);
}
void
Renderer_OGL::drawTwoSidedPolygons(bool turn_on)
{
if ( turn_on ) {
glDisable(GL_CULL_FACE);
} else {
glEnable(GL_CULL_FACE);
}
}
void
Renderer_OGL::drawVector(double start[3], double dir[3], double scale, bool draw_point, bool draw_arrow)
{
static double p1[3];
static double p2[3];
int i;
for (i = 0; i < 3; i++) {
p1[i] = start[i];
p2[i] = p1[i] + scale * dir[i];
}
glBegin(GL_LINES);
glVertex3dv(p1);
glVertex3dv(p2);
glEnd();
if ( draw_point ) {
glPointSize(5.0f);
glBegin(GL_POINTS);
glVertex3dv(start);
glEnd();
}
if ( draw_arrow ) {
glPointSize(2.0f);
glBegin(GL_POINTS);
glVertex3dv(start);
glEnd();
}
}
bool
Renderer_OGL::hasDisplayList(int list_id)
{
if ( GL_TRUE == glIsList(list_id) )
return true;
else
return false;
}
void
Renderer_OGL::hideRenderer()
{
destroyWindow(rendererDisplay, rendererWindow);
}
// Method initialises the GL-environment
void
Renderer_OGL::init()
{
int i;
// Reset model transformation
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
// Reset projection
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
for (i = 0; i < 3; i++) {
scaleVector[i] = 1.0f;
translateVector[i] = 0.0f;
}
rotateAxis = 0;
for (i = 0; i < 16; i++) {
rotateMatrix[i] = 0.0;
}
rotateMatrix[0] = 1.0;
rotateMatrix[5] = 1.0;
rotateMatrix[10] = 1.0;
rotateMatrix[15] = 1.0;
//glEnable(GL_NORMALIZE);
// Add lights to the scene.
// Light-0, Undirected, strong ambient light
GLfloat position0[4] = { 0.0, 0.0, 1.0, 0.0 };
GLfloat ambient0[4] = { 1.0, 1.0, 1.0, 1.0 };
GLfloat diffuse0[4] = { 0.0, 0.0, 0.0, 1.0 };
GLfloat specular0[4] = { 0.0, 0.0, 0.0, 1.0 };
//glLightfv(GL_LIGHT0, GL_POSITION, position0);
glLightfv(GL_LIGHT0, GL_AMBIENT, ambient0);
glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse0);
glLightfv(GL_LIGHT0, GL_SPECULAR, specular0);
// Light-1, Undirected, weak ambient light
GLfloat position1[4] = { 0.0, 0.0, 1.0, 0.0 };
GLfloat ambient1[4] = { 0.3, 0.3, 0.3, 1.0 };
GLfloat diffuse1[4] = { 0.0, 0.0, 0.0, 1.0 };
GLfloat specular1[4] = { 0.0, 0.0, 0.0, 1.0 };
//glLightfv(GL_LIGHT1, GL_POSITION, position1);
glLightfv(GL_LIGHT1, GL_AMBIENT, ambient1);
glLightfv(GL_LIGHT1, GL_DIFFUSE, diffuse1);
glLightfv(GL_LIGHT1, GL_SPECULAR, specular1);
// Light-2, Diffuse light
GLfloat position2[4] = { 0.0, 0.0, 1.0, 0.0 };
GLfloat ambient2[4] = { 0.35, 0.35, 0.35, 1.0 };
GLfloat diffuse2[4] = { 0.65, 0.65, 0.65, 1.0 };
GLfloat specular2[4] = { 0.0, 0.0, 0.0, 1.0 };
//glLightfv(GL_LIGHT2, GL_POSITION, position2);
glLightfv(GL_LIGHT2, GL_AMBIENT, ambient2);
glLightfv(GL_LIGHT2, GL_DIFFUSE, diffuse2);
glLightfv(GL_LIGHT2, GL_SPECULAR, specular2);
// Light-3, Directed, left diffuse light
GLfloat position3[4] = { -0.5, 0.0, 1.0, 0.0 };
GLfloat ambient3[4] = { 0.2, 0.2, 0.2, 1.0 };
GLfloat diffuse3[4] = { 0.8, 0.8, 0.8, 1.0 };
GLfloat specular3[4] = { 0.3, 0.3, 0.3, 1.0 };
glLightfv(GL_LIGHT3, GL_POSITION, position3);
glLightfv(GL_LIGHT3, GL_AMBIENT, ambient3);
glLightfv(GL_LIGHT3, GL_DIFFUSE, diffuse3);
glLightfv(GL_LIGHT3, GL_SPECULAR, specular3);
// Right-3, Directed, right diffuse light
GLfloat position4[4] = { 0.5, 0.0, 1.0, 0.0 };
GLfloat ambient4[4] = { 0.2, 0.2, 0.2, 1.0 };
GLfloat diffuse4[4] = { 0.8, 0.8, 0.8, 1.0 };
GLfloat specular4[4] = { 1.0, 1.0, 1.0, 1.0 };
glLightfv(GL_LIGHT4, GL_POSITION, position4);
glLightfv(GL_LIGHT4, GL_AMBIENT, ambient4);
glLightfv(GL_LIGHT4, GL_DIFFUSE, diffuse4);
glLightfv(GL_LIGHT4, GL_SPECULAR, specular4);
glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE);
//glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, GL_TRUE);
// Select active light(s)
//
setLightDirected();
// Turn on the lights
glEnable(GL_LIGHTING);
// Shading mode
//glShadeModel(GL_FLAT);
glShadeModel(GL_SMOOTH);
// Set default normal vector for 2D geometry
glNormal3d(0.0, 0.0, 1.0);
setParameters();
reshape();
}
void
Renderer_OGL::reset()
{
// If not visible
if ( winX == 0 || winY == 0 ) {
return;
}
init();
refresh();
}
void
Renderer_OGL::resetData()
{
deleteData();
createData();
init();
}
void
Renderer_OGL::makeRasterFont()
{
GLuint i;
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
fontOffset = glGenLists (128);
for (i = 32; i < 127; i++) {
glNewList(i+fontOffset, GL_COMPILE);
glBitmap(8, 13, 0.0f, 2.0f, 10.0f, 0.0f, rasterFont[i-32]);
glEndList();
}
}
void
Renderer_OGL::name_delete(int name_id)
{
glPopName();
}
void
Renderer_OGL::name_replace(int name_id)
{
glLoadName((GLuint)name_id);
}
void
Renderer_OGL::name_save(int name_id)
{
GLint stack[1];
stack[0] = 0;
glPushName((GLuint)name_id);
glGetIntegerv(GL_NAME_STACK_DEPTH, stack);
}
// Scale double-point to [0,1]
void
Renderer_OGL::normalize1_point(Point3& p)
{
for (int i = 0; i < 3; i++) {
if ( modelLength[i] > 0 )
p[i] = (p[i] - modelStart[i]) / modelLength[i];
}
}
// Scale float-point to [0,1]
void
Renderer_OGL::normalize1_point(Point3f& p)
{
for (int i = 0; i < 3; i++) {
if ( modelLength[i] > 0 )
p[i] = (p[i] - modelStart[i]) / modelLength[i];
}
}
// Scale double-point to [-1,1]
void
Renderer_OGL::normalize2_point(Point3& p)
{
for (int i = 0; i < 3; i++) {
p[i] = 2 * (p[i] - modelCenter[i]) / modelLength[i];
}
}
// Scale float-point to [-1,1]
void
Renderer_OGL::normalize2_point(Point3f& p)
{
for (int i = 0; i < 3; i++) {
p[i] = 2 * (p[i] - modelCenter[i]) / modelLength[i];
}
}
void
Renderer_OGL::printString(char* s)
{
#if defined(WIN32)
glPushAttrib(GL_LIST_BIT);
glListBase(fontOffset);
glCallLists(strlen(s), GL_UNSIGNED_BYTE, (GLubyte*) s);
glPopAttrib();
#else
// NOTE: Push/Pop attributes crashes (after scaling)
// in Linux, so we skip them in Unix!!!
// Consequences...?
glListBase(fontOffset);
glCallLists(strlen(s), GL_UNSIGNED_BYTE, (GLubyte*) s);
#endif
}
// Process mouse selections when editing mesh boundaries, selecting boundaries etc.
//
void
Renderer_OGL::processSelection(mouseAction maction,int mk_state, int x_pos, int y_pos)
{
if ( winX == 0 || winY == 0) {
return;
}
// Update mouse-keyboard state attribute
mkState = mk_state;
bool in_mesh_draw_mode = model->getFlagValue(DRAW_SOURCE_MESH);
bool in_draw_mode = inBoxDrawMode || inLineDrawMode || inVectorDrawMode;
bool do_picking = false;
// Select by mouse action
switch (maction) {
case MOUSE_DBL_CLCK:
// Turn on picking mode, turn off extended object selection
do_picking = true;
model->setFlagValue(SELECT_OBJECTS, SELECT_OBJECTS_EXTEND, false);
break;
case MOUSE_CTRL_DBL_CLCK:
// Turn on picking mode, turn on extend selection
do_picking = true;
model->setFlagValue(SELECT_OBJECTS, SELECT_OBJECTS_EXTEND, true);
break;
case MOUSE_CTRL_CLCK:
// Effective only in editing mode
if ( !inMeshEditingMode )
return;
break;
case MOUSE_SHFT_MOVE:
// Effective only in editing mode
if ( !(inMeshEditingMode || in_draw_mode) )
return;
break;
default:
return;
break;
}
//----Ray-cast picking for the mesh boundary/bulk elements
// Editing bodies/boundaries
if ( inMeshEditingMode || in_mesh_draw_mode || in_draw_mode
) {
// Find model coordinates for the window coordinates
Point3 p1, p2;
int res1 = gluUnProject( x_pos, y_pos, 0.0, modelMatrix, projectionMatrix, viewport,
&p1[0], &p1[1], &p1[2]);
int res2 = gluUnProject( x_pos, y_pos, 1.0, modelMatrix, projectionMatrix, viewport,
&p2[0], &p2[1], &p2[2]);
// Line direction for the casted ray in the form:
// start-point + t*dir-vec
Point3 ldir;
diff3(p2, p1, ldir);
model->setCurrentPickInfo(p1,ldir);
if ( inMeshEditingMode || in_mesh_draw_mode ) {
int bndr_id;
int bd1_id, lr1_id;
int bd2_id, lr2_id;
bool use_cur_bndr = inMeshEditingMode;
int fem_id = model->findSelectedMeshBoundaryElement(this, p1, ldir, use_cur_bndr,
bndr_id,
bd1_id, lr1_id,
bd2_id, lr2_id);
if ( fem_id == NO_INDEX ) {
return;
}
if ( inMeshEditingMode ) {
model->meshBoundaryElementSelected(this, fem_id);
} else {
model->boundarySelected(this, bndr_id, bd1_id, lr1_id, bd2_id, lr2_id, true);
}
display(this);
return;
}
}
if (!do_picking) return;
//---Save current projection
glMatrixMode(GL_PROJECTION);
glPushMatrix();
//---Build picking "cube"
//-Window center
// Convert mouse's location in the window to the location in the model world
double x_ratio = 1.0;
double y_ratio = 1.0;
if ( viewport[2] > 0 ) {
x_ratio = (double)x_pos / (viewport[2] - viewport[0]);
}
if ( viewport[3] > 0 ) {
y_ratio = (double)(y_pos) / (viewport[3] - viewport[1]);
}
double x_center = projection[0] + x_ratio * (projection[1] - projection[0]);
double y_center = projection[2] + y_ratio * (projection[3] - projection[2]);
//-Picking window size
double pixels;
// NOTE:If this is small the gluEndSurface(background)
// in 2D nurbs trimming slows down dramatically!!!
if ( model->getDimension() == ECIF_2D &&
model->getFlagValue(DRAW_SOURCE_CAD) &&
model->getFlagValue(DRAW_TARGET_BODIES)
) {
pixels = 10.0;
} else {
pixels = 1.0;
}
double x_del = pixels * (projection[1] - projection[0]) / winX;
double y_del = pixels * (projection[3] - projection[2]) / winY;
x_del *= scaleVector[0];
y_del *= scaleVector[1];
//----OpenGL "selection picking" for boundaries/bodies
// "Double click selection mode"
//---Set selection buffer
glSelectBuffer(selectionBufferSize, selectionBuffer);
//---Goto selection mode
// Comment this call if you want to see the screen after
// selection (for debugging!)
glRenderMode(GL_SELECT);
renderMode = RENDER_SELECTION;
//---Init selection name buffer
glInitNames();
glLoadIdentity();
//-Clipping viewing volume
glOrtho(x_center - x_del, x_center + x_del,
y_center - y_del, y_center + y_del,
projection[4], projection[5]);
//---Get selections
//inMeshPickingMode = 1;
selectionHits = 0;
display(this);
selectionHits = glRenderMode(GL_RENDER);
inMeshPickingMode = 0;
renderMode = RENDER_NORMAL;
//---Restore original projection
glMatrixMode(GL_PROJECTION);
glPopMatrix();
glMatrixMode(GL_MODELVIEW); // Default matrix mode from now on
display(this);
}
void
Renderer_OGL::meshBoundaryElementSelectionHits()
{
if ( selectionHits == 0)
return;
int picked_fem_id = NO_INDEX;
GLuint tot_min_z = 0xffffffff;
GLuint tot_max_z = 0;
int record_start = 0;
for (int i = 0; i < selectionHits; i++) {
int nof_names = selectionBuffer[record_start];
GLuint min_z = selectionBuffer[record_start + 1];
GLuint max_z = selectionBuffer[record_start + 2];
int body_id = selectionBuffer[record_start + 3];
int bndr_id = selectionBuffer[record_start + 4];
int fem_id = NO_INDEX;
if (nof_names > 2) {
fem_id = selectionBuffer[record_start + 5];
}
record_start += 3 + nof_names;
if ( fem_id != NO_INDEX && min_z < tot_min_z ) {
tot_min_z = min_z;
picked_fem_id = fem_id;
}
}
if ( picked_fem_id != NO_INDEX ) {
model->meshBoundaryElementSelectionHit(this, picked_fem_id);
}
selectionHits = 0;
}
void
Renderer_OGL::meshBulkElementSelectionHits()
{
if ( selectionHits == 0)
return;
int picked_fem_id = NO_INDEX;
GLuint tot_min_z = 0xffffffff;
GLuint tot_max_z = 0;
int record_start = 0;
for (int i = 0; i < selectionHits; i++) {
int nof_names = selectionBuffer[record_start];
GLuint min_z = selectionBuffer[record_start + 1];
GLuint max_z = selectionBuffer[record_start + 2];
int body_id = selectionBuffer[record_start + 3];
int fem_id = NO_INDEX;
if (nof_names > 1)
fem_id = selectionBuffer[record_start + 4];
record_start += 3 + nof_names;
if ( fem_id != NO_INDEX &&
max_z > tot_max_z
) {
tot_max_z = max_z;
picked_fem_id = fem_id;
}
}
if ( picked_fem_id != NO_INDEX )
model->meshBulkElementSelectionHit(this, picked_fem_id);
selectionHits = 0;
}
void
Renderer_OGL::refresh()
{
if (!visible) {
displayRenderer();
} else {
display(this);
}
}
void
Renderer_OGL::removeDisplayLists()
{
IdList::iterator itr = displayListIds->begin();
while ( itr != displayListIds->end() ) {
int id = (*itr++);
glDeleteLists(id, 1);
}
displayListIds->clear();
}
// Setup renderer
void
Renderer_OGL::reshape()
{
static GLdouble ratio[3];
static GLdouble marginal[3];
static GLdouble aspect;
// Window (client) size
findRendererWindowSize(winX, winY);
// If window is minimized
if (winX == 0 || winY == 0 ) {
return;
}
// Set viewport to full window (client) size
glViewport(0, 0, winX, winY);
// Correct for window aspect ratio
GLdouble xw_ratio = (GLfloat)winY / winX;
GLdouble yw_ratio = 1;
// Correct model geometry aspect ratio from the -1, 1 normalized world
// NOTE: This is in "opposite" direction compared to window ratio correction!
GLdouble xg_ratio = 1;
if ( modelLength[1] > 0 ) {
xg_ratio = modelLength[0] / modelLength[1];
}
GLdouble yg_ratio = 1;
aspect = yw_ratio / xw_ratio;
// Calculate the final aspect-ratios for an ortho projection
// Here the larger aspect is set to value 1.0 and the smaller
// is normed > 1.0, (this way smaller is made smaller in ortho projection!)
double x_ratio = xw_ratio * xg_ratio;
double y_ratio = yw_ratio * yg_ratio;
double z_ratio = 1.0f;
if (x_ratio > y_ratio && y_ratio > 0 ) {
y_ratio = x_ratio / y_ratio;
x_ratio = 1.0f;
} else if ( x_ratio > 0 ) {
x_ratio = y_ratio / x_ratio;
y_ratio = 1.0f;
}
// Set data into arries (to make filling projection[] easier)
ratio[0] = x_ratio;
ratio[1] = y_ratio;
ratio[2] = z_ratio;
double max_length = modelLength[0];
if (modelLength[1] > max_length) {
max_length = modelLength[1];
}
if (modelLength[2] > max_length) {
max_length = modelLength[2];
}
marginal[0] = 0.10 * modelLength[0]; // 10% x marginal
marginal[1] = 0.10 * modelLength[1]; // 10% y marginal
marginal[2] = 10.0 * scaleVector[2] * max_length; // That's a marginal!
//marginal[2] = 10.0 * max_length; // That's also a marginal!
// Clipping values for orthogonal projection
for (int i = 0; i < 3; i++) {
// correct with the ratio
double half = 0.5 * modelLength[i] * ratio[i];
double marg = marginal[i] * ratio[i];
// add marginal
projection[0 + 2 * i] = modelCenter[i] - half - marg;
projection[1 + 2 * i] = modelCenter[i] + half + marg;
}
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
// Ortho projection
if ( useOrthoProjection ) {
glOrtho(projection[0], projection[1],
projection[2], projection[3],
projection[4], projection[5]
);
//glOrtho(-aspect * max_length, aspect * max_length, -1.0 * max_length, 1.0 * max_length,
// //projection[4], projection[5]
// scaleVector[2] * -max_length, scaleVector[2] * 3 * max_length
// );
//translateVector[2] = - scaleVector[2] * max_length;
// Perspective projection
// NOTE: This is not correct, do NOT use this!
} else {
//glFrustum(projection[0], projection[1],
// projection[2], projection[3],
//2.0, 2.0 + 5 * max_length
// 0.0, 1.0 + 3 * max_length
// );
gluPerspective(15.0, aspect, 1.0, 1.0 + 3 * scaleVector[2] * max_length);
translateVector[2] = -1.0 - scaleVector[2] * max_length;
}
glGetDoublev(GL_PROJECTION_MATRIX, projectionMatrix);
glGetIntegerv(GL_VIEWPORT, viewport);
}
void
Renderer_OGL::rotate(double angle)
{
// Note: Reaction to rotation angle is slower when model is
// scaled to a large one!
//
double factor = 2.0 / (scaleVector[0] + scaleVector[1]);
if ( factor < 1.0 ) {
angle *= sqrt(factor);
} else {
angle *= factor;
}
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();
GLdouble* rm = rotateMatrix;
// 2D model
if ( is2D ) {
glRotated(angle, 0, 0, 1);
// 3D model
} else if (rotateAxisX) {
glRotated(angle, rm[0], rm[1], rm[2]);
} else if (rotateAxisY) {
glRotated(angle, rm[4], rm[5], rm[6]);
} else if (rotateAxisZ) {
glRotated(angle, rm[8], rm[9], rm[10]);
} else if ( rotateAxis == 0 ) {
glRotated(angle, 1.0f, 0.0f, 0.0f);
} else if ( rotateAxis == 1 ) {
glRotated(angle, 0.0f, 1.0f, 0.0f);
} else if ( rotateAxis == 2 ) {
glRotated(angle, 0.0f, 0.0f, 1.0f);
}
glMultMatrixd(rotateMatrix);
glGetDoublev(GL_MODELVIEW_MATRIX, rotateMatrix);
glPopMatrix();
glMultMatrixd(rotateMatrix);
}
// Rotates a fixed amount to given direction around given axis
void
Renderer_OGL::rotate(int axis, short direction)
{
if ( winX == 0 || winY == 0 )
return;
int base_rt[] = {winX, winY, winZ};
if (axis == 2)
direction *= -1;
// Rotate 1.5 pixel equivalent
double rt = direction * 1.5;
rotate(axis, rt);
}
// Rotate a given "screen amount" around given axis
// This is used mainly when using mouse movements
// for rotation
void
Renderer_OGL::rotate(int axis, int amount)
{
// In 2D we rotate only around z-axis, and
// only if z-rotation is turned on!
if (is2D && axis != 2) {
return;
}
//int base[3] = {winX, winY, winZ};
int base[3] = {winX, winY, (winX + winY) /2};
GLfloat degrees = 0.0;
if ( base[axis] != 0 )
degrees = 90 * (GLfloat)amount / base[axis];
if (axis == 2)
degrees *= -1;
rotate(axis, degrees);
}
void
Renderer_OGL::rotate(int axis, double degrees)
{
if ( is2D ) {
rotate(degrees);
}
else {
rotate(degrees);
rotateAxis = axis;
}
display(this);
}
void
Renderer_OGL::scale()
{
glScaled(scaleVector[0], scaleVector[1], scaleVector[2]);
}
void
Renderer_OGL::scale(short direction)
{
if ( winX == 0 || winY == 0 )
return;
// Scale 5% of the avg. window size
int sc = direction * 0.05 * 0.5 * (winX + winY);
scale(sc);
}
void
Renderer_OGL::setDrawMode()
{
glDisable(GL_LINE_SMOOTH);
glDisable(GL_DEPTH_TEST);
glDisable(GL_POLYGON_OFFSET_FILL);
// In 3D
if (!is2D) {
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL);
//glDepthFunc(GL_LESS);
//glDisable(GL_DEPTH_TEST);
//glPolygonOffset(1.0f, 1.1f);
glPolygonOffset(2.1f, 2.1f);
glEnable(GL_POLYGON_OFFSET_FILL);
}
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
//glPolygonMode(GL_FRONT, GL_FILL);
//glPolygonMode(GL_BACK, GL_LINE);
glCullFace(GL_BACK);
glEnable(GL_CULL_FACE);
//glDisable(GL_CULL_FACE);
}
void
Renderer_OGL::setLightDirected()
{
glDisable(GL_LIGHT0);
glDisable(GL_LIGHT1);
glDisable(GL_LIGHT2);
glDisable(GL_LIGHT3);
glDisable(GL_LIGHT4);
if (is2D) {
glEnable(GL_LIGHT0); // Undirected strong ambient
} else {
glEnable(GL_LIGHT1); // Undirected weak ambient
//glEnable(GL_LIGHT2); // Undirected diffuse
glEnable(GL_LIGHT3); // Directed ,left diffuse
//glEnable(GL_LIGHT4); // Directed ,right diffuse
}
}
void
Renderer_OGL::setLightUndirected()
{
glDisable(GL_LIGHT0);
glDisable(GL_LIGHT1);
glDisable(GL_LIGHT2);
glDisable(GL_LIGHT3);
glDisable(GL_LIGHT4);
if (is2D) {
glEnable(GL_LIGHT0); // Undirected strong ambient
} else {
glEnable(GL_LIGHT0); // Undirected strong ambient
glEnable(GL_LIGHT1); // Undirected weak ambient
glEnable(GL_LIGHT2); // Undirected diffuse
}
}
// Set general drawing paramteters.
void
Renderer_OGL::setParameters()
{
int i;
// Model's scale for normalization parameters (class attributes)
// Transformation (x + shift) / norm transforms data into [-1, 1]
RangeVector rv;
model->getBoundingBox(rv);
// If 2D model, set z-dimensions to [-1,1] for proper
// clipping in orto projection
if ( is2D ) {
rv[4] = -1.0;
rv[5] = 1.0;
}
for (i = 0; i < 3; i++) {
int i1 = 2 * i;
int i2 = i1 + 1;
modelStart[i] = rv[i1];
modelEnd[i] = rv[i2];
modelCenter[i] = (rv[i2] + rv[i1]) / 2;
modelLength[i] = rv[i2] - rv[i1];
}
if ( isZero(modelLength[0]) ) {
modelLength[0] = max(modelLength[1], modelLength[2]);
}
if ( isZero(modelLength[1]) ) {
modelLength[1] = max(modelLength[0], modelLength[2]);
}
if ( !is2D && isZero(modelLength[2]) ) {
modelLength[2] = max(modelLength[0], modelLength[1]);
}
}
void
Renderer_OGL::setRendererInfo()
{
WindowInfo winfo;
if (status == HAS_NO_WINDOW){
createGLWindow(NULL, "Test", 0, 0, 1, 1, winfo);
}
GLfloat f1[1];
GLfloat f2[2];
glGetFloatv(GL_LINE_WIDTH_GRANULARITY, f1);
rendererInfo.LINE_WIDTH_GRANULARITY = f1[0];
glGetFloatv(GL_LINE_WIDTH_RANGE, f2);
rendererInfo.LINE_WIDTH_RANGE[0] = f2[0];
rendererInfo.LINE_WIDTH_RANGE[1] = f2[1];
theControlCenter->getUI()->updateRendererInfo(rendererInfo);
if (status == HAS_NO_WINDOW){
destroyWindow(winfo.display, winfo.window);
}
}
void
Renderer_OGL::scale(int amount)
{
GLfloat sc_amount = 0.0;
if ( winY > 0 ) {
sc_amount = 1.0f + (GLfloat)amount / winY;
}
//glMatrixMode(GL_MODELVIEW);
//glScaled(sc_amount, sc_amount, sc_amount);
scaleVector[0] *= sc_amount;
scaleVector[1] *= sc_amount;
scaleVector[2] *= sc_amount;
reshape();
display(this);
}
// Generic test-procedure for testing renderer related stuff
void
Renderer_OGL::test()
{
}
void
Renderer_OGL::transform_scene()
{
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
translate();
// Re-center from origo for translation
glTranslated(modelCenter[0], modelCenter[1], modelCenter[2]);
scale();
rotate();
// Center to origo before rotate and scale
glTranslated(-modelCenter[0], -modelCenter[1], -modelCenter[2]);
glGetDoublev(GL_MODELVIEW_MATRIX, modelMatrix);
}
void
Renderer_OGL::translate()
{
glTranslated(translateVector[0]*scaleVector[0],
translateVector[1]*scaleVector[1],
translateVector[2]*scaleVector[2]
);
}
void
Renderer_OGL::translate(int coordinate, short direction)
{
if ( winX == 0 || winY == 0 )
return;
int base_tv[] = {winX, winY, winZ};
int tv[] = {0, 0, 0};
// Translate 2.5%
tv[coordinate] = (int) direction * 0.025 * base_tv[coordinate];
translate(tv[0], tv[1], tv[2]);
}
void
Renderer_OGL::translate(int x_delta, int y_delta, int z_delta)
{
if ( winX == 0 || winY == 0 )
return;
GLdouble avg = 0.5 * (modelLength[0] + modelLength[1]);
GLdouble x_amount = avg * (GLdouble)x_delta / winX;
GLdouble y_amount = avg * (GLdouble)y_delta / winY;
GLdouble z_amount = modelLength[2] * (GLdouble)z_delta / winZ;
// The Macig Speedup Factor of Translations
// (from The Handbook of Contants)
double factor = 1.5;
translateVector[0] += factor * x_amount / scaleVector[0];
translateVector[1] += factor * y_amount / scaleVector[1];
translateVector[2] += z_amount / scaleVector[2];
display(this);
}
void
Renderer_OGL::useDisplayList(int list_id)
{
glCallList(list_id);
}
// *** System dependent section ***
#ifdef WIN32
#include "ecif_renderer_OGL_WIN32.hpp"
#else
#include "ecif_renderer_OGL_UNIX.hpp"
#endif
// *** End system dependent ***
//************************
//*** FONTS DEFINITION ***
//************************
// Set value for the static attribute:
GLubyte Renderer_OGL::rasterFont[][13] = {
//GLubyte rasterFont[][13] = {
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
{0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18},
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x36, 0x36, 0x36},
{0x00, 0x00, 0x00, 0x66, 0x66, 0xff, 0x66, 0x66, 0xff, 0x66, 0x66, 0x00, 0x00},
{0x00, 0x00, 0x18, 0x7e, 0xff, 0x1b, 0x1f, 0x7e, 0xf8, 0xd8, 0xff, 0x7e, 0x18},
{0x00, 0x00, 0x0e, 0x1b, 0xdb, 0x6e, 0x30, 0x18, 0x0c, 0x76, 0xdb, 0xd8, 0x70},
{0x00, 0x00, 0x7f, 0xc6, 0xcf, 0xd8, 0x70, 0x70, 0xd8, 0xcc, 0xcc, 0x6c, 0x38},
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x1c, 0x0c, 0x0e},
{0x00, 0x00, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x18, 0x0c},
{0x00, 0x00, 0x30, 0x18, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x18, 0x30},
{0x00, 0x00, 0x00, 0x00, 0x99, 0x5a, 0x3c, 0xff, 0x3c, 0x5a, 0x99, 0x00, 0x00},
{0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0xff, 0xff, 0x18, 0x18, 0x18, 0x00, 0x00},
{0x00, 0x00, 0x30, 0x18, 0x1c, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00},
{0x00, 0x00, 0x00, 0x38, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
{0x00, 0x60, 0x60, 0x30, 0x30, 0x18, 0x18, 0x0c, 0x0c, 0x06, 0x06, 0x03, 0x03},
{0x00, 0x00, 0x3c, 0x66, 0xc3, 0xe3, 0xf3, 0xdb, 0xcf, 0xc7, 0xc3, 0x66, 0x3c},
{0x00, 0x00, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x78, 0x38, 0x18},
{0x00, 0x00, 0xff, 0xc0, 0xc0, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x03, 0xe7, 0x7e},
{0x00, 0x00, 0x7e, 0xe7, 0x03, 0x03, 0x07, 0x7e, 0x07, 0x03, 0x03, 0xe7, 0x7e},
{0x00, 0x00, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0xff, 0xcc, 0x6c, 0x3c, 0x1c, 0x0c},
{0x00, 0x00, 0x7e, 0xe7, 0x03, 0x03, 0x07, 0xfe, 0xc0, 0xc0, 0xc0, 0xc0, 0xff},
{0x00, 0x00, 0x7e, 0xe7, 0xc3, 0xc3, 0xc7, 0xfe, 0xc0, 0xc0, 0xc0, 0xe7, 0x7e},
{0x00, 0x00, 0x30, 0x30, 0x30, 0x30, 0x18, 0x0c, 0x06, 0x03, 0x03, 0x03, 0xff},
{0x00, 0x00, 0x7e, 0xe7, 0xc3, 0xc3, 0xe7, 0x7e, 0xe7, 0xc3, 0xc3, 0xe7, 0x7e},
{0x00, 0x00, 0x7e, 0xe7, 0x03, 0x03, 0x03, 0x7f, 0xe7, 0xc3, 0xc3, 0xe7, 0x7e},
{0x00, 0x00, 0x00, 0x38, 0x38, 0x00, 0x00, 0x38, 0x38, 0x00, 0x00, 0x00, 0x00},
{0x00, 0x00, 0x30, 0x18, 0x1c, 0x1c, 0x00, 0x00, 0x1c, 0x1c, 0x00, 0x00, 0x00},
{0x00, 0x00, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0x60, 0x30, 0x18, 0x0c, 0x06},
{0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00},
{0x00, 0x00, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x03, 0x06, 0x0c, 0x18, 0x30, 0x60},
{0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x18, 0x0c, 0x06, 0x03, 0xc3, 0xc3, 0x7e},
{0x00, 0x00, 0x3f, 0x60, 0xcf, 0xdb, 0xd3, 0xdd, 0xc3, 0x7e, 0x00, 0x00, 0x00},
{0x00, 0x00, 0xc3, 0xc3, 0xc3, 0xc3, 0xff, 0xc3, 0xc3, 0xc3, 0x66, 0x3c, 0x18},
{0x00, 0x00, 0xfe, 0xc7, 0xc3, 0xc3, 0xc7, 0xfe, 0xc7, 0xc3, 0xc3, 0xc7, 0xfe},
{0x00, 0x00, 0x7e, 0xe7, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xe7, 0x7e},
{0x00, 0x00, 0xfc, 0xce, 0xc7, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc7, 0xce, 0xfc},
{0x00, 0x00, 0xff, 0xc0, 0xc0, 0xc0, 0xc0, 0xfc, 0xc0, 0xc0, 0xc0, 0xc0, 0xff},
{0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xfc, 0xc0, 0xc0, 0xc0, 0xff},
{0x00, 0x00, 0x7e, 0xe7, 0xc3, 0xc3, 0xcf, 0xc0, 0xc0, 0xc0, 0xc0, 0xe7, 0x7e},
{0x00, 0x00, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xff, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3},
{0x00, 0x00, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7e},
{0x00, 0x00, 0x7c, 0xee, 0xc6, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06},
{0x00, 0x00, 0xc3, 0xc6, 0xcc, 0xd8, 0xf0, 0xe0, 0xf0, 0xd8, 0xcc, 0xc6, 0xc3},
{0x00, 0x00, 0xff, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0},
{0x00, 0x00, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xdb, 0xff, 0xff, 0xe7, 0xc3},
{0x00, 0x00, 0xc7, 0xc7, 0xcf, 0xcf, 0xdf, 0xdb, 0xfb, 0xf3, 0xf3, 0xe3, 0xe3},
{0x00, 0x00, 0x7e, 0xe7, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xe7, 0x7e},
{0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xfe, 0xc7, 0xc3, 0xc3, 0xc7, 0xfe},
{0x00, 0x00, 0x3f, 0x6e, 0xdf, 0xdb, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0x66, 0x3c},
{0x00, 0x00, 0xc3, 0xc6, 0xcc, 0xd8, 0xf0, 0xfe, 0xc7, 0xc3, 0xc3, 0xc7, 0xfe},
{0x00, 0x00, 0x7e, 0xe7, 0x03, 0x03, 0x07, 0x7e, 0xe0, 0xc0, 0xc0, 0xe7, 0x7e},
{0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xff},
{0x00, 0x00, 0x7e, 0xe7, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3},
{0x00, 0x00, 0x18, 0x3c, 0x3c, 0x66, 0x66, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3},
{0x00, 0x00, 0xc3, 0xe7, 0xff, 0xff, 0xdb, 0xdb, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3},
{0x00, 0x00, 0xc3, 0x66, 0x66, 0x3c, 0x3c, 0x18, 0x3c, 0x3c, 0x66, 0x66, 0xc3},
{0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x3c, 0x66, 0x66, 0xc3},
{0x00, 0x00, 0xff, 0xc0, 0xc0, 0x60, 0x30, 0x7e, 0x0c, 0x06, 0x03, 0x03, 0xff},
{0x00, 0x00, 0x3c, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x3c},
{0x00, 0x03, 0x03, 0x06, 0x06, 0x0c, 0x0c, 0x18, 0x18, 0x30, 0x30, 0x60, 0x60},
{0x00, 0x00, 0x3c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x3c},
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0x66, 0x3c, 0x18},
{0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x38, 0x30, 0x70},
{0x00, 0x00, 0x7f, 0xc3, 0xc3, 0x7f, 0x03, 0xc3, 0x7e, 0x00, 0x00, 0x00, 0x00},
{0x00, 0x00, 0xfe, 0xc3, 0xc3, 0xc3, 0xc3, 0xfe, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0},
{0x00, 0x00, 0x7e, 0xc3, 0xc0, 0xc0, 0xc0, 0xc3, 0x7e, 0x00, 0x00, 0x00, 0x00},
{0x00, 0x00, 0x7f, 0xc3, 0xc3, 0xc3, 0xc3, 0x7f, 0x03, 0x03, 0x03, 0x03, 0x03},
{0x00, 0x00, 0x7f, 0xc0, 0xc0, 0xfe, 0xc3, 0xc3, 0x7e, 0x00, 0x00, 0x00, 0x00},
{0x00, 0x00, 0x30, 0x30, 0x30, 0x30, 0x30, 0xfc, 0x30, 0x30, 0x30, 0x33, 0x1e},
{0x7e, 0xc3, 0x03, 0x03, 0x7f, 0xc3, 0xc3, 0xc3, 0x7e, 0x00, 0x00, 0x00, 0x00},
{0x00, 0x00, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xfe, 0xc0, 0xc0, 0xc0, 0xc0},
{0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x18, 0x00},
{0x38, 0x6c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x00, 0x00, 0x0c, 0x00},
{0x00, 0x00, 0xc6, 0xcc, 0xf8, 0xf0, 0xd8, 0xcc, 0xc6, 0xc0, 0xc0, 0xc0, 0xc0},
{0x00, 0x00, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x78},
{0x00, 0x00, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xfe, 0x00, 0x00, 0x00, 0x00},
{0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xfc, 0x00, 0x00, 0x00, 0x00},
{0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00},
{0xc0, 0xc0, 0xc0, 0xfe, 0xc3, 0xc3, 0xc3, 0xc3, 0xfe, 0x00, 0x00, 0x00, 0x00},
{0x03, 0x03, 0x03, 0x7f, 0xc3, 0xc3, 0xc3, 0xc3, 0x7f, 0x00, 0x00, 0x00, 0x00},
{0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xe0, 0xfe, 0x00, 0x00, 0x00, 0x00},
{0x00, 0x00, 0xfe, 0x03, 0x03, 0x7e, 0xc0, 0xc0, 0x7f, 0x00, 0x00, 0x00, 0x00},
{0x00, 0x00, 0x1c, 0x36, 0x30, 0x30, 0x30, 0x30, 0xfc, 0x30, 0x30, 0x30, 0x00},
{0x00, 0x00, 0x7e, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00},
{0x00, 0x00, 0x18, 0x3c, 0x3c, 0x66, 0x66, 0xc3, 0xc3, 0x00, 0x00, 0x00, 0x00},
{0x00, 0x00, 0xc3, 0xe7, 0xff, 0xdb, 0xc3, 0xc3, 0xc3, 0x00, 0x00, 0x00, 0x00},
{0x00, 0x00, 0xc3, 0x66, 0x3c, 0x18, 0x3c, 0x66, 0xc3, 0x00, 0x00, 0x00, 0x00},
{0xc0, 0x60, 0x60, 0x30, 0x18, 0x3c, 0x66, 0x66, 0xc3, 0x00, 0x00, 0x00, 0x00},
{0x00, 0x00, 0xff, 0x60, 0x30, 0x18, 0x0c, 0x06, 0xff, 0x00, 0x00, 0x00, 0x00},
{0x00, 0x00, 0x0f, 0x18, 0x18, 0x18, 0x38, 0xf0, 0x38, 0x18, 0x18, 0x18, 0x0f},
{0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18},
{0x00, 0x00, 0xf0, 0x18, 0x18, 0x18, 0x1c, 0x0f, 0x1c, 0x18, 0x18, 0x18, 0xf0},
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x8f, 0xf1, 0x60, 0x00, 0x00, 0x00}
};
syntax highlighted by Code2HTML, v. 0.9.1