xref: /haiku/src/apps/glteapot/ObjectView.cpp (revision d374a27286b8a52974a97dba0d5966ea026a665d)
1 /*
2  * Copyright 2008 Haiku Inc. All rights reserved.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Alexandre Deckner
7  *
8  */
9 
10 /*
11  * Original Be Sample source modified to use a quaternion for the object's orientation
12  */
13 
14 /*
15 	Copyright 1999, Be Incorporated.   All Rights Reserved.
16 	This file may be used under the terms of the Be Sample Code License.
17 */
18 
19 #include "ObjectView.h"
20 
21 #include <stdio.h>
22 #include <stdlib.h>
23 
24 #include <Cursor.h>
25 #include <InterfaceKit.h>
26 #include <FindDirectory.h>
27 
28 #include "FPS.h"
29 #include "GLObject.h"
30 #include "ResScroll.h"
31 
32 #define teapotData "teapot.data"
33 char teapotPath[PATH_MAX];
34 
35 float displayScale = 1.0;
36 float depthOfView = 30.0;
37 float zRatio = 10.0;
38 
39 float white[3] = {1.0, 1.0, 1.0};
40 float dimWhite[3] = {0.25, 0.25, 0.25};
41 float black[3] = {0.0, 0.0, 0.0};
42 float foggy[3] = {0.4, 0.4, 0.4};
43 float blue[3] = {0.0, 0.0, 1.0};
44 float dimBlue[3] = {0.0, 0.0, 0.5};
45 float yellow[3] = {1.0, 1.0, 0.0};
46 float dimYellow[3] = {0.5, 0.5, 0.0};
47 float green[3] = {0.0, 1.0, 0.0};
48 float dimGreen[3] = {0.0, 0.5, 0.0};
49 float red[3] = {1.0, 0.0, 0.0};
50 
51 float* bgColor = black;
52 
53 
54 struct light {
55 	float *ambient;
56 	float *diffuse;
57 	float *specular;
58 };
59 
60 
61 light lights[] = {
62 	{NULL, NULL, NULL},
63 	{dimWhite, white, white},
64 	{dimWhite, yellow, yellow},
65 	{dimWhite, red, red},
66 	{dimWhite, blue, blue},
67 	{dimWhite, green, green}
68 };
69 
70 
71 
72 long
73 signalEvent(sem_id event)
74 {
75 	long c;
76 	get_sem_count(event,&c);
77 	if (c < 0)
78 		release_sem_etc(event,-c,0);
79 
80 	return 0;
81 }
82 
83 
84 long
85 setEvent(sem_id event)
86 {
87 	long c;
88 	get_sem_count(event,&c);
89 	if (c < 0)
90 	  release_sem_etc(event,-c,0);
91 
92 	return 0;
93 }
94 
95 
96 long
97 waitEvent(sem_id event)
98 {
99 	acquire_sem(event);
100 
101 	long c;
102 	get_sem_count(event,&c);
103 	if (c > 0)
104 		acquire_sem_etc(event,c,0,0);
105 
106 	return 0;
107 }
108 
109 
110 static int32
111 simonThread(void* cookie)
112 {
113 	ObjectView* objectView = reinterpret_cast<ObjectView*>(cookie);
114 
115 	int noPause = 0;
116 	while (acquire_sem_etc(objectView->quittingSem, 1, B_TIMEOUT, 0) == B_NO_ERROR) {
117 		if (objectView->SpinIt()) {
118 			objectView->DrawFrame(noPause);
119 			release_sem(objectView->quittingSem);
120 			noPause = 1;
121 		} else {
122 			release_sem(objectView->quittingSem);
123 			noPause = 0;
124 			waitEvent(objectView->drawEvent);
125 		}
126 	}
127 	return 0;
128 }
129 
130 
131 ObjectView::ObjectView(BRect rect, const char *name, ulong resizingMode,
132 	ulong options)
133 	: BGLView(rect, name, resizingMode, 0, options),
134 	fHistEntries(0),
135 	fOldestEntry(0),
136 	fFps(true),
137 	fLastGouraud(true),
138 	fGouraud(true),
139 	fLastZbuf(true),
140 	fZbuf(true),
141 	fLastCulling(true),
142 	fCulling(true),
143 	fLastLighting(true),
144 	fLighting(true),
145 	fLastFilled(true),
146 	fFilled(true),
147 	fLastPersp(false),
148 	fPersp(false),
149 	fLastTextured(false),
150 	fTextured(false),
151 	fLastFog(false),
152 	fFog(false),
153 	fForceRedraw(false),
154 	fLastYXRatio(1),
155 	fYxRatio(1)
156 {
157 	fTrackingInfo.isTracking = false;
158 	fTrackingInfo.pickedObject = NULL;
159 	fTrackingInfo.buttons = 0;
160 	fTrackingInfo.lastX = 0.0f;
161 	fTrackingInfo.lastY = 0.0f;
162 	fTrackingInfo.lastDx = 0.0f;
163 	fTrackingInfo.lastDy = 0.0f;
164 
165 	fLastObjectDistance = fObjectDistance = depthOfView / 8;
166 	quittingSem = create_sem(1, "quitting sem");
167 	drawEvent = create_sem(0, "draw event");
168 
169 	char findDir[PATH_MAX];
170 	find_directory(B_SYSTEM_DATA_DIRECTORY, -1, true, findDir, PATH_MAX);
171 	sprintf(teapotPath, "%s/%s", findDir, teapotData);
172 	fObjListLock.Lock();
173 	fObjects.AddItem(new TriangleObject(this, teapotPath));
174 	fObjListLock.Unlock();
175 }
176 
177 
178 ObjectView::~ObjectView()
179 {
180 	delete_sem(quittingSem);
181 	delete_sem(drawEvent);
182 }
183 
184 
185 void
186 ObjectView::AttachedToWindow()
187 {
188 	float position[] = {0.0, 3.0, 3.0, 0.0};
189 	float position1[] = {-3.0, -3.0, 3.0, 0.0};
190 	float position2[] = {3.0, 0.0, 0.0, 0.0};
191 	float local_view[] = {0.0, 0.0};
192 //	float ambient[] = {0.1745, 0.03175, 0.03175};
193 //	float diffuse[] = {0.61424, 0.10136, 0.10136};
194 //	float specular[] = {0.727811, 0.626959, 0.626959};
195 //	rgb_color black = {0, 0, 0, 255};
196 	BRect bounds = Bounds();
197 
198 	BGLView::AttachedToWindow();
199 	Window()->SetPulseRate(100000);
200 
201 	LockGL();
202 
203 	glEnable(GL_DITHER);
204 	glEnable(GL_CULL_FACE);
205 	glCullFace(GL_BACK);
206 	glDepthFunc(GL_LESS);
207 
208 	glShadeModel(GL_SMOOTH);
209 
210 	glLightfv(GL_LIGHT0, GL_POSITION, position);
211 	glLightfv(GL_LIGHT0 + 1, GL_POSITION, position1);
212 	glLightfv(GL_LIGHT0 + 2, GL_POSITION, position2);
213 	glLightModelfv(GL_LIGHT_MODEL_LOCAL_VIEWER, local_view);
214 
215 	glEnable(GL_LIGHT0);
216 	glLightfv(GL_LIGHT0, GL_SPECULAR, lights[lightWhite].specular);
217 	glLightfv(GL_LIGHT0, GL_DIFFUSE,lights[lightWhite].diffuse);
218 	glLightfv(GL_LIGHT0, GL_AMBIENT,lights[lightWhite].ambient);
219 	glEnable(GL_LIGHT1);
220 	glLightfv(GL_LIGHT1, GL_SPECULAR, lights[lightBlue].specular);
221 	glLightfv(GL_LIGHT1, GL_DIFFUSE,lights[lightBlue].diffuse);
222 	glLightfv(GL_LIGHT1, GL_AMBIENT,lights[lightBlue].ambient);
223 
224 	glFrontFace(GL_CW);
225 	glEnable(GL_LIGHTING);
226 	glEnable(GL_AUTO_NORMAL);
227 	glEnable(GL_NORMALIZE);
228 
229 	glMaterialf(GL_FRONT, GL_SHININESS, 0.6 * 128.0);
230 
231 	glClearColor(bgColor[0], bgColor[1], bgColor[2], 1.0);
232 	glColor3f(1.0, 1.0, 1.0);
233 
234 	glViewport(0, 0, (GLint)bounds.IntegerWidth() + 1,
235 				(GLint)bounds.IntegerHeight() + 1);
236 	glMatrixMode(GL_PROJECTION);
237 	glLoadIdentity();
238 
239 	float scale = displayScale;
240 	glOrtho(-scale, scale, -scale, scale, -scale * depthOfView,
241 			scale * depthOfView);
242 	glMatrixMode(GL_MODELVIEW);
243 	glLoadIdentity();
244 
245 	UnlockGL();
246 
247 	fDrawThread = spawn_thread(simonThread, "Simon", B_NORMAL_PRIORITY, this);
248 	resume_thread(fDrawThread);
249 	fForceRedraw = true;
250 	setEvent(drawEvent);
251 }
252 
253 
254 void
255 ObjectView::DetachedFromWindow()
256 {
257 	BGLView::DetachedFromWindow();
258 
259 	long dummy;
260 	long locks = 0;
261 
262 	while (Window()->IsLocked()) {
263 		locks++;
264 		Window()->Unlock();
265 	}
266 
267 	acquire_sem(quittingSem);
268 	release_sem(drawEvent);
269 	wait_for_thread(fDrawThread, &dummy);
270 	release_sem(quittingSem);
271 
272 	while (locks--)
273 		Window()->Lock();
274 }
275 
276 
277 void
278 ObjectView::Pulse()
279 {
280 	Window()->Lock();
281 	BRect parentBounds = Parent()->Bounds();
282 	BRect bounds = Bounds();
283 	parentBounds.OffsetTo(0, 0);
284 	bounds.OffsetTo(0, 0);
285 	if (bounds != parentBounds) {
286 		ResizeTo(parentBounds.right - parentBounds.left,
287 				 parentBounds.bottom - parentBounds.top);
288 	}
289 	Window()->Unlock();
290 }
291 
292 
293 void
294 ObjectView::MessageReceived(BMessage* msg)
295 {
296 	BMenuItem* item = NULL;
297 	bool toggleItem = false;
298 
299 	switch (msg->what) {
300 		case kMsgFPS:
301 			fFps = (fFps) ? false : true;
302 			msg->FindPointer("source", reinterpret_cast<void**>(&item));
303 			item->SetMarked(fFps);
304 			fForceRedraw = true;
305 			setEvent(drawEvent);
306 			break;
307 		case kMsgAddModel:
308 			fObjListLock.Lock();
309 			fObjects.AddItem(new TriangleObject(this, teapotPath));
310 			fObjListLock.Unlock();
311 			setEvent(drawEvent);
312 			break;
313 		case kMsgLights:
314 		{
315 			msg->FindPointer("source", reinterpret_cast<void**>(&item));
316 			long lightNum = msg->FindInt32("num");
317 			long color = msg->FindInt32("color");
318 			BMenu *menu = item->Menu();
319 			long index = menu->IndexOf(item);
320 			menu->ItemAt(index)->SetMarked(true);
321 			for (int i = 0; i < menu->CountItems(); i++) {
322 				if (i != index)
323 					menu->ItemAt(i)->SetMarked(false);
324 			}
325 
326 			LockGL();
327 			if (color != lightNone) {
328 				glEnable(GL_LIGHT0 + lightNum - 1);
329 				glLightfv(GL_LIGHT0 + lightNum - 1, GL_SPECULAR,
330 					lights[color].specular);
331 				glLightfv(GL_LIGHT0 + lightNum - 1, GL_DIFFUSE,
332 					lights[color].diffuse);
333 				glLightfv(GL_LIGHT0 + lightNum - 1, GL_AMBIENT,
334 					lights[color].ambient);
335 			} else {
336 				glDisable(GL_LIGHT0 + lightNum - 1);
337 			}
338 			UnlockGL();
339 			fForceRedraw = true;
340 			setEvent(drawEvent);
341 			break;
342 		}
343 		case kMsgGouraud:
344 			fGouraud = !fGouraud;
345 			toggleItem = true;
346 			break;
347 		case kMsgZBuffer:
348 			fZbuf = !fZbuf;
349 			toggleItem = true;
350 			break;
351 		case kMsgCulling:
352 			fCulling = !fCulling;
353 			toggleItem = true;
354 			break;
355 		case kMsgLighting:
356 			fLighting = !fLighting;
357 			toggleItem = true;
358 			break;
359 		case kMsgFilled:
360 			fFilled = !fFilled;
361 			toggleItem = true;
362 			break;
363 		case kMsgPerspective:
364 			fPersp = !fPersp;
365 			toggleItem = true;
366 			break;
367 		case kMsgFog:
368 			fFog = !fFog;
369 			toggleItem = true;
370 			break;
371 	}
372 
373 	if (toggleItem && msg->FindPointer("source", reinterpret_cast<void**>(&item)) == B_OK){
374 		item->SetMarked(!item->IsMarked());
375 		setEvent(drawEvent);
376 	}
377 
378 	BGLView::MessageReceived(msg);
379 }
380 
381 
382 int
383 ObjectView::ObjectAtPoint(const BPoint &point)
384 {
385 	LockGL();
386 	glShadeModel(GL_FLAT);
387 	glDisable(GL_LIGHTING);
388 	glDisable(GL_FOG);
389 	glClearColor(black[0], black[1], black[2], 1.0);
390 	glClear(GL_COLOR_BUFFER_BIT | (fZbuf ? GL_DEPTH_BUFFER_BIT : 0));
391 
392 	float idColor[3];
393 	idColor[1] = idColor[2] = 0;
394 	for (int i = 0; i < fObjects.CountItems(); i++) {
395 		// to take into account 16 bits colorspaces,
396 		// only use the 5 highest bits of the red channel
397 		idColor[0] = (255 - (i << 3)) / 255.0;
398 		reinterpret_cast<GLObject*>(fObjects.ItemAt(i))->Draw(true, idColor);
399 	}
400 	glReadBuffer(GL_BACK);
401 	uchar pixel[256];
402 	glReadPixels((GLint)point.x, (GLint)(Bounds().bottom - point.y), 1, 1,
403 		GL_RGB, GL_UNSIGNED_BYTE, pixel);
404 	int objNum = pixel[0];
405 	objNum = (255 - objNum) >> 3;
406 
407 	EnforceState();
408 	UnlockGL();
409 
410 	return objNum;
411 }
412 
413 
414 void
415 ObjectView::MouseDown(BPoint point)
416 {
417 	GLObject* object = NULL;
418 
419 	BMessage *msg = Window()->CurrentMessage();
420 	uint32 buttons = msg->FindInt32("buttons");
421 	object = reinterpret_cast<GLObject*>(fObjects.ItemAt(ObjectAtPoint(point)));
422 
423 	if (object != NULL){
424 		if (buttons == B_PRIMARY_MOUSE_BUTTON || buttons == B_SECONDARY_MOUSE_BUTTON) {
425 			fTrackingInfo.pickedObject = object;
426 			fTrackingInfo.buttons = buttons;
427 			fTrackingInfo.isTracking = true;
428 			fTrackingInfo.lastX = point.x;
429 			fTrackingInfo.lastY = point.y;
430 			fTrackingInfo.lastDx = 0.0f;
431 			fTrackingInfo.lastDy = 0.0f;
432 			fTrackingInfo.pickedObject->Spin(0.0f, 0.0f);
433 
434 
435 			SetMouseEventMask(B_POINTER_EVENTS,
436 						B_LOCK_WINDOW_FOCUS | B_NO_POINTER_HISTORY);
437 
438 			BCursor grabbingCursor(B_CURSOR_ID_GRABBING);
439 			SetViewCursor(&grabbingCursor);
440 		} else {
441 			ConvertToScreen(&point);
442 			object->MenuInvoked(point);
443 		}
444 	}
445 }
446 
447 
448 void
449 ObjectView::MouseUp(BPoint point)
450 {
451 	if (fTrackingInfo.isTracking) {
452 
453 		//spin the teapot on release, TODO: use a marching sum and divide by time
454 		if (fTrackingInfo.buttons == B_PRIMARY_MOUSE_BUTTON
455 			&& fTrackingInfo.pickedObject != NULL
456 			&& (fabs(fTrackingInfo.lastDx) > 1.0f
457 				|| fabs(fTrackingInfo.lastDy) > 1.0f) ) {
458 
459 			fTrackingInfo.pickedObject->Spin(0.5f * fTrackingInfo.lastDy, 0.5f * fTrackingInfo.lastDx);
460 
461 			setEvent(drawEvent);
462 		}
463 
464 		//stop tracking
465 		fTrackingInfo.isTracking = false;
466 		fTrackingInfo.buttons = 0;
467 		fTrackingInfo.pickedObject = NULL;
468 		fTrackingInfo.lastX = 0.0f;
469 		fTrackingInfo.lastY = 0.0f;
470 		fTrackingInfo.lastDx = 0.0f;
471 		fTrackingInfo.lastDy = 0.0f;
472 
473 		BCursor grabCursor(B_CURSOR_ID_GRAB);
474 		SetViewCursor(&grabCursor);
475 	}
476 }
477 
478 
479 void
480 ObjectView::MouseMoved(BPoint point, uint32 transit, const BMessage *msg)
481 {
482 	if (fTrackingInfo.isTracking && fTrackingInfo.pickedObject != NULL) {
483 
484 		float dx = point.x - fTrackingInfo.lastX;
485 		float dy = point.y - fTrackingInfo.lastY;
486 		fTrackingInfo.lastX = point.x;
487 		fTrackingInfo.lastY = point.y;
488 
489 		if (fTrackingInfo.buttons == B_PRIMARY_MOUSE_BUTTON) {
490 
491 			fTrackingInfo.pickedObject->Spin(0.0f, 0.0f);
492 			fTrackingInfo.pickedObject->RotateWorldSpace(dx,dy);
493 			fTrackingInfo.lastDx = dx;
494 			fTrackingInfo.lastDy = dy;
495 
496 			setEvent(drawEvent);
497 
498 		} else if (fTrackingInfo.buttons == B_SECONDARY_MOUSE_BUTTON) {
499 
500 			float xinc = (dx * 2 * displayScale / Bounds().Width());
501 			float yinc = (-dy * 2 * displayScale / Bounds().Height());
502 			float zinc = 0;
503 
504 			if (fPersp) {
505 				zinc = yinc * (fTrackingInfo.pickedObject->z / displayScale);
506 				xinc *= -(fTrackingInfo.pickedObject->z * 4 / zRatio);
507 				yinc *= -(fTrackingInfo.pickedObject->z * 4 / zRatio);
508 			}
509 
510 			fTrackingInfo.pickedObject->x += xinc;
511 			if (modifiers() & B_SHIFT_KEY)
512 				fTrackingInfo.pickedObject->z += zinc;
513 			else
514 	  			fTrackingInfo.pickedObject->y += yinc;
515 
516 			fForceRedraw = true;
517 			setEvent(drawEvent);
518 		}
519 	} else {
520 		void* object = fObjects.ItemAt(ObjectAtPoint(point));
521 		BCursor cursor(object != NULL
522 			? B_CURSOR_ID_GRAB : B_CURSOR_ID_SYSTEM_DEFAULT);
523 		SetViewCursor(&cursor);
524 	}
525 }
526 
527 
528 void
529 ObjectView::FrameResized(float width, float height)
530 {
531 	LockGL();
532 
533 	BGLView::FrameResized(width, height);
534 
535 	width = Bounds().Width();
536 	height = Bounds().Height();
537 	fYxRatio = height / width;
538     glViewport(0, 0, (GLint)width + 1, (GLint)height + 1);
539 
540 	// To prevent weird buffer contents
541 	glClear(GL_COLOR_BUFFER_BIT);
542 
543 	glMatrixMode(GL_PROJECTION);
544 	glLoadIdentity();
545 	float scale = displayScale;
546 
547 	if (fPersp) {
548 		gluPerspective(60, 1.0 / fYxRatio, 0.15, 120);
549 	} else {
550 		if (fYxRatio < 1) {
551 			glOrtho(-scale / fYxRatio, scale / fYxRatio, -scale, scale, -1.0,
552 				depthOfView * 4);
553 		} else {
554 			glOrtho(-scale, scale, -scale * fYxRatio, scale * fYxRatio, -1.0,
555 				depthOfView * 4);
556 		}
557 	}
558 
559 	fLastYXRatio = fYxRatio;
560 
561 	glMatrixMode(GL_MODELVIEW);
562 
563 	UnlockGL();
564 
565 	fForceRedraw = true;
566 	setEvent(drawEvent);
567 }
568 
569 
570 bool
571 ObjectView::RepositionView()
572 {
573 	if (!(fPersp != fLastPersp) &&
574 		!(fLastObjectDistance != fObjectDistance) &&
575 		!(fLastYXRatio != fYxRatio)) {
576 		return false;
577 	}
578 
579 	LockGL();
580 
581 	glMatrixMode(GL_PROJECTION);
582 	glLoadIdentity();
583 	float scale = displayScale;
584 
585 	if (fPersp) {
586 		gluPerspective(60, 1.0 / fYxRatio, 0.15, 120);
587 	} else {
588 		if (fYxRatio < 1) {
589 			glOrtho(-scale / fYxRatio, scale / fYxRatio, -scale, scale, -1.0,
590 				depthOfView * 4);
591 		} else {
592 			glOrtho(-scale, scale, -scale * fYxRatio, scale * fYxRatio, -1.0,
593 				depthOfView * 4);
594 		}
595 	}
596 
597 	glMatrixMode(GL_MODELVIEW);
598 
599 	UnlockGL();
600 
601 	fLastObjectDistance = fObjectDistance;
602 	fLastPersp = fPersp;
603 	fLastYXRatio = fYxRatio;
604 	return true;
605 }
606 
607 
608 void
609 ObjectView::EnforceState()
610 {
611 	glShadeModel(fGouraud ? GL_SMOOTH : GL_FLAT);
612 
613 	if (fZbuf)
614 		glEnable(GL_DEPTH_TEST);
615 	else
616 		glDisable(GL_DEPTH_TEST);
617 
618 	if (fCulling)
619 		glEnable(GL_CULL_FACE);
620 	else
621 		glDisable(GL_CULL_FACE);
622 
623 	if (fLighting)
624 		glEnable(GL_LIGHTING);
625 	else
626 		glDisable(GL_LIGHTING);
627 
628 	if (fFilled)
629 		glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
630 	else
631 		glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
632 
633 	if (fFog) {
634 		glFogf(GL_FOG_START, 10.0);
635 		glFogf(GL_FOG_DENSITY, 0.2);
636 		glFogf(GL_FOG_END, depthOfView);
637 		glFogfv(GL_FOG_COLOR, foggy);
638 		glEnable(GL_FOG);
639 		bgColor = foggy;
640 		glClearColor(bgColor[0], bgColor[1], bgColor[2], 1.0);
641 	} else {
642 		glDisable(GL_FOG);
643 		bgColor = black;
644 		glClearColor(bgColor[0], bgColor[1], bgColor[2], 1.0);
645 	}
646 }
647 
648 
649 bool
650 ObjectView::SpinIt()
651 {
652 	bool changed = false;
653 
654 	if (fGouraud != fLastGouraud) {
655 		LockGL();
656 		glShadeModel(fGouraud ? GL_SMOOTH : GL_FLAT);
657 		UnlockGL();
658 		fLastGouraud = fGouraud;
659 		changed = true;
660 	}
661 
662 	if (fZbuf != fLastZbuf) {
663 		LockGL();
664 		if (fZbuf)
665 			glEnable(GL_DEPTH_TEST);
666 		else
667 			glDisable(GL_DEPTH_TEST);
668 		UnlockGL();
669 		fLastZbuf = fZbuf;
670 		changed = true;
671 	}
672 
673 	if (fCulling != fLastCulling) {
674 		LockGL();
675 		if (fCulling)
676 			glEnable(GL_CULL_FACE);
677 		else
678 			glDisable(GL_CULL_FACE);
679 		UnlockGL();
680 		fLastCulling = fCulling;
681 		changed = true;
682 	}
683 
684 	if (fLighting != fLastLighting) {
685 		LockGL();
686 		if (fLighting)
687 			glEnable(GL_LIGHTING);
688 		else
689 			glDisable(GL_LIGHTING);
690 		UnlockGL();
691 		fLastLighting = fLighting;
692 		changed = true;
693 	}
694 
695 	if (fFilled != fLastFilled) {
696 		LockGL();
697 		if (fFilled) {
698 			glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
699 		} else {
700 			glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
701 		}
702 		UnlockGL();
703 		fLastFilled = fFilled;
704 		changed = true;
705 	}
706 
707 	if (fFog != fLastFog) {
708 		if (fFog) {
709 			glFogf(GL_FOG_START, 1.0);
710 			glFogf(GL_FOG_DENSITY, 0.2);
711 			glFogf(GL_FOG_END, depthOfView);
712 			glFogfv(GL_FOG_COLOR, foggy);
713 			glEnable(GL_FOG);
714 			bgColor = foggy;
715 			glClearColor(bgColor[0], bgColor[1], bgColor[2], 1.0);
716 		} else {
717 			glDisable(GL_FOG);
718 			bgColor = black;
719 			glClearColor(bgColor[0], bgColor[1], bgColor[2], 1.0);
720 		}
721 		fLastFog = fFog;
722 		changed = true;
723 	}
724 
725 	changed = changed || RepositionView();
726 	changed = changed || fForceRedraw;
727 	fForceRedraw = false;
728 
729 	for (int i = 0; i < fObjects.CountItems(); i++) {
730 		bool hack = reinterpret_cast<GLObject*>(fObjects.ItemAt(i))->SpinIt();
731 		changed = changed || hack;
732 	}
733 
734 	return changed;
735 }
736 
737 
738 void
739 ObjectView::DrawFrame(bool noPause)
740 {
741 	LockGL();
742 	glClear(GL_COLOR_BUFFER_BIT | (fZbuf ? GL_DEPTH_BUFFER_BIT : 0));
743 
744 	fObjListLock.Lock();
745 	for (int i = 0; i < fObjects.CountItems(); i++) {
746 	  GLObject *object = reinterpret_cast<GLObject*>(fObjects.ItemAt(i));
747 		if (object->Solidity() == 0)
748 			object->Draw(false, NULL);
749 	}
750 	EnforceState();
751 	for (int i = 0; i < fObjects.CountItems(); i++) {
752 		GLObject *object = reinterpret_cast<GLObject*>(fObjects.ItemAt(i));
753 		if (object->Solidity() != 0)
754 			object->Draw(false, NULL);
755 	}
756 	fObjListLock.Unlock();
757 
758 	glDisable(GL_BLEND);
759 	glDepthMask(GL_TRUE);
760 
761 	if (noPause) {
762 		uint64 now = system_time();
763 		float fps = 1.0 / ((now - fLastFrame) / 1000000.0);
764 		fLastFrame = now;
765 		int entry;
766 		if (fHistEntries < HISTSIZE) {
767 			entry = (fOldestEntry + fHistEntries) % HISTSIZE;
768 			fHistEntries++;
769 		} else {
770 			entry = fOldestEntry;
771 			fOldestEntry = (fOldestEntry + 1) % HISTSIZE;
772 		}
773 
774 		fFpsHistory[entry] = fps;
775 
776 		if (fHistEntries > 5) {
777 			fps = 0;
778 			for (int i = 0; i < fHistEntries; i++)
779 				fps += fFpsHistory[(fOldestEntry + i) % HISTSIZE];
780 
781 			fps /= fHistEntries;
782 
783 			if (fFps) {
784 				glPushAttrib(GL_ENABLE_BIT | GL_LIGHTING_BIT);
785 				glPushMatrix();
786 				glLoadIdentity();
787 				glTranslatef(-0.9, -0.9, 0);
788 				glScalef(0.10, 0.10, 0.10);
789 				glDisable(GL_LIGHTING);
790 				glDisable(GL_DEPTH_TEST);
791 				glDisable(GL_BLEND);
792 				glColor3f(1.0, 1.0, 0);
793 				glMatrixMode(GL_PROJECTION);
794 				glPushMatrix();
795 				glLoadIdentity();
796 				glMatrixMode(GL_MODELVIEW);
797 
798 				FPS::drawCounter(fps);
799 
800 				glMatrixMode(GL_PROJECTION);
801 				glPopMatrix();
802 				glMatrixMode(GL_MODELVIEW);
803 				glPopMatrix();
804 				glPopAttrib();
805 			}
806 		}
807 	} else {
808 		fHistEntries = 0;
809 		fOldestEntry = 0;
810 	}
811 	SwapBuffers();
812 	UnlockGL();
813 }
814