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