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
34float displayScale = 1.0;
35float depthOfView = 30.0;
36float zRatio = 10.0;
37
38float white[3] = {1.0, 1.0, 1.0};
39float dimWhite[3] = {0.25, 0.25, 0.25};
40float black[3] = {0.0, 0.0, 0.0};
41float foggy[3] = {0.4, 0.4, 0.4};
42float blue[3] = {0.0, 0.0, 1.0};
43float dimBlue[3] = {0.0, 0.0, 0.5};
44float yellow[3] = {1.0, 1.0, 0.0};
45float dimYellow[3] = {0.5, 0.5, 0.0};
46float green[3] = {0.0, 1.0, 0.0};
47float dimGreen[3] = {0.0, 0.5, 0.0};
48float red[3] = {1.0, 0.0, 0.0};
49
50float* bgColor = black;
51
52const char *kNoResourceError = B_TRANSLATE("The Teapot 3D model was "
53									"not found in application resources. "
54									"Please repair the program installation.");
55
56struct light {
57	float *ambient;
58	float *diffuse;
59	float *specular;
60};
61
62
63light 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
74long
75signalEvent(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
86long
87setEvent(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
98long
99waitEvent(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
112static int32
113simonThread(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
133ObjectView::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
187ObjectView::~ObjectView()
188{
189	delete_sem(quittingSem);
190	delete_sem(drawEvent);
191}
192
193
194void
195ObjectView::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
263void
264ObjectView::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
286void
287ObjectView::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
302void
303ObjectView::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
403int
404ObjectView::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
435void
436ObjectView::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
469void
470ObjectView::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
500void
501ObjectView::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
549void
550ObjectView::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
591bool
592ObjectView::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
629void
630ObjectView::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
670bool
671ObjectView::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
759void
760ObjectView::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