/* * Copyright 2008 Haiku Inc. All rights reserved. * Distributed under the terms of the MIT License. * * Authors: * Alexandre Deckner * */ /* * Original Be Sample source modified to use a quaternion for the object's orientation */ /* Copyright 1999, Be Incorporated. All Rights Reserved. This file may be used under the terms of the Be Sample Code License. */ #include "ObjectView.h" #include #include #include #include #include #include "FPS.h" #include "GLObject.h" #include "ResScroll.h" #undef B_TRANSLATION_CONTEXT #define B_TRANSLATION_CONTEXT "ObjectView" float displayScale = 1.0; float depthOfView = 30.0; float zRatio = 10.0; float white[3] = {1.0, 1.0, 1.0}; float dimWhite[3] = {0.25, 0.25, 0.25}; float black[3] = {0.0, 0.0, 0.0}; float foggy[3] = {0.4, 0.4, 0.4}; float blue[3] = {0.0, 0.0, 1.0}; float dimBlue[3] = {0.0, 0.0, 0.5}; float yellow[3] = {1.0, 1.0, 0.0}; float dimYellow[3] = {0.5, 0.5, 0.0}; float green[3] = {0.0, 1.0, 0.0}; float dimGreen[3] = {0.0, 0.5, 0.0}; float red[3] = {1.0, 0.0, 0.0}; float* bgColor = black; const char *kNoResourceError = B_TRANSLATE("The Teapot 3D model was " "not found in application resources. " "Please repair the program installation."); struct light { float *ambient; float *diffuse; float *specular; }; light lights[] = { {NULL, NULL, NULL}, {dimWhite, white, white}, {dimWhite, yellow, yellow}, {dimWhite, red, red}, {dimWhite, blue, blue}, {dimWhite, green, green} }; long signalEvent(sem_id event) { int32 c; get_sem_count(event,&c); if (c < 0) release_sem_etc(event,-c,0); return 0; } long setEvent(sem_id event) { int32 c; get_sem_count(event,&c); if (c < 0) release_sem_etc(event,-c,0); return 0; } long waitEvent(sem_id event) { acquire_sem(event); int32 c; get_sem_count(event,&c); if (c > 0) acquire_sem_etc(event,c,0,0); return 0; } static int32 simonThread(void* cookie) { ObjectView* objectView = reinterpret_cast(cookie); BScreen screen(objectView->Window()); int noPause = 0; while (acquire_sem_etc(objectView->quittingSem, 1, B_TIMEOUT, 0) == B_NO_ERROR) { if (objectView->SpinIt()) { objectView->DrawFrame(noPause); release_sem(objectView->quittingSem); noPause = 1; } else { release_sem(objectView->quittingSem); noPause = 0; waitEvent(objectView->drawEvent); } screen.WaitForRetrace(); } return 0; } ObjectView::ObjectView(BRect rect, const char *name, ulong resizingMode, ulong options) : BGLView(rect, name, resizingMode, 0, options), fHistEntries(0), fOldestEntry(0), fFps(true), fLastGouraud(true), fGouraud(true), fLastZbuf(true), fZbuf(true), fLastCulling(true), fCulling(true), fLastLighting(true), fLighting(true), fLastFilled(true), fFilled(true), fLastPersp(false), fPersp(false), fLastTextured(false), fTextured(false), fLastFog(false), fFog(false), fForceRedraw(false), fLastYXRatio(1), fYxRatio(1) { fTrackingInfo.isTracking = false; fTrackingInfo.pickedObject = NULL; fTrackingInfo.buttons = 0; fTrackingInfo.lastX = 0.0f; fTrackingInfo.lastY = 0.0f; fTrackingInfo.lastDx = 0.0f; fTrackingInfo.lastDy = 0.0f; fLastObjectDistance = fObjectDistance = depthOfView / 8; quittingSem = create_sem(1, "quitting sem"); drawEvent = create_sem(0, "draw event"); TriangleObject *Tri = new TriangleObject(this); if (Tri->InitCheck() == B_OK) { fObjListLock.Lock(); fObjects.AddItem(Tri); fObjListLock.Unlock(); } else { BAlert *NoResourceAlert = new BAlert(B_TRANSLATE("Error"), kNoResourceError, B_TRANSLATE("OK"), NULL, NULL, B_WIDTH_AS_USUAL, B_OFFSET_SPACING, B_STOP_ALERT); NoResourceAlert->SetFlags(NoResourceAlert->Flags() | B_CLOSE_ON_ESCAPE); NoResourceAlert->Go(); delete Tri; } } ObjectView::~ObjectView() { delete_sem(quittingSem); delete_sem(drawEvent); } void ObjectView::AttachedToWindow() { float position[] = {0.0, 3.0, 3.0, 0.0}; float position1[] = {-3.0, -3.0, 3.0, 0.0}; float position2[] = {3.0, 0.0, 0.0, 0.0}; float local_view[] = {0.0, 0.0}; // float ambient[] = {0.1745, 0.03175, 0.03175}; // float diffuse[] = {0.61424, 0.10136, 0.10136}; // float specular[] = {0.727811, 0.626959, 0.626959}; // rgb_color black = {0, 0, 0, 255}; BRect bounds = Bounds(); BGLView::AttachedToWindow(); Window()->SetPulseRate(100000); LockGL(); glEnable(GL_DITHER); glEnable(GL_CULL_FACE); glCullFace(GL_BACK); glDepthFunc(GL_LESS); glShadeModel(GL_SMOOTH); glLightfv(GL_LIGHT0, GL_POSITION, position); glLightfv(GL_LIGHT0 + 1, GL_POSITION, position1); glLightfv(GL_LIGHT0 + 2, GL_POSITION, position2); glLightModelfv(GL_LIGHT_MODEL_LOCAL_VIEWER, local_view); glEnable(GL_LIGHT0); glLightfv(GL_LIGHT0, GL_SPECULAR, lights[lightWhite].specular); glLightfv(GL_LIGHT0, GL_DIFFUSE,lights[lightWhite].diffuse); glLightfv(GL_LIGHT0, GL_AMBIENT,lights[lightWhite].ambient); glEnable(GL_LIGHT1); glLightfv(GL_LIGHT1, GL_SPECULAR, lights[lightBlue].specular); glLightfv(GL_LIGHT1, GL_DIFFUSE,lights[lightBlue].diffuse); glLightfv(GL_LIGHT1, GL_AMBIENT,lights[lightBlue].ambient); glFrontFace(GL_CW); glEnable(GL_LIGHTING); glEnable(GL_AUTO_NORMAL); glEnable(GL_NORMALIZE); glMaterialf(GL_FRONT, GL_SHININESS, 0.6 * 128.0); glClearColor(bgColor[0], bgColor[1], bgColor[2], 1.0); glColor3f(1.0, 1.0, 1.0); glViewport(0, 0, (GLint)bounds.IntegerWidth() + 1, (GLint)bounds.IntegerHeight() + 1); glMatrixMode(GL_PROJECTION); glLoadIdentity(); float scale = displayScale; glOrtho(-scale, scale, -scale, scale, -scale * depthOfView, scale * depthOfView); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); UnlockGL(); fDrawThread = spawn_thread(simonThread, "Simon", B_NORMAL_PRIORITY, this); resume_thread(fDrawThread); fForceRedraw = true; setEvent(drawEvent); } void ObjectView::DetachedFromWindow() { BGLView::DetachedFromWindow(); status_t dummy; long locks = 0; while (Window()->IsLocked()) { locks++; Window()->Unlock(); } acquire_sem(quittingSem); release_sem(drawEvent); wait_for_thread(fDrawThread, &dummy); release_sem(quittingSem); while (locks--) Window()->Lock(); } void ObjectView::Pulse() { Window()->Lock(); BRect parentBounds = Parent()->Bounds(); BRect bounds = Bounds(); parentBounds.OffsetTo(0, 0); bounds.OffsetTo(0, 0); if (bounds != parentBounds) { ResizeTo(parentBounds.right - parentBounds.left, parentBounds.bottom - parentBounds.top); } Window()->Unlock(); } void ObjectView::MessageReceived(BMessage* msg) { BMenuItem* item = NULL; bool toggleItem = false; switch (msg->what) { case kMsgFPS: fFps = (fFps) ? false : true; msg->FindPointer("source", reinterpret_cast(&item)); item->SetMarked(fFps); fForceRedraw = true; setEvent(drawEvent); break; case kMsgAddModel: { TriangleObject *Tri = new TriangleObject(this); if (Tri->InitCheck() == B_OK) { fObjListLock.Lock(); fObjects.AddItem(Tri); fObjListLock.Unlock(); } else { BAlert *NoResourceAlert = new BAlert(B_TRANSLATE("Error"), kNoResourceError, B_TRANSLATE("OK"), NULL, NULL, B_WIDTH_AS_USUAL, B_OFFSET_SPACING, B_STOP_ALERT); NoResourceAlert->SetFlags(NoResourceAlert->Flags() | B_CLOSE_ON_ESCAPE); NoResourceAlert->Go(); delete Tri; } setEvent(drawEvent); break; } case kMsgLights: { msg->FindPointer("source", reinterpret_cast(&item)); long lightNum = msg->FindInt32("num"); long color = msg->FindInt32("color"); BMenu *menu = item->Menu(); long index = menu->IndexOf(item); menu->ItemAt(index)->SetMarked(true); for (int i = 0; i < menu->CountItems(); i++) { if (i != index) menu->ItemAt(i)->SetMarked(false); } LockGL(); if (color != lightNone) { glEnable(GL_LIGHT0 + lightNum - 1); glLightfv(GL_LIGHT0 + lightNum - 1, GL_SPECULAR, lights[color].specular); glLightfv(GL_LIGHT0 + lightNum - 1, GL_DIFFUSE, lights[color].diffuse); glLightfv(GL_LIGHT0 + lightNum - 1, GL_AMBIENT, lights[color].ambient); } else { glDisable(GL_LIGHT0 + lightNum - 1); } UnlockGL(); fForceRedraw = true; setEvent(drawEvent); break; } case kMsgGouraud: fGouraud = !fGouraud; toggleItem = true; break; case kMsgZBuffer: fZbuf = !fZbuf; toggleItem = true; break; case kMsgCulling: fCulling = !fCulling; toggleItem = true; break; case kMsgLighting: fLighting = !fLighting; toggleItem = true; break; case kMsgFilled: fFilled = !fFilled; toggleItem = true; break; case kMsgPerspective: fPersp = !fPersp; toggleItem = true; break; case kMsgFog: fFog = !fFog; toggleItem = true; break; } if (toggleItem && msg->FindPointer("source", reinterpret_cast(&item)) == B_OK){ item->SetMarked(!item->IsMarked()); setEvent(drawEvent); } BGLView::MessageReceived(msg); } int ObjectView::ObjectAtPoint(const BPoint &point) { LockGL(); glShadeModel(GL_FLAT); glDisable(GL_LIGHTING); glDisable(GL_FOG); glClearColor(black[0], black[1], black[2], 1.0); glClear(GL_COLOR_BUFFER_BIT | (fZbuf ? GL_DEPTH_BUFFER_BIT : 0)); float idColor[3]; idColor[1] = idColor[2] = 0; for (int i = 0; i < fObjects.CountItems(); i++) { // to take into account 16 bits colorspaces, // only use the 5 highest bits of the red channel idColor[0] = (255 - (i << 3)) / 255.0; reinterpret_cast(fObjects.ItemAt(i))->Draw(true, idColor); } glReadBuffer(GL_BACK); uchar pixel[256]; glReadPixels((GLint)point.x, (GLint)(Bounds().bottom - point.y), 1, 1, GL_RGB, GL_UNSIGNED_BYTE, pixel); int objNum = pixel[0]; objNum = (255 - objNum) >> 3; EnforceState(); UnlockGL(); return objNum; } void ObjectView::MouseDown(BPoint point) { GLObject* object = NULL; BMessage *msg = Window()->CurrentMessage(); uint32 buttons = msg->FindInt32("buttons"); object = reinterpret_cast(fObjects.ItemAt(ObjectAtPoint(point))); if (object != NULL){ if (buttons == B_PRIMARY_MOUSE_BUTTON || buttons == B_SECONDARY_MOUSE_BUTTON) { fTrackingInfo.pickedObject = object; fTrackingInfo.buttons = buttons; fTrackingInfo.isTracking = true; fTrackingInfo.lastX = point.x; fTrackingInfo.lastY = point.y; fTrackingInfo.lastDx = 0.0f; fTrackingInfo.lastDy = 0.0f; fTrackingInfo.pickedObject->Spin(0.0f, 0.0f); SetMouseEventMask(B_POINTER_EVENTS, B_LOCK_WINDOW_FOCUS | B_NO_POINTER_HISTORY); BCursor grabbingCursor(B_CURSOR_ID_GRABBING); SetViewCursor(&grabbingCursor); } else { ConvertToScreen(&point); object->MenuInvoked(point); } } } void ObjectView::MouseUp(BPoint point) { if (fTrackingInfo.isTracking) { //spin the teapot on release, TODO: use a marching sum and divide by time if (fTrackingInfo.buttons == B_PRIMARY_MOUSE_BUTTON && fTrackingInfo.pickedObject != NULL && (fabs(fTrackingInfo.lastDx) > 1.0f || fabs(fTrackingInfo.lastDy) > 1.0f) ) { fTrackingInfo.pickedObject->Spin(0.5f * fTrackingInfo.lastDy, 0.5f * fTrackingInfo.lastDx); setEvent(drawEvent); } //stop tracking fTrackingInfo.isTracking = false; fTrackingInfo.buttons = 0; fTrackingInfo.pickedObject = NULL; fTrackingInfo.lastX = 0.0f; fTrackingInfo.lastY = 0.0f; fTrackingInfo.lastDx = 0.0f; fTrackingInfo.lastDy = 0.0f; BCursor grabCursor(B_CURSOR_ID_GRAB); SetViewCursor(&grabCursor); } } void ObjectView::MouseMoved(BPoint point, uint32 transit, const BMessage *msg) { if (fTrackingInfo.isTracking && fTrackingInfo.pickedObject != NULL) { float dx = point.x - fTrackingInfo.lastX; float dy = point.y - fTrackingInfo.lastY; fTrackingInfo.lastX = point.x; fTrackingInfo.lastY = point.y; if (fTrackingInfo.buttons == B_PRIMARY_MOUSE_BUTTON) { fTrackingInfo.pickedObject->Spin(0.0f, 0.0f); fTrackingInfo.pickedObject->RotateWorldSpace(dx,dy); fTrackingInfo.lastDx = dx; fTrackingInfo.lastDy = dy; setEvent(drawEvent); } else if (fTrackingInfo.buttons == B_SECONDARY_MOUSE_BUTTON) { float xinc = (dx * 2 * displayScale / Bounds().Width()); float yinc = (-dy * 2 * displayScale / Bounds().Height()); float zinc = 0; if (fPersp) { zinc = yinc * (fTrackingInfo.pickedObject->z / displayScale); xinc *= -(fTrackingInfo.pickedObject->z * 4 / zRatio); yinc *= -(fTrackingInfo.pickedObject->z * 4 / zRatio); } fTrackingInfo.pickedObject->x += xinc; if (modifiers() & B_SHIFT_KEY) fTrackingInfo.pickedObject->z += zinc; else fTrackingInfo.pickedObject->y += yinc; fForceRedraw = true; setEvent(drawEvent); } } else { void* object = fObjects.ItemAt(ObjectAtPoint(point)); BCursor cursor(object != NULL ? B_CURSOR_ID_GRAB : B_CURSOR_ID_SYSTEM_DEFAULT); SetViewCursor(&cursor); } } void ObjectView::FrameResized(float width, float height) { BGLView::FrameResized(width, height); LockGL(); width = Bounds().Width(); height = Bounds().Height(); fYxRatio = height / width; glViewport(0, 0, (GLint)width + 1, (GLint)height + 1); // To prevent weird buffer contents glClear(GL_COLOR_BUFFER_BIT); glMatrixMode(GL_PROJECTION); glLoadIdentity(); float scale = displayScale; if (fPersp) { gluPerspective(60, 1.0 / fYxRatio, 0.15, 120); } else { if (fYxRatio < 1) { glOrtho(-scale / fYxRatio, scale / fYxRatio, -scale, scale, -1.0, depthOfView * 4); } else { glOrtho(-scale, scale, -scale * fYxRatio, scale * fYxRatio, -1.0, depthOfView * 4); } } fLastYXRatio = fYxRatio; glMatrixMode(GL_MODELVIEW); UnlockGL(); fForceRedraw = true; setEvent(drawEvent); } bool ObjectView::RepositionView() { if (!(fPersp != fLastPersp) && !(fLastObjectDistance != fObjectDistance) && !(fLastYXRatio != fYxRatio)) { return false; } LockGL(); glMatrixMode(GL_PROJECTION); glLoadIdentity(); float scale = displayScale; if (fPersp) { gluPerspective(60, 1.0 / fYxRatio, 0.15, 120); } else { if (fYxRatio < 1) { glOrtho(-scale / fYxRatio, scale / fYxRatio, -scale, scale, -1.0, depthOfView * 4); } else { glOrtho(-scale, scale, -scale * fYxRatio, scale * fYxRatio, -1.0, depthOfView * 4); } } glMatrixMode(GL_MODELVIEW); UnlockGL(); fLastObjectDistance = fObjectDistance; fLastPersp = fPersp; fLastYXRatio = fYxRatio; return true; } void ObjectView::EnforceState() { glShadeModel(fGouraud ? GL_SMOOTH : GL_FLAT); if (fZbuf) glEnable(GL_DEPTH_TEST); else glDisable(GL_DEPTH_TEST); if (fCulling) glEnable(GL_CULL_FACE); else glDisable(GL_CULL_FACE); if (fLighting) glEnable(GL_LIGHTING); else glDisable(GL_LIGHTING); if (fFilled) glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); else glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); if (fFog) { glFogf(GL_FOG_START, 10.0); glFogf(GL_FOG_DENSITY, 0.2); glFogf(GL_FOG_END, depthOfView); glFogfv(GL_FOG_COLOR, foggy); glEnable(GL_FOG); bgColor = foggy; glClearColor(bgColor[0], bgColor[1], bgColor[2], 1.0); } else { glDisable(GL_FOG); bgColor = black; glClearColor(bgColor[0], bgColor[1], bgColor[2], 1.0); } } bool ObjectView::SpinIt() { bool changed = false; if (fGouraud != fLastGouraud) { LockGL(); glShadeModel(fGouraud ? GL_SMOOTH : GL_FLAT); UnlockGL(); fLastGouraud = fGouraud; changed = true; } if (fZbuf != fLastZbuf) { LockGL(); if (fZbuf) glEnable(GL_DEPTH_TEST); else glDisable(GL_DEPTH_TEST); UnlockGL(); fLastZbuf = fZbuf; changed = true; } if (fCulling != fLastCulling) { LockGL(); if (fCulling) glEnable(GL_CULL_FACE); else glDisable(GL_CULL_FACE); UnlockGL(); fLastCulling = fCulling; changed = true; } if (fLighting != fLastLighting) { LockGL(); if (fLighting) glEnable(GL_LIGHTING); else glDisable(GL_LIGHTING); UnlockGL(); fLastLighting = fLighting; changed = true; } if (fFilled != fLastFilled) { LockGL(); if (fFilled) { glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); } else { glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); } UnlockGL(); fLastFilled = fFilled; changed = true; } if (fFog != fLastFog) { if (fFog) { glFogf(GL_FOG_START, 1.0); glFogf(GL_FOG_DENSITY, 0.2); glFogf(GL_FOG_END, depthOfView); glFogfv(GL_FOG_COLOR, foggy); glEnable(GL_FOG); bgColor = foggy; glClearColor(bgColor[0], bgColor[1], bgColor[2], 1.0); } else { glDisable(GL_FOG); bgColor = black; glClearColor(bgColor[0], bgColor[1], bgColor[2], 1.0); } fLastFog = fFog; changed = true; } changed = changed || RepositionView(); changed = changed || fForceRedraw; fForceRedraw = false; for (int i = 0; i < fObjects.CountItems(); i++) { bool hack = reinterpret_cast(fObjects.ItemAt(i))->SpinIt(); changed = changed || hack; } return changed; } void ObjectView::DrawFrame(bool noPause) { LockGL(); glClear(GL_COLOR_BUFFER_BIT | (fZbuf ? GL_DEPTH_BUFFER_BIT : 0)); fObjListLock.Lock(); for (int i = 0; i < fObjects.CountItems(); i++) { GLObject *object = reinterpret_cast(fObjects.ItemAt(i)); if (object->Solidity() == 0) object->Draw(false, NULL); } EnforceState(); for (int i = 0; i < fObjects.CountItems(); i++) { GLObject *object = reinterpret_cast(fObjects.ItemAt(i)); if (object->Solidity() != 0) object->Draw(false, NULL); } fObjListLock.Unlock(); glDisable(GL_BLEND); glDepthMask(GL_TRUE); if (noPause) { uint64 now = system_time(); float fps = 1.0 / ((now - fLastFrame) / 1000000.0); fLastFrame = now; int entry; if (fHistEntries < HISTSIZE) { entry = (fOldestEntry + fHistEntries) % HISTSIZE; fHistEntries++; } else { entry = fOldestEntry; fOldestEntry = (fOldestEntry + 1) % HISTSIZE; } fFpsHistory[entry] = fps; if (fHistEntries > 5) { fps = 0; for (int i = 0; i < fHistEntries; i++) fps += fFpsHistory[(fOldestEntry + i) % HISTSIZE]; fps /= fHistEntries; if (fFps) { glPushAttrib(GL_ENABLE_BIT | GL_LIGHTING_BIT); glPushMatrix(); glLoadIdentity(); glTranslatef(-0.9, -0.9, 0); glScalef(0.10, 0.10, 0.10); glDisable(GL_LIGHTING); glDisable(GL_DEPTH_TEST); glDisable(GL_BLEND); glColor3f(1.0, 1.0, 0); glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); glMatrixMode(GL_MODELVIEW); FPS::drawCounter(fps); glMatrixMode(GL_PROJECTION); glPopMatrix(); glMatrixMode(GL_MODELVIEW); glPopMatrix(); glPopAttrib(); } } } else { fHistEntries = 0; fOldestEntry = 0; } SwapBuffers(); UnlockGL(); }