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