]> Pileus Git - grits/blob - src/objects/grits-object.c
Move OpenGL includes to a common place
[grits] / src / objects / grits-object.c
1 /*
2  * Copyright (C) 2009-2011 Andy Spencer <andy753421@gmail.com>
3  *
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 3 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
16  */
17
18 /**
19  * SECTION:grits-object
20  * @short_description: Base class for drawing operations
21  *
22  * Objects in grits are things which can be added to the viewer and will be
23  * displayed to the user. Each object has information such as it's location and
24  * level of detail which are used by the viewer to determine which objects
25  * should be drawn.
26  *
27  * Each #GritsObject is also a #GObject, but not every GObject in grits is a
28  * GritsObject. The "Object" part of the name is just coincidence.
29  */
30
31 #include <config.h>
32 #include <math.h>
33
34 #include "gtkgl.h"
35 #include "grits-object.h"
36
37 /* Constants */
38 enum {
39         SIG_ENTER,
40         SIG_LEAVE,
41         SIG_CLICKED,
42         SIG_BUTTON_PRESS,
43         SIG_BUTTON_RELEASE,
44         SIG_KEY_PRESS,
45         SIG_KEY_RELEASE,
46         SIG_MOTION,
47         NUM_SIGNALS,
48 };
49 static guint signals[NUM_SIGNALS];
50
51 void grits_object_pickdraw(GritsObject *object, GritsOpenGL *opengl, gboolean pick)
52 {
53         GritsObjectClass *klass = GRITS_OBJECT_GET_CLASS(object);
54
55         if (!klass->draw) {
56                 g_warning("GritsObject: draw - Unimplemented");
57                 return;
58         }
59
60         /* Skip hidden objects */
61         if (object->hidden)
62                 return;
63
64         /* Skip object with no signals when picking */
65         for (int i = 0; pick; i++) {
66                 if (i == NUM_SIGNALS)
67                         return;
68                 if (g_signal_has_handler_pending(object, signals[i], 0, FALSE))
69                         break;
70         }
71
72         /* Support GritsTester */
73         if (!GRITS_IS_OPENGL(opengl)) {
74                 g_debug("GritsObject: draw - drawing raw object");
75                 klass->draw(object, opengl);
76                 return;
77         }
78
79         /* Calculate distance for LOD and horizon tests */
80         GritsPoint *center = &object->center;
81         if ((!(object->skip & GRITS_SKIP_LOD) ||
82              !(object->skip & GRITS_SKIP_HORIZON)) &&
83             (center->elev != -EARTH_R)) {
84                 /* LOD test */
85                 gdouble eye[3], obj[3];
86                 grits_viewer_get_location(GRITS_VIEWER(opengl),
87                                 &eye[0], &eye[1], &eye[2]);
88                 gdouble elev = eye[2];
89                 lle2xyz(eye[0], eye[1], eye[2],
90                                 &eye[0], &eye[1], &eye[2]);
91                 lle2xyz(center->lat, center->lon, center->elev,
92                                 &obj[0], &obj[1], &obj[2]);
93                 gdouble dist = distd(obj, eye);
94
95                 /* Level of detail test */
96                 if (!(object->skip & GRITS_SKIP_LOD)
97                                 && object->lod > 0) {
98                         if (object->lod < dist)
99                                 return;
100                 }
101
102                 /* Horizon test */
103                 if (!(object->skip & GRITS_SKIP_HORIZON)) {
104                         gdouble c = EARTH_R+elev;
105                         gdouble a = EARTH_R;
106                         gdouble horizon = sqrt(c*c - a*a);
107                         if (dist > horizon)
108                                 return;
109                 }
110         }
111
112         /* Save state, draw, restore state */
113         g_mutex_lock(opengl->sphere_lock);
114         if (!(object->skip & GRITS_SKIP_STATE)) {
115                 glPushAttrib(GL_ALL_ATTRIB_BITS);
116                 glMatrixMode(GL_PROJECTION); glPushMatrix();
117                 glMatrixMode(GL_MODELVIEW);  glPushMatrix();
118         }
119
120         if (!(object->skip & GRITS_SKIP_CENTER))
121                 grits_viewer_center_position(GRITS_VIEWER(opengl),
122                                 object->center.lat,
123                                 object->center.lon,
124                                 object->center.elev);
125
126         if (pick && klass->pick)
127                 klass->pick(object, opengl);
128         else
129                 klass->draw(object, opengl);
130
131         if (!(object->skip & GRITS_SKIP_STATE)) {
132                 glPopAttrib();
133                 glMatrixMode(GL_PROJECTION); glPopMatrix();
134                 glMatrixMode(GL_MODELVIEW);  glPopMatrix();
135         }
136         g_mutex_unlock(opengl->sphere_lock);
137 }
138
139 /**
140  * grits_object_draw:
141  * @object: the object
142  * @opengl: the viewer the object is being displayed in
143  *
144  * Perform any OpenGL commands necessasairy to draw the object.
145  *
146  * The GL_PROJECTION and GL_MODELVIEW matricies and GL_ALL_ATTRIB_BITS will be
147  * restored to the default state after the call to draw.
148  */
149 void grits_object_draw(GritsObject *object, GritsOpenGL *opengl)
150 {
151         grits_object_pickdraw(object, opengl, FALSE);
152 }
153
154 void grits_object_hide(GritsObject *object, gboolean hidden)
155 {
156         GritsObjectClass *klass = GRITS_OBJECT_GET_CLASS(object);
157         object->hidden = hidden;
158         if (klass->hide)
159                 klass->hide(object, hidden);
160 }
161
162 void grits_object_queue_draw(GritsObject *object)
163 {
164         if (object->viewer)
165                 gtk_widget_queue_draw(GTK_WIDGET(object->viewer));
166 }
167
168 /* Event handling */
169 void grits_object_pick(GritsObject *object, GritsOpenGL *opengl)
170 {
171         grits_object_pickdraw(object, opengl, TRUE);
172 }
173
174 void grits_object_set_pointer(GritsObject *object, gboolean selected)
175 {
176         if (selected) {
177                 if (!object->state.selected)
178                         g_signal_emit(object, signals[SIG_ENTER], 0);
179                 object->state.selected = TRUE;
180         } else {
181                 if (object->state.selected)
182                         g_signal_emit(object, signals[SIG_LEAVE], 0);
183                 object->state.selected = FALSE;
184         }
185 }
186
187 void grits_object_event(GritsObject *object, GdkEvent *event)
188 {
189         const int map[GDK_EVENT_LAST] = {
190                 [GDK_BUTTON_PRESS  ] SIG_BUTTON_PRESS,
191                 [GDK_2BUTTON_PRESS ] SIG_BUTTON_PRESS,
192                 [GDK_3BUTTON_PRESS ] SIG_BUTTON_PRESS,
193                 [GDK_BUTTON_RELEASE] SIG_BUTTON_RELEASE,
194                 [GDK_KEY_PRESS     ] SIG_KEY_PRESS,
195                 [GDK_KEY_RELEASE   ] SIG_KEY_RELEASE,
196                 [GDK_MOTION_NOTIFY ] SIG_MOTION,
197         };
198         if (!object->state.selected)
199                 return;
200         guint sig = map[event->type];
201
202         /* Handle button click */
203         if (sig == SIG_BUTTON_PRESS)
204                 object->state.clicking = TRUE;
205         if (sig == SIG_BUTTON_RELEASE && object->state.clicking)
206                 g_signal_emit(object, signals[SIG_CLICKED], 0, event);
207         if (sig == SIG_BUTTON_RELEASE || sig == SIG_MOTION)
208                 object->state.clicking = FALSE;
209
210         /* Emit this signal */
211         if (!g_signal_has_handler_pending(object, signals[sig], 0, FALSE))
212                 return;
213         g_signal_emit(object, signals[sig], 0, event);
214 }
215
216 /* GObject stuff */
217 G_DEFINE_ABSTRACT_TYPE(GritsObject, grits_object, G_TYPE_OBJECT);
218 static void grits_object_init(GritsObject *object)
219 {
220         object->center.lat  =  0;
221         object->center.lon  =  0;
222         object->center.elev = -EARTH_R;
223 }
224
225 static void grits_object_class_init(GritsObjectClass *klass)
226 {
227         GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
228
229         /**
230          * GritsObject::enter:
231          * @object: the object.
232          *
233          * The ::enter signal is emitted when the pointer moves over the object
234          */
235         signals[SIG_ENTER] = g_signal_new(
236                         "enter",
237                         G_TYPE_FROM_CLASS(gobject_class),
238                         G_SIGNAL_RUN_LAST,
239                         0,
240                         NULL,
241                         NULL,
242                         g_cclosure_marshal_VOID__VOID,
243                         G_TYPE_NONE,
244                         0);
245
246         /**
247          * GritsViewer::leave:
248          * @object: the object.
249          *
250          * The ::leave signal is emitted when the pointer moves away from the
251          * object
252          */
253         signals[SIG_LEAVE] = g_signal_new(
254                         "leave",
255                         G_TYPE_FROM_CLASS(gobject_class),
256                         G_SIGNAL_RUN_LAST,
257                         0,
258                         NULL,
259                         NULL,
260                         g_cclosure_marshal_VOID__VOID,
261                         G_TYPE_NONE,
262                         0);
263
264         /**
265          * GritsViewer::clicked:
266          * @object: the object.
267          *
268          * The ::clicked signal is emitted when the user clicks on the object
269          */
270         signals[SIG_CLICKED] = g_signal_new(
271                         "clicked",
272                         G_TYPE_FROM_CLASS(gobject_class),
273                         G_SIGNAL_RUN_LAST,
274                         0,
275                         NULL,
276                         NULL,
277                         g_cclosure_marshal_VOID__VOID,
278                         G_TYPE_NONE,
279                         0);
280
281         /**
282          * GritsViewer::button-press:
283          * @object: the object.
284          * @event:  the GdkEventButton which triggered this signal
285          *
286          * The ::button-press signal is emitted when a button (typically from a
287          * mouse) is pressed.
288          */
289         signals[SIG_BUTTON_PRESS] = g_signal_new(
290                         "button-press",
291                         G_TYPE_FROM_CLASS(gobject_class),
292                         G_SIGNAL_RUN_LAST,
293                         0,
294                         NULL,
295                         NULL,
296                         g_cclosure_marshal_VOID__POINTER,
297                         G_TYPE_NONE,
298                         1,
299                         G_TYPE_POINTER);
300
301         /**
302          * GritsViewer::button-release:
303          * @object: the object.
304          * @event:  the GdkEventButton which triggered this signal
305          *
306          * The ::button-release signal is emitted when a button (typically from
307          * a mouse) is released.
308          */
309         signals[SIG_BUTTON_RELEASE] = g_signal_new(
310                         "button-release",
311                         G_TYPE_FROM_CLASS(gobject_class),
312                         G_SIGNAL_RUN_LAST,
313                         0,
314                         NULL,
315                         NULL,
316                         g_cclosure_marshal_VOID__POINTER,
317                         G_TYPE_NONE,
318                         1,
319                         G_TYPE_POINTER);
320
321         /**
322          * GritsViewer::key-press:
323          * @object: the object.
324          * @event:  the GdkEventKey which triggered this signal
325          *
326          * The ::key-press signal is emitted when a key is pressed.
327          */
328         signals[SIG_KEY_PRESS] = g_signal_new(
329                         "key-press",
330                         G_TYPE_FROM_CLASS(gobject_class),
331                         G_SIGNAL_RUN_LAST,
332                         0,
333                         NULL,
334                         NULL,
335                         g_cclosure_marshal_VOID__POINTER,
336                         G_TYPE_NONE,
337                         1,
338                         G_TYPE_POINTER);
339
340         /**
341          * GritsViewer::key-release:
342          * @object: the object.
343          * @event:  the GdkEventKey which triggered this signal
344          *
345          * The ::key-release signal is emitted when a key is released.
346          */
347         signals[SIG_KEY_RELEASE] = g_signal_new(
348                         "key-release",
349                         G_TYPE_FROM_CLASS(gobject_class),
350                         G_SIGNAL_RUN_LAST,
351                         0,
352                         NULL,
353                         NULL,
354                         g_cclosure_marshal_VOID__POINTER,
355                         G_TYPE_NONE,
356                         1,
357                         G_TYPE_POINTER);
358
359         /**
360          * GritsViewer::motion:
361          * @object: the object.
362          * @event:  the GdkEventMotion which triggered this signal
363          *
364          * The ::motion signal is emitted the pointer moves over the object
365          */
366         signals[SIG_MOTION] = g_signal_new(
367                         "motion",
368                         G_TYPE_FROM_CLASS(gobject_class),
369                         G_SIGNAL_RUN_LAST,
370                         0,
371                         NULL,
372                         NULL,
373                         g_cclosure_marshal_VOID__POINTER,
374                         G_TYPE_NONE,
375                         1,
376                         G_TYPE_POINTER);
377 }