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