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