2 * Copyright 2008 Haiku Inc. All rights reserved.
3 * Distributed under the terms of the MIT License.
11 * Original Be Sample source modified to use a quaternion for the object's orientation
15 Copyright 1999, Be Incorporated. All Rights Reserved.
16 This file may be used under the terms of the Be Sample Code License.
19 #include "ObjectView.h"
21 #include <Application.h>
24 #include <InterfaceKit.h>
25 #include <FindDirectory.h>
29 #include "ResScroll.h"
31 #undef B_TRANSLATION_CONTEXT
32 #define B_TRANSLATION_CONTEXT "ObjectView"
34 float displayScale
= 1.0;
35 float depthOfView
= 30.0;
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};
50 float* bgColor
= black
;
52 const char *kNoResourceError
= B_TRANSLATE("The Teapot 3D model was "
53 "not found in application resources. "
54 "Please repair the program installation.");
65 {dimWhite
, white
, white
},
66 {dimWhite
, yellow
, yellow
},
68 {dimWhite
, blue
, blue
},
69 {dimWhite
, green
, green
}
75 signalEvent(sem_id event
)
78 get_sem_count(event
,&c
);
80 release_sem_etc(event
,-c
,0);
87 setEvent(sem_id event
)
90 get_sem_count(event
,&c
);
92 release_sem_etc(event
,-c
,0);
99 waitEvent(sem_id event
)
104 get_sem_count(event
,&c
);
106 acquire_sem_etc(event
,c
,0,0);
113 simonThread(void* cookie
)
115 ObjectView
* objectView
= reinterpret_cast<ObjectView
*>(cookie
);
116 BScreen
screen(objectView
->Window());
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
);
125 release_sem(objectView
->quittingSem
);
127 waitEvent(objectView
->drawEvent
);
129 screen
.WaitForRetrace();
135 ObjectView::ObjectView(BRect rect
, const char *name
, ulong resizingMode
,
137 : BGLView(rect
, name
, resizingMode
, 0, options
),
153 fLastTextured(false),
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
;
169 fLastObjectDistance
= fObjectDistance
= depthOfView
/ 8;
170 quittingSem
= create_sem(1, "quitting sem");
171 drawEvent
= create_sem(0, "draw event");
173 TriangleObject
*Tri
= new TriangleObject(this);
174 if (Tri
->InitCheck() == B_OK
) {
176 fObjects
.AddItem(Tri
);
177 fObjListLock
.Unlock();
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();
189 ObjectView::~ObjectView()
191 delete_sem(quittingSem
);
192 delete_sem(drawEvent
);
197 ObjectView::AttachedToWindow()
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();
209 BGLView::AttachedToWindow();
210 Window()->SetPulseRate(100000);
215 glEnable(GL_CULL_FACE
);
217 glDepthFunc(GL_LESS
);
219 glShadeModel(GL_SMOOTH
);
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
);
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
);
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
);
236 glEnable(GL_LIGHTING
);
237 glEnable(GL_AUTO_NORMAL
);
238 glEnable(GL_NORMALIZE
);
240 glMaterialf(GL_FRONT
, GL_SHININESS
, 0.6 * 128.0);
242 glClearColor(bgColor
[0], bgColor
[1], bgColor
[2], 1.0);
243 glColor3f(1.0, 1.0, 1.0);
245 glViewport(0, 0, (GLint
)bounds
.IntegerWidth() + 1,
246 (GLint
)bounds
.IntegerHeight() + 1);
247 glMatrixMode(GL_PROJECTION
);
250 float scale
= displayScale
;
251 glOrtho(-scale
, scale
, -scale
, scale
, -scale
* depthOfView
,
252 scale
* depthOfView
);
253 glMatrixMode(GL_MODELVIEW
);
258 fDrawThread
= spawn_thread(simonThread
, "Simon", B_NORMAL_PRIORITY
, this);
259 resume_thread(fDrawThread
);
266 ObjectView::DetachedFromWindow()
268 BGLView::DetachedFromWindow();
273 while (Window()->IsLocked()) {
278 acquire_sem(quittingSem
);
279 release_sem(drawEvent
);
280 wait_for_thread(fDrawThread
, &dummy
);
281 release_sem(quittingSem
);
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
);
305 ObjectView::MessageReceived(BMessage
* msg
)
307 BMenuItem
* item
= NULL
;
308 bool toggleItem
= false;
312 fFps
= (fFps
) ? false : true;
313 msg
->FindPointer("source", reinterpret_cast<void**>(&item
));
314 item
->SetMarked(fFps
);
320 TriangleObject
*Tri
= new TriangleObject(this);
321 if (Tri
->InitCheck() == B_OK
) {
323 fObjects
.AddItem(Tri
);
324 fObjListLock
.Unlock();
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();
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
++) {
346 menu
->ItemAt(i
)->SetMarked(false);
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
);
359 glDisable(GL_LIGHT0
+ lightNum
- 1);
367 fGouraud
= !fGouraud
;
375 fCulling
= !fCulling
;
379 fLighting
= !fLighting
;
386 case kMsgPerspective
:
396 if (toggleItem
&& msg
->FindPointer("source", reinterpret_cast<void**>(&item
)) == B_OK
){
397 item
->SetMarked(!item
->IsMarked());
401 BGLView::MessageReceived(msg
);
406 ObjectView::ObjectAtPoint(const BPoint
&point
)
409 glShadeModel(GL_FLAT
);
410 glDisable(GL_LIGHTING
);
412 glClearColor(black
[0], black
[1], black
[2], 1.0);
413 glClear(GL_COLOR_BUFFER_BIT
| (fZbuf
? GL_DEPTH_BUFFER_BIT
: 0));
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
);
423 glReadBuffer(GL_BACK
);
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;
438 ObjectView::MouseDown(BPoint point
)
440 GLObject
* object
= NULL
;
442 BMessage
*msg
= Window()->CurrentMessage();
443 uint32 buttons
= msg
->FindInt32("buttons");
444 object
= reinterpret_cast<GLObject
*>(fObjects
.ItemAt(ObjectAtPoint(point
)));
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
);
458 SetMouseEventMask(B_POINTER_EVENTS
,
459 B_LOCK_WINDOW_FOCUS
| B_NO_POINTER_HISTORY
);
461 BCursor
grabbingCursor(B_CURSOR_ID_GRABBING
);
462 SetViewCursor(&grabbingCursor
);
464 ConvertToScreen(&point
);
465 object
->MenuInvoked(point
);
472 ObjectView::MouseUp(BPoint point
)
474 if (fTrackingInfo
.isTracking
) {
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
) ) {
482 fTrackingInfo
.pickedObject
->Spin(0.5f
* fTrackingInfo
.lastDy
, 0.5f
* fTrackingInfo
.lastDx
);
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
;
496 BCursor
grabCursor(B_CURSOR_ID_GRAB
);
497 SetViewCursor(&grabCursor
);
503 ObjectView::MouseMoved(BPoint point
, uint32 transit
, const BMessage
*msg
)
505 if (fTrackingInfo
.isTracking
&& fTrackingInfo
.pickedObject
!= NULL
) {
507 float dx
= point
.x
- fTrackingInfo
.lastX
;
508 float dy
= point
.y
- fTrackingInfo
.lastY
;
509 fTrackingInfo
.lastX
= point
.x
;
510 fTrackingInfo
.lastY
= point
.y
;
512 if (fTrackingInfo
.buttons
== B_PRIMARY_MOUSE_BUTTON
) {
514 fTrackingInfo
.pickedObject
->Spin(0.0f
, 0.0f
);
515 fTrackingInfo
.pickedObject
->RotateWorldSpace(dx
,dy
);
516 fTrackingInfo
.lastDx
= dx
;
517 fTrackingInfo
.lastDy
= dy
;
521 } else if (fTrackingInfo
.buttons
== B_SECONDARY_MOUSE_BUTTON
) {
523 float xinc
= (dx
* 2 * displayScale
/ Bounds().Width());
524 float yinc
= (-dy
* 2 * displayScale
/ Bounds().Height());
528 zinc
= yinc
* (fTrackingInfo
.pickedObject
->z
/ displayScale
);
529 xinc
*= -(fTrackingInfo
.pickedObject
->z
* 4 / zRatio
);
530 yinc
*= -(fTrackingInfo
.pickedObject
->z
* 4 / zRatio
);
533 fTrackingInfo
.pickedObject
->x
+= xinc
;
534 if (modifiers() & B_SHIFT_KEY
)
535 fTrackingInfo
.pickedObject
->z
+= zinc
;
537 fTrackingInfo
.pickedObject
->y
+= yinc
;
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
);
552 ObjectView::FrameResized(float width
, float height
)
554 BGLView::FrameResized(width
, height
);
558 width
= Bounds().Width();
559 height
= Bounds().Height();
560 fYxRatio
= height
/ width
;
561 glViewport(0, 0, (GLint
)width
+ 1, (GLint
)height
+ 1);
563 // To prevent weird buffer contents
564 glClear(GL_COLOR_BUFFER_BIT
);
566 glMatrixMode(GL_PROJECTION
);
568 float scale
= displayScale
;
571 gluPerspective(60, 1.0 / fYxRatio
, 0.15, 120);
574 glOrtho(-scale
/ fYxRatio
, scale
/ fYxRatio
, -scale
, scale
, -1.0,
577 glOrtho(-scale
, scale
, -scale
* fYxRatio
, scale
* fYxRatio
, -1.0,
582 fLastYXRatio
= fYxRatio
;
584 glMatrixMode(GL_MODELVIEW
);
594 ObjectView::RepositionView()
596 if (!(fPersp
!= fLastPersp
) &&
597 !(fLastObjectDistance
!= fObjectDistance
) &&
598 !(fLastYXRatio
!= fYxRatio
)) {
604 glMatrixMode(GL_PROJECTION
);
606 float scale
= displayScale
;
609 gluPerspective(60, 1.0 / fYxRatio
, 0.15, 120);
612 glOrtho(-scale
/ fYxRatio
, scale
/ fYxRatio
, -scale
, scale
, -1.0,
615 glOrtho(-scale
, scale
, -scale
* fYxRatio
, scale
* fYxRatio
, -1.0,
620 glMatrixMode(GL_MODELVIEW
);
624 fLastObjectDistance
= fObjectDistance
;
626 fLastYXRatio
= fYxRatio
;
632 ObjectView::EnforceState()
634 glShadeModel(fGouraud
? GL_SMOOTH
: GL_FLAT
);
637 glEnable(GL_DEPTH_TEST
);
639 glDisable(GL_DEPTH_TEST
);
642 glEnable(GL_CULL_FACE
);
644 glDisable(GL_CULL_FACE
);
647 glEnable(GL_LIGHTING
);
649 glDisable(GL_LIGHTING
);
652 glPolygonMode(GL_FRONT_AND_BACK
, GL_FILL
);
654 glPolygonMode(GL_FRONT_AND_BACK
, GL_LINE
);
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
);
663 glClearColor(bgColor
[0], bgColor
[1], bgColor
[2], 1.0);
667 glClearColor(bgColor
[0], bgColor
[1], bgColor
[2], 1.0);
675 bool changed
= false;
677 if (fGouraud
!= fLastGouraud
) {
679 glShadeModel(fGouraud
? GL_SMOOTH
: GL_FLAT
);
681 fLastGouraud
= fGouraud
;
685 if (fZbuf
!= fLastZbuf
) {
688 glEnable(GL_DEPTH_TEST
);
690 glDisable(GL_DEPTH_TEST
);
696 if (fCulling
!= fLastCulling
) {
699 glEnable(GL_CULL_FACE
);
701 glDisable(GL_CULL_FACE
);
703 fLastCulling
= fCulling
;
707 if (fLighting
!= fLastLighting
) {
710 glEnable(GL_LIGHTING
);
712 glDisable(GL_LIGHTING
);
714 fLastLighting
= fLighting
;
718 if (fFilled
!= fLastFilled
) {
721 glPolygonMode(GL_FRONT_AND_BACK
, GL_FILL
);
723 glPolygonMode(GL_FRONT_AND_BACK
, GL_LINE
);
726 fLastFilled
= fFilled
;
730 if (fFog
!= fLastFog
) {
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
);
738 glClearColor(bgColor
[0], bgColor
[1], bgColor
[2], 1.0);
742 glClearColor(bgColor
[0], bgColor
[1], bgColor
[2], 1.0);
748 changed
= changed
|| RepositionView();
749 changed
= changed
|| fForceRedraw
;
750 fForceRedraw
= false;
752 for (int i
= 0; i
< fObjects
.CountItems(); i
++) {
753 bool hack
= reinterpret_cast<GLObject
*>(fObjects
.ItemAt(i
))->SpinIt();
754 changed
= changed
|| hack
;
762 ObjectView::DrawFrame(bool noPause
)
765 glClear(GL_COLOR_BUFFER_BIT
| (fZbuf
? GL_DEPTH_BUFFER_BIT
: 0));
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
);
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
);
779 fObjListLock
.Unlock();
782 glDepthMask(GL_TRUE
);
785 uint64 now
= system_time();
786 float fps
= 1.0 / ((now
- fLastFrame
) / 1000000.0);
789 if (fHistEntries
< HISTSIZE
) {
790 entry
= (fOldestEntry
+ fHistEntries
) % HISTSIZE
;
793 entry
= fOldestEntry
;
794 fOldestEntry
= (fOldestEntry
+ 1) % HISTSIZE
;
797 fFpsHistory
[entry
] = fps
;
799 if (fHistEntries
> 5) {
801 for (int i
= 0; i
< fHistEntries
; i
++)
802 fps
+= fFpsHistory
[(fOldestEntry
+ i
) % HISTSIZE
];
807 glPushAttrib(GL_ENABLE_BIT
| GL_LIGHTING_BIT
);
810 glTranslatef(-0.9, -0.9, 0);
811 glScalef(0.10, 0.10, 0.10);
812 glDisable(GL_LIGHTING
);
813 glDisable(GL_DEPTH_TEST
);
815 glColor3f(1.0, 1.0, 0);
816 glMatrixMode(GL_PROJECTION
);
819 glMatrixMode(GL_MODELVIEW
);
821 FPS::drawCounter(fps
);
823 glMatrixMode(GL_PROJECTION
);
825 glMatrixMode(GL_MODELVIEW
);