/*********************************************************************** * * 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} };