Developer Documentation
CursorPainter.cc
1 /*===========================================================================*\
2 * *
3 * OpenFlipper *
4  * Copyright (c) 2001-2015, RWTH-Aachen University *
5  * Department of Computer Graphics and Multimedia *
6  * All rights reserved. *
7  * www.openflipper.org *
8  * *
9  *---------------------------------------------------------------------------*
10  * This file is part of OpenFlipper. *
11  *---------------------------------------------------------------------------*
12  * *
13  * Redistribution and use in source and binary forms, with or without *
14  * modification, are permitted provided that the following conditions *
15  * are met: *
16  * *
17  * 1. Redistributions of source code must retain the above copyright notice, *
18  * this list of conditions and the following disclaimer. *
19  * *
20  * 2. Redistributions in binary form must reproduce the above copyright *
21  * notice, this list of conditions and the following disclaimer in the *
22  * documentation and/or other materials provided with the distribution. *
23  * *
24  * 3. Neither the name of the copyright holder nor the names of its *
25  * contributors may be used to endorse or promote products derived from *
26  * this software without specific prior written permission. *
27  * *
28  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS *
29  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED *
30  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
31  * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER *
32  * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, *
33  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, *
34  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR *
35  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF *
36  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING *
37  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS *
38  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *
39 * *
40 \*===========================================================================*/
41 
42 
43 
44 
45 
46 
47 //=============================================================================
48 //
49 // CLASS CursorPainter - IMPLEMENTATION
50 //
51 //=============================================================================
52 
53 //== INCLUDES =================================================================
54 
55 #include <QPixmap>
56 #include <QBitmap>
57 
59 
60 #include "CursorPainter.hh"
61 #include "QtBaseViewer.hh"
62 
63 //== NAMESPACES ===============================================================
64 
65 CursorPainter::CursorPainter (QObject *_parent) :
66  QObject(_parent),
67  cursor_(),
68  initialized_(false),
69  enabled_(false),
70  mouseIn_(false),
71  forceNative_(false),
72  xOff_(0),
73  yOff_(0),
74  texture_(0),
75  hasCursor_(false)
76 {
77 }
78 
79 //-----------------------------------------------------------------------------
80 
82 {
83  if (initialized_)
84  {
85  glDeleteTextures (1, &texture_);
86  }
87 }
88 
89 //-----------------------------------------------------------------------------
90 
91 void CursorPainter::setCursor (const QCursor &_cursor)
92 {
93  nativeCursor_ = _cursor;
94  cursorToCursor ();
95  cursorToTexture ();
96  if (!(initialized_ && enabled_ && hasCursor_) || forceNative_) {
97  foreach (glViewer *v, views_)
98  v->setCursor ((forceNative_)? nativeCursor_ : cursor_);
99  }
100 }
101 
102 //-----------------------------------------------------------------------------
103 
105 {
106  if (initialized_)
107  return;
108  initialized_ = true;
109 
110  // setup cursor texture
111  glGenTextures (1, &texture_);
112 
113  ACG::GLState::bindTexture (GL_TEXTURE_2D, texture_);
114  if(!OpenFlipper::Options::coreProfile())
115  {
116  glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP );
117  glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP );
118  }
119  glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
120  glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
121  ACG::GLState::bindTexture (GL_TEXTURE_2D, 0);
122 
123  cursorToTexture ();
124 
125  if (enabled_ && hasCursor_ && !forceNative_)
126  {
127  foreach (glViewer *v, views_)
128  v->setCursor (Qt::BlankCursor);
129 
130  }
131  else
132  {
133  foreach (glViewer *v, views_)
134  v->setCursor ((forceNative_)? nativeCursor_ : cursor_);
135 
136  }
137 }
138 
139 //-----------------------------------------------------------------------------
140 
142 {
143  views_.append (_viewer);
144  _viewer->setCursorPainter (this);
145 }
146 
147 //-----------------------------------------------------------------------------
148 
150 {
151  if (!initialized_)
152  return;
153 
154  if (!enabled())
155  return;
156 
157  // project (0, 0, 0) to get its position on the screen
158  ACG::Vec3d zPos = _state->project (ACG::Vec3d (0.0, 0.0, 0.0));
159  // unproject the result translated by 1px in x and y
160  zPos = _state->unproject (ACG::Vec3d (zPos[0] + 1, zPos[1] + 1, zPos[2]));
161 
162  // this gives us the size of one pixel in the current scene transformation
163  // now we can paint the cursor always with the same width/height
164  float xscale = zPos[0];
165  float yscale = -zPos[1];
166 
167  glPushAttrib (GL_ALL_ATTRIB_BITS);
168 
169  ACG::GLState::disable (GL_DEPTH_TEST);
170  ACG::GLState::disable(GL_LIGHTING);
171  ACG::GLState::enable(GL_BLEND);
172 
173  ACG::GLState::blendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
174 
175  // bind texture
176  ACG::GLState::enable (GL_TEXTURE_2D);
177  ACG::GLState::bindTexture (GL_TEXTURE_2D, texture_);
178 
179  glColor4f (1.0, 1.0, 1.0, 1.0);
180 
181  float x1 = -xOff_ * xscale;
182  float x2 = (32 - xOff_) * xscale;
183  float y1 = -yOff_ * yscale;
184  float y2 = (32 - yOff_) * yscale;
185 
186  // draw cursor quad
187  glBegin (GL_QUADS);
188  glTexCoord2f (0, 0);
189  glVertex3f (x1, y1, 0);
190  glTexCoord2f (0, 1);
191  glVertex3f (x1, y2, 0);
192  glTexCoord2f (1, 1);
193  glVertex3f (x2, y2, 0);
194  glTexCoord2f (1, 0);
195  glVertex3f (x2, y1, 0);
196  glEnd ();
197 
198 
199  glPopAttrib ();
200 }
201 
202 //-----------------------------------------------------------------------------
203 
204 void CursorPainter::updateCursorPosition (QPointF _scenePos)
205 {
206  cursorPos_ = _scenePos;
207  setMouseIn (true);
208 }
209 
210 //-----------------------------------------------------------------------------
211 
213 {
214  return cursorPos_;
215 }
216 
217 //-----------------------------------------------------------------------------
218 
219 void CursorPainter::setEnabled(bool _enabled)
220 {
221  enabled_ = _enabled;
222 
223  if (initialized_)
224  {
225  if (_enabled && hasCursor_)
226  {
227  foreach (glViewer *v, views_)
228  v->setCursor (Qt::BlankCursor);
229  }
230  else
231  {
232  foreach (glViewer *v, views_)
233  v->setCursor ((forceNative_)? nativeCursor_ : cursor_);
234  }
235  }
236 }
237 
238 //-----------------------------------------------------------------------------
239 
241 {
242  return initialized_ && enabled_ && hasCursor_ && mouseIn_ && !forceNative_;
243 }
244 
245 //-----------------------------------------------------------------------------
246 
248 {
249 
250  if (!initialized_) {
251  return;
252  }
253 
254  unsigned char buf[4096];
255  QImage cImg;
256 
257  hasCursor_ = false;
258 
259  // Initialize hotspot with default position in the upper left corner of the cursor
260  xOff_ = 0;
261  yOff_ = 0;
262 
264  switch (nativeCursor_.shape())
265  {
266  case Qt::ArrowCursor:
267  cImg.load (OpenFlipper::Options::iconDirStr()+OpenFlipper::Options::dirSeparator()+"cursor_arrow.png");
268  break;
269  case Qt::PointingHandCursor:
270  cImg.load (OpenFlipper::Options::iconDirStr()+OpenFlipper::Options::dirSeparator()+"cursor_move.png");
271  break;
272  case Qt::WhatsThisCursor:
273  cImg.load (OpenFlipper::Options::iconDirStr()+OpenFlipper::Options::dirSeparator()+"cursor_whatsthis.png");
274  break;
275  case Qt::BitmapCursor:
276  // Get the image of the cursor
277  cImg = QImage(( nativeCursor_.pixmap().toImage() ) );
278 
279 
280  // get the hotspot from the cursor
281  xOff_ = nativeCursor_.hotSpot().x();
282  yOff_ = nativeCursor_.hotSpot().y();
283  break;
284  default:
285  std::cerr << "cursorToTexture: Unknown cursor shape!" << nativeCursor_.shape() << std::endl;
286  return;
287  }
288 
289  // Check if the cursor dimension is matching our requirements
290  if (cImg.width () != 32 || cImg.height () != 32) {
291  std::cerr << "cursorToTexture: Dimension error" << nativeCursor_.shape() << std::endl;
292  return;
293  }
294 
295  // convert ARGB QImage to RGBA for gl
296  int index = 0;
297  for (int y = 0; y < cImg.height (); y++)
298  for (int x = 0; x < cImg.width (); x++)
299  {
300  QRgb pix = cImg.pixel (x, y);
301  buf[index] = qRed (pix);
302  buf[index+1] = qGreen (pix);
303  buf[index+2] = qBlue (pix);
304  buf[index+3] = qAlpha (pix);
305  index += 4;
306  }
307 
308 
309  // avoid call to glEnable(GL_TEXTURE_2D) in core profile
310 
311  if (!OpenFlipper::Options::coreProfile())
312  ACG::GLState::enable (GL_TEXTURE_2D);
313 
314  ACG::GLState::bindTexture (GL_TEXTURE_2D, texture_);
315  glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA, 32, 32, 0,
316  GL_RGBA, GL_UNSIGNED_BYTE, buf);
317  ACG::GLState::bindTexture (GL_TEXTURE_2D, 0);
318 
319  if (!OpenFlipper::Options::coreProfile())
320  ACG::GLState::disable (GL_TEXTURE_2D);
321 
322  hasCursor_ = true;
323 }
324 
325 //-----------------------------------------------------------------------------
326 
328 {
329  mouseIn_ = _in;
330 }
331 
332 //-----------------------------------------------------------------------------
333 
335 {
336  return QRectF (-xOff_, -yOff_, 32, 32);
337 }
338 
339 //=============================================================================
340 //=============================================================================
341 
342 void CursorPainter::setForceNative(bool _enabled)
343 {
344  forceNative_ = _enabled;
345 
346  if (!(initialized_ && enabled_ && hasCursor_) || forceNative_)
347  {
348  foreach (glViewer *v, views_)
349  v->setCursor ((forceNative_)? nativeCursor_ : cursor_);
350 
351  }
352  else
353  {
354  foreach (glViewer *v, views_)
355  v->setCursor (Qt::BlankCursor);
356  }
357 }
358 
359 void CursorPainter::cursorToCursor()
360 {
361  QPixmap pix;
362 
363  switch (nativeCursor_.shape())
364  {
365  case Qt::ArrowCursor:
366  pix.load (OpenFlipper::Options::iconDirStr()+OpenFlipper::Options::dirSeparator()+"cursor_arrow.png");
367  if (!pix.isNull() && pix.width() == 32 && pix.height() == 32)
368  {
369  cursor_ = QCursor (pix, 0, 0);
370  }
371  else
372  cursor_ = nativeCursor_;
373  break;
374  case Qt::PointingHandCursor:
375  pix.load (OpenFlipper::Options::iconDirStr()+OpenFlipper::Options::dirSeparator()+"cursor_move.png");
376  if (!pix.isNull() && pix.width() == 32 && pix.height() == 32)
377  {
378  cursor_ = QCursor (pix, 0, 0);
379  }
380  else
381  cursor_ = nativeCursor_;
382  break;
383  default:
384  cursor_ = nativeCursor_;
385  return;
386  }
387 }
388 
389 
390 
static void enable(GLenum _cap, bool _warnRemoved=true)
replaces glEnable, but supports locking
Definition: GLState.cc:1507
~CursorPainter()
Destructor.
QPointF cursorPosition()
Return the current cursor position.
void setEnabled(bool _enabled)
Enabled/Disables gl cursor painting.
bool enabled()
Returns true if cursor painting is enabled and compatible cursor is set.
Vec3d project(const Vec3d &_point) const
project point in world coordinates to window coordinates
Definition: GLState.cc:640
void initializeGL()
Needs to be called after the gl context has been set up to initialize internal values.
static void bindTexture(GLenum _target, GLuint _buffer)
replaces glBindTexture, supports locking
Definition: GLState.cc:1911
void updateCursorPosition(QPointF _scenePos)
Sets the current cursor position.
void setMouseIn(bool _in)
Inform the cursor painter about mouse enter / leave.
void cursorToTexture()
Vec3d unproject(const Vec3d &_winPoint) const
unproject point in window coordinates _winPoint to world coordinates
Definition: GLState.cc:651
static void disable(GLenum _cap, bool _warnRemoved=true)
replaces glDisable, but supports locking
Definition: GLState.cc:1527
void setCursor(const QCursor &_cursor)
Sets the current used cursor.
QRectF cursorBoundingBox()
Bounding box of the cursor.
void paintCursor(ACG::GLState *_state)
Cursor painting function. The _state has to be setup that 0,0,0 is at the cursor position.
void registerViewer(glViewer *_viewer)
Add a glViewer that will use this CursorPainter.
CursorPainter(QObject *_parent=0)
Constructor.
static void blendFunc(GLenum _sfactor, GLenum _dfactor)
replaces glBlendFunc, supports locking
Definition: GLState.hh:307
void setCursorPainter(CursorPainter *_cursorPainter)
sets the current cursor painter
void setForceNative(bool _enabled)
Enabled/Disables native cursors.