xref: /haiku/src/apps/glteapot/ObjectView.cpp (revision 1e36cfc2721ef13a187c6f7354dc9cbc485e89d3)
1 /*
2 	Copyright 1999, Be Incorporated.   All Rights Reserved.
3 	This file may be used under the terms of the Be Sample Code License.
4 */
5 
6 #include <stdio.h>
7 #include <stdlib.h>
8 
9 #include <InterfaceKit.h>
10 #include <FindDirectory.h>
11 
12 #include "ObjectView.h"
13 #include "ResScroll.h"
14 #include "GLObject.h"
15 #include "FPS.h"
16 
17 #define teapotData "teapot.data"
18 char teapotPath[PATH_MAX];
19 
20 float displayScale = 1.0;
21 float depthOfView = 30.0;
22 float zRatio = 10.0;
23 
24 float white[3] = {1.0,1.0,1.0};
25 float dimWhite[3] = {0.25,0.25,0.25};
26 float black[3] = {0.0,0.0,0.0};
27 float foggy[3] = {0.4,0.4,0.4};
28 float blue[3] = {0.0,0.0,1.0};
29 float dimBlue[3] = {0.0,0.0,0.5};
30 float yellow[3] = {1.0,1.0,0.0};
31 float dimYellow[3] = {0.5,0.5,0.0};
32 float green[3] = {0.0,1.0,0.0};
33 float dimGreen[3] = {0.0,0.5,0.0};
34 float red[3] = {1.0,0.0,0.0};
35 
36 float *bgColor = black;
37 
38 struct light {
39 	float *ambient;
40 	float *diffuse;
41 	float *specular;
42 };
43 
44 light lights[] = {
45 	{NULL,NULL,NULL},
46 	{dimWhite,white,white},
47 	{dimWhite,yellow,yellow},
48 	{dimWhite,red,red},
49 	{dimWhite,blue,blue},
50 	{dimWhite,green,green}
51 };
52 
53 long signalEvent(sem_id event)
54 {
55 	long c;
56 	get_sem_count(event,&c);
57 	if (c<0)
58 		release_sem_etc(event,-c,0);
59 
60 	return 0;
61 };
62 
63 long setEvent(sem_id event)
64 {
65 	long c;
66 	get_sem_count(event,&c);
67 	if (c<0)
68 	  release_sem_etc(event,-c,0);
69 
70 	return 0;
71 };
72 
73 
74 long waitEvent(sem_id event)
75 {
76 	acquire_sem(event);
77 
78 	long c;
79 	get_sem_count(event,&c);
80 	if (c>0)
81 		acquire_sem_etc(event,c,0,0);
82 
83 	return 0;
84 };
85 
86 long simonThread(ObjectView *ov)
87 {
88     int noPause=0;
89     while (acquire_sem_etc(ov->quittingSem,1,B_TIMEOUT,0) == B_NO_ERROR) {
90 		if (ov->SpinIt()) {
91 			ov->DrawFrame(noPause);
92 			release_sem(ov->quittingSem);
93 			noPause = 1;
94 		} else {
95 			release_sem(ov->quittingSem);
96 			noPause = 0;
97 			waitEvent(ov->drawEvent);
98 		};
99 	};
100 
101 	return 0;
102 };
103 
104 ObjectView::ObjectView(BRect r, char *name, ulong resizingMode, ulong options)
105 	: BGLView(r,name,resizingMode,0,options)
106 {
107     histEntries = 0;
108 	oldestEntry = 0;
109     lastGouraud = gouraud = true;
110 	lastZbuf = zbuf = true;
111 	lastCulling = culling = true;
112 	lastLighting = lighting = true;
113 	lastFilled = filled = true;
114 	lastPersp = persp = false;
115 	lastFog = fog = false;
116 	lastTextured = textured = false;
117 	lastObjectDistance = objectDistance = depthOfView/8;
118 	fps = true;
119 	forceRedraw = false;
120 	lastYXRatio = yxRatio = 1;
121 
122 	quittingSem = create_sem(1,"quitting sem");
123 	drawEvent = create_sem(0,"draw event");
124 
125 	char findDir[PATH_MAX];
126 	find_directory(B_BEOS_ETC_DIRECTORY,-1, TRUE,findDir,PATH_MAX);
127 	sprintf(teapotPath,"%s/%s",findDir,teapotData);
128 	objListLock.Lock();
129 	objects.AddItem(new TriangleObject(this,teapotPath));
130 	objListLock.Unlock();
131 };
132 
133 ObjectView::~ObjectView()
134 {
135 	delete_sem(quittingSem);
136 	delete_sem(drawEvent);
137 };
138 
139 void ObjectView::AttachedToWindow()
140 {
141 	float position[] = {0.0, 3.0, 3.0, 0.0};
142 	float position1[] = {-3.0, -3.0, 3.0, 0.0};
143 	float position2[] = {3.0, 0.0, 0.0, 0.0};
144 	float local_view[] = {0.0,0.0};
145 //	float ambient[] = {0.1745, 0.03175, 0.03175};
146 //	float diffuse[] = {0.61424, 0.10136, 0.10136};
147 //	float specular[] = {0.727811, 0.626959, 0.626959};
148 //	rgb_color black = {0,0,0,255};
149 	BRect r = Bounds();
150 
151 	BGLView::AttachedToWindow();
152 	Window()->SetPulseRate(100000);
153 
154 	LockGL();
155 
156 	glEnable(GL_DITHER);
157 	glEnable(GL_CULL_FACE);
158 	glCullFace(GL_BACK);
159 	glDepthFunc(GL_LESS);
160 
161 	glShadeModel(GL_SMOOTH);
162 
163 	glLightfv(GL_LIGHT0, GL_POSITION, position);
164 	glLightfv(GL_LIGHT0+1, GL_POSITION, position1);
165 	glLightfv(GL_LIGHT0+2, GL_POSITION, position2);
166 	glLightModelfv(GL_LIGHT_MODEL_LOCAL_VIEWER, local_view);
167 
168 	glEnable(GL_LIGHT0);
169 	glLightfv(GL_LIGHT0, GL_SPECULAR, lights[lightWhite].specular);
170 	glLightfv(GL_LIGHT0, GL_DIFFUSE,lights[lightWhite].diffuse);
171 	glLightfv(GL_LIGHT0, GL_AMBIENT,lights[lightWhite].ambient);
172 	glEnable(GL_LIGHT1);
173 	glLightfv(GL_LIGHT1, GL_SPECULAR, lights[lightBlue].specular);
174 	glLightfv(GL_LIGHT1, GL_DIFFUSE,lights[lightBlue].diffuse);
175 	glLightfv(GL_LIGHT1, GL_AMBIENT,lights[lightBlue].ambient);
176 
177 	glFrontFace(GL_CW);
178 	glEnable(GL_LIGHTING);
179 	glEnable(GL_AUTO_NORMAL);
180 	glEnable(GL_NORMALIZE);
181 
182 	glMaterialf(GL_FRONT, GL_SHININESS, 0.6*128.0);
183 
184 	glClearColor(bgColor[0],bgColor[1],bgColor[2], 1.0);
185 	glColor3f(1.0, 1.0, 1.0);
186 
187 	glViewport(0, 0, (GLint)r.IntegerWidth()+1, (GLint)r.IntegerHeight()+1);
188 	glMatrixMode(GL_PROJECTION);
189 	glLoadIdentity();
190 	float scale=displayScale;
191 	//    glOrtho (0.0, 16.0, 0, 16.0*(GLfloat)300/(GLfloat)300,
192 	//			         -10.0, 10.0);
193 	glOrtho(-scale, scale, -scale, scale, -scale*depthOfView,
194 			scale*depthOfView);
195 	glMatrixMode(GL_MODELVIEW);
196 	glLoadIdentity();
197 
198 	UnlockGL();
199 
200 	drawThread = spawn_thread((thread_entry)simonThread,
201 		"Simon",B_NORMAL_PRIORITY,(void*)this);
202 	resume_thread(drawThread);
203 	forceRedraw = true;
204 	setEvent(drawEvent);
205 };
206 
207 void ObjectView::DetachedFromWindow()
208 {
209 	BGLView::DetachedFromWindow();
210 
211 	long dummy;
212 	long locks=0;
213 
214 	while (Window()->IsLocked()) {
215 		locks++;
216 		Window()->Unlock();
217 	};
218 
219 	acquire_sem(quittingSem);
220 	release_sem(drawEvent);
221 	wait_for_thread(drawThread,&dummy);
222 	release_sem(quittingSem);
223 
224 	while (locks--) Window()->Lock();
225 };
226 
227 void ObjectView::Pulse()
228 {
229   Window()->Lock();
230   BRect p = Parent()->Bounds();
231   BRect b = Bounds();
232   p.OffsetTo(0,0);
233   b.OffsetTo(0,0);
234   if (b != p) {
235 	ResizeTo(p.right-p.left,p.bottom-p.top);
236   };
237   Window()->Unlock();
238 };
239 
240 void ObjectView::MessageReceived(BMessage *msg)
241 {
242 	BMenuItem *i;
243 	bool *b;
244 
245 	switch (msg->what) {
246    	    case bmsgFPS:
247 		    fps = (fps)?false:true;
248 			msg->FindPointer("source", (void **)&i);
249 			i->SetMarked(fps);
250 			forceRedraw = true;
251 			setEvent(drawEvent);
252 			break;
253 	    case bmsgAddModel:
254 		    objListLock.Lock();
255 			objects.AddItem(new TriangleObject(this,teapotPath));
256 			objListLock.Unlock();
257 			setEvent(drawEvent);
258 			break;
259 		case bmsgLights:
260 		{
261 			msg->FindPointer("source",(void **)&i);
262 			long lightNum = msg->FindInt32("num");
263 			long color = msg->FindInt32("color");
264 			BMenu *m = i->Menu();
265 			long index = m->IndexOf(i);
266 			m->ItemAt(index)->SetMarked(true);
267 			for (int i=0;i<m->CountItems();i++) {
268 				if (i != index)
269 					m->ItemAt(i)->SetMarked(false);
270 			};
271 
272 			LockGL();
273 			if (color != lightNone) {
274 				glEnable(GL_LIGHT0+lightNum-1);
275 				glLightfv(GL_LIGHT0+lightNum-1, GL_SPECULAR, lights[color].specular);
276 				glLightfv(GL_LIGHT0+lightNum-1, GL_DIFFUSE,lights[color].diffuse);
277 				glLightfv(GL_LIGHT0+lightNum-1, GL_AMBIENT,lights[color].ambient);
278 			} else {
279 				glDisable(GL_LIGHT0+lightNum-1);
280 			};
281 			UnlockGL();
282 			forceRedraw = true;
283 			setEvent(drawEvent);
284 			break;
285 		}
286 		case bmsgGouraud:
287 			b = &gouraud;
288 			goto stateChange;
289 		case bmsgZBuffer:
290 			b = &zbuf;
291 			goto stateChange;
292 		case bmsgCulling:
293 			b = &culling;
294 			goto stateChange;
295 		case bmsgLighting:
296 			b = &lighting;
297 			goto stateChange;
298 		case bmsgFilled:
299 			b = &filled;
300 			goto stateChange;
301 		case bmsgPerspective:
302 			b = &persp;
303 			goto stateChange;
304 		case bmsgFog:
305 			b = &fog;
306 			goto stateChange;
307 		stateChange:
308 			msg->FindPointer("source",(void **)&i);
309 			if (!i) return;
310 
311 			if (*b) {
312 				i->SetMarked(*b = false);
313 			} else {
314 				i->SetMarked(*b = true);
315 			};
316 			setEvent(drawEvent);
317 			break;
318 		default:
319 			BGLView::MessageReceived(msg);
320 	};
321 };
322 
323 int ObjectView::ObjectAtPoint(BPoint p)
324 {
325 	LockGL();
326 	glShadeModel(GL_FLAT);
327 	glDisable(GL_LIGHTING);
328 	glDisable(GL_FOG);
329 	glClearColor(black[0],black[1],black[2],1.0);
330 	glClear(GL_COLOR_BUFFER_BIT|(zbuf?GL_DEPTH_BUFFER_BIT:0));
331 
332 	float f[3];
333 	f[1] = f[2] = 0;
334 	for (int i=0;i<objects.CountItems();i++) {
335 		f[0] = (255-i)/255.0;
336 		((GLObject*)objects.ItemAt(i))->Draw(true, f);
337 	};
338 	glReadBuffer(GL_BACK);
339 	uchar pixel[256];
340 	glReadPixels((GLint)p.x,(GLint)(Bounds().bottom-p.y),1,1,GL_RGB,GL_UNSIGNED_BYTE,pixel);
341 	int objNum = pixel[0];
342 	objNum = 255-objNum;
343 
344 	EnforceState();
345 	UnlockGL();
346 
347 	return objNum;
348 };
349 
350 void ObjectView::MouseDown(BPoint p)
351 {
352 	BPoint op=p,np=p;
353 	BRect bounds = Bounds();
354 	float lastDx=0,lastDy=0;
355 	GLObject *o = NULL;
356 	uint32 mods;
357 
358 	BMessage *m = Window()->CurrentMessage();
359 	uint32 buttons = m->FindInt32("buttons");
360 	o = ((GLObject*)objects.ItemAt(ObjectAtPoint(p)));
361 
362 	long locks=0;
363 
364 	while (Window()->IsLocked()) {
365 		locks++;
366 		Window()->Unlock();
367 	};
368 
369 	if (buttons == B_SECONDARY_MOUSE_BUTTON) {
370 	  if (o) {
371 		while (buttons) {
372 		  lastDx = np.x-op.x;
373 		  lastDy = np.y-op.y;
374 		  if (lastDx || lastDy) {
375 			float xinc = (lastDx*2*displayScale/bounds.Width());
376 			float yinc = (-lastDy*2*displayScale/bounds.Height());
377 			float zinc = 0;
378 			if (persp) {
379 			  zinc = yinc*(o->z/(displayScale));
380 			  xinc *= -(o->z*4/zRatio);
381 			  yinc *= -(o->z*4/zRatio);
382 			};
383 			o->x += xinc;
384 			mods = modifiers();
385 			if (mods & B_SHIFT_KEY)
386 			  o->z += zinc;
387 			else
388 			  o->y += yinc;
389 			op = np;
390 			forceRedraw = true;
391 			setEvent(drawEvent);
392 		  };
393 
394 		  snooze(25000);
395 
396 		  Window()->Lock();
397 		  GetMouse(&np, &buttons, true);
398 		  Window()->Unlock();
399 		};
400 	  };
401 	} else if (buttons == B_PRIMARY_MOUSE_BUTTON) {
402 	  float llx=0,lly=0;
403 	  lastDx = lastDy = 0;
404 	  if (o) {
405 		o->spinX = 0;
406 		o->spinY = 0;
407 		while (buttons) {
408 		  llx = lastDx;
409 		  lly = lastDy;
410 		  lastDx = np.x-op.x;
411 		  lastDy = np.y-op.y;
412 		  if (lastDx || lastDy) {
413 			o->rotY += lastDx;
414 			o->rotX += lastDy;
415 			op = np;
416 			setEvent(drawEvent);
417 		  };
418 
419 		  snooze(25000);
420 
421 		  Window()->Lock();
422 		  GetMouse(&np, &buttons, true);
423 		  Window()->Unlock();
424 		};
425 		o->spinY = lastDx+llx;
426 		o->spinX = lastDy+lly;
427 	  };
428 	} else {
429 		if (o) {
430 			BPoint point = p;
431 			Window()->Lock();
432 			ConvertToScreen(&point);
433 			Window()->Unlock();
434 			o->MenuInvoked(point);
435 		};
436 	};
437 
438 	while (locks--)
439 		Window()->Lock();
440 
441 	setEvent(drawEvent);
442 };
443 
444 void ObjectView::FrameResized(float w, float h)
445 {
446 	LockGL();
447 
448 	BGLView::FrameResized(w,h);
449 
450 	BRect b = Bounds();
451 	w = b.Width();
452 	h = b.Height();
453 	yxRatio = h/w;
454     glViewport(0, 0, (GLint)w+1, (GLint)h+1);
455 
456 	// To prevent weird buffer contents
457 	glClear(GL_COLOR_BUFFER_BIT);
458 
459 	glMatrixMode(GL_PROJECTION);
460 	glLoadIdentity();
461 	float scale=displayScale;
462 	if (persp) {
463 	  gluPerspective(60,1.0/yxRatio,0.15,120);
464 	} else {
465 	  if (yxRatio < 1) {
466 		glOrtho(-scale/yxRatio, scale/yxRatio, -scale, scale,
467 				-1.0, depthOfView*4);
468 	  } else {
469 		glOrtho(-scale, scale, -scale*yxRatio, scale*yxRatio,
470 				-1.0, depthOfView*4);
471 	  };
472 	};
473 
474 	lastYXRatio = yxRatio;
475 
476 	glMatrixMode(GL_MODELVIEW);
477 
478 	UnlockGL();
479 
480 	forceRedraw = true;
481 	setEvent(drawEvent);
482 }
483 
484 bool ObjectView::RepositionView()
485 {
486 	if (!(persp != lastPersp) &&
487 		!(lastObjectDistance != objectDistance) &&
488 		!(lastYXRatio != yxRatio)) {
489 		return false;
490 	};
491 
492 	LockGL();
493 
494 	glMatrixMode(GL_PROJECTION);
495 	glLoadIdentity();
496 	float scale=displayScale;
497 	if (persp) {
498 	  gluPerspective(60,1.0/yxRatio,0.15,120);
499 	} else {
500 	  if (yxRatio < 1) {
501 		glOrtho(-scale/yxRatio, scale/yxRatio, -scale, scale,
502 				-1.0, depthOfView*4);
503 	  } else {
504 		glOrtho(-scale, scale, -scale*yxRatio, scale*yxRatio,
505 				-1.0, depthOfView*4);
506 	  };
507 	};
508 
509 	glMatrixMode(GL_MODELVIEW);
510 
511 	UnlockGL();
512 
513 	lastObjectDistance = objectDistance;
514 	lastPersp = persp;
515 	lastYXRatio = yxRatio;
516 	return true;
517 };
518 
519 void ObjectView::EnforceState()
520 {
521 	glShadeModel(gouraud?GL_SMOOTH:GL_FLAT);
522 	if (zbuf) glEnable(GL_DEPTH_TEST); else glDisable(GL_DEPTH_TEST);
523 	if (culling) glEnable(GL_CULL_FACE); else glDisable(GL_CULL_FACE);
524 	if (lighting) glEnable(GL_LIGHTING); else glDisable(GL_LIGHTING);
525 	if (filled) {
526 		glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);
527 	} else {
528 		glPolygonMode(GL_FRONT_AND_BACK,GL_LINE);
529 	};
530 	if (fog) {
531 		glFogf(GL_FOG_START,10.0);
532 		glFogf(GL_FOG_DENSITY,0.2);
533 		glFogf(GL_FOG_END,depthOfView);
534 		glFogfv(GL_FOG_COLOR,foggy);
535 		glEnable(GL_FOG);
536 		bgColor = foggy;
537 		glClearColor(bgColor[0],bgColor[1],bgColor[2], 1.0);
538 	} else {
539 		glDisable(GL_FOG);
540 		bgColor = black;
541 		glClearColor(bgColor[0],bgColor[1],bgColor[2], 1.0);
542 	};
543 };
544 
545 bool ObjectView::SpinIt()
546 {
547 	bool changed = false;
548 
549 	if (gouraud != lastGouraud) {
550 		LockGL();
551 		glShadeModel(gouraud?GL_SMOOTH:GL_FLAT);
552 		UnlockGL();
553 		lastGouraud = gouraud;
554 		changed = true;
555 	};
556 	if (zbuf != lastZbuf) {
557 		LockGL();
558 		if (zbuf) glEnable(GL_DEPTH_TEST); else glDisable(GL_DEPTH_TEST);
559 		UnlockGL();
560 		lastZbuf = zbuf;
561 		changed = true;
562 	};
563 	if (culling != lastCulling) {
564 		LockGL();
565 		if (culling) glEnable(GL_CULL_FACE); else glDisable(GL_CULL_FACE);
566 		UnlockGL();
567 		lastCulling = culling;
568 		changed = true;
569 	};
570 	if (lighting != lastLighting) {
571 		LockGL();
572 		if (lighting) glEnable(GL_LIGHTING); else glDisable(GL_LIGHTING);
573 		UnlockGL();
574 		lastLighting = lighting;
575 		changed = true;
576 	};
577 	if (filled != lastFilled) {
578 		LockGL();
579 		if (filled) {
580 			glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);
581 		} else {
582 			glPolygonMode(GL_FRONT_AND_BACK,GL_LINE);
583 		};
584 		UnlockGL();
585 		lastFilled = filled;
586 		changed = true;
587 	};
588 	if (fog != lastFog) {
589 		if (fog) {
590 			glFogf(GL_FOG_START,1.0);
591 			glFogf(GL_FOG_DENSITY,0.2);
592 			glFogf(GL_FOG_END,depthOfView);
593 			glFogfv(GL_FOG_COLOR,foggy);
594 			glEnable(GL_FOG);
595 			bgColor = foggy;
596 			glClearColor(bgColor[0],bgColor[1],bgColor[2], 1.0);
597 		} else {
598 			glDisable(GL_FOG);
599 			bgColor = black;
600 			glClearColor(bgColor[0],bgColor[1],bgColor[2], 1.0);
601 		};
602 		lastFog = fog;
603 		changed = true;
604 	};
605 
606 	changed = changed || RepositionView();
607 	changed = changed || forceRedraw;
608 	forceRedraw = false;
609 
610 	for (int i=0;i<objects.CountItems();i++) {
611 		bool hack = ((GLObject*)objects.ItemAt(i))->SpinIt();
612 		changed = changed || hack;
613 	};
614 
615 	return changed;
616 };
617 
618 void ObjectView::DrawFrame(bool noPause)
619 {
620 	LockGL();
621 	glClear(GL_COLOR_BUFFER_BIT|(zbuf?GL_DEPTH_BUFFER_BIT:0));
622 
623 	objListLock.Lock();
624 	for (int i=0;i<objects.CountItems();i++) {
625 	  GLObject *o = ((GLObject*)objects.ItemAt(i));
626 		if (o->solidity==0)
627 			o->Draw(false, NULL);
628 	};
629 	EnforceState();
630 	for (int i=0;i<objects.CountItems();i++) {
631 		GLObject *o = ((GLObject*)objects.ItemAt(i));
632 		if (o->solidity!=0)
633 			o->Draw(false, NULL);
634 	};
635 	objListLock.Unlock();
636 
637 	glDisable(GL_BLEND);
638 	glDepthMask(GL_TRUE);
639 
640 	if (noPause) {
641 	  uint64 now = system_time();
642 	  float f = 1.0/((now - lastFrame)/1000000.0);
643 	  lastFrame = now;
644 	  int e;
645 	  if (histEntries < HISTSIZE) {
646 		e = (oldestEntry + histEntries)%HISTSIZE;
647 		histEntries++;
648 	  } else {
649 		e = oldestEntry;
650 		oldestEntry = (oldestEntry+1)%HISTSIZE;
651 	  };
652 
653 	  fpsHistory[e] = f;
654 
655 	  if (histEntries > 5) {
656 		  f = 0;
657 		  for (int i=0;i<histEntries;i++)
658 			f += fpsHistory[(oldestEntry+i)%HISTSIZE];
659 		  f = f/histEntries;
660 		  if (fps) {
661 
662 			glPushAttrib( GL_ENABLE_BIT | GL_LIGHTING_BIT );
663 			glPushMatrix();
664 			glLoadIdentity();
665 			glTranslatef( -0.9, -0.9, 0 );
666 			glScalef( 0.10, 0.10, 0.10 );
667 			glDisable( GL_LIGHTING );
668 			glDisable( GL_DEPTH_TEST );
669 			glDisable( GL_BLEND );
670 			glColor3f( 1.0, 1.0, 0 );
671 
672 			glMatrixMode(GL_PROJECTION);
673 			glPushMatrix();
674 			glLoadIdentity();
675 			glMatrixMode( GL_MODELVIEW );
676 
677 			FPS::drawCounter( f );
678 
679 			glMatrixMode(GL_PROJECTION);
680 			glPopMatrix();
681 			glMatrixMode( GL_MODELVIEW );
682 			glPopMatrix();
683 			glPopAttrib();
684 		  };
685 	  };
686 	} else {
687 	  histEntries = 0;
688 	  oldestEntry = 0;
689 	};
690 	SwapBuffers();
691 	UnlockGL();
692 };
693