diff --git a/src/apps/3dmov/3dmov.rdef b/src/apps/3dmov/3dmov.rdef index c61a264322..bc2109fe7c 100644 --- a/src/apps/3dmov/3dmov.rdef +++ b/src/apps/3dmov/3dmov.rdef @@ -1,37 +1,11 @@ /* - * Copyright 2004-2009, Haiku Inc. All rights reserved. + * Copyright 2009, Haiku Inc. All rights reserved. * Distributed under the terms of the MIT License. * * Authors: - * François Revol + * Zenja Solaja */ -resource(1, "BEOS:APP_FLAGS") (#'APPF') $"00000000"; - -resource(1, "BEOS:APP_VERSION") #'APPV' array { - $"0000000000000000000000000000000000000000000000000000000000000000" - $"0000000000000000000000000000000000000000000000000000000000000000" - $"0000000000000000000000000000000000000000000000000000000000000000" - $"0000000000000000000000000000000000000000000000000000000000000000" - $"0000000000000000000000000000000000000000000000000000000000000000" - $"0000000000000000000000000000000000000000000000000000000000000000" - $"0000000000000000000000000000000000000000000000000000000000000000" - $"0000000000000000000000000000000000000000000000000000000000000000" - $"0000000000000000000000000000000000000000000000000000000000000000" - $"0000000000000000000000000000000000000000000000000000000000000000" - $"000000000000000000000000000000000000000080031200FC105760EC111000" - $"EC088BC800000000FC1057808003220080052980800294408000600380032200" - $"8005298000000002FC1057C0FC105A7C000000018003E7800000000280032200" - $"800529808002944080006003EC02ECB88005298000000000FC1057D0EC111000" - $"EC01BE200000000200000000FC105A7C000000018003E7800000000280006003" - $"000000028001DEE88001DEF8EC02ECB88004B38000000000FC1058108003E780" - $"EC01BE20EC02ECB8FC105810EC111000EC01BE7C00000001FC10582000000000" - $"EC02ECBCFFFFFFFFEC05B434FC1058B88005298080029440FC10585080019244" - $"FC1059B8EC02ECB8FC105850EC111000EC01BE7C8002A680FC105870EC111000" - $"80052980FFFFFFFFEC05B434FC10593800000004000201DC800060038003E780" - $"8005298000000000FC105890420C0000EC019C080000010043300000EC111000" - $"8004B38000000000" -}; - -resource(1, "BEOS:APP_SIG") (#'MIMS') "application/x-vnd.Haiku-3DMov"; +resource app_signature "application/x-vnd.Haiku-3DMov"; +resource(1, "instructions.png") #'PNG ' import "instructions.png"; diff --git a/src/apps/3dmov/Application.cpp b/src/apps/3dmov/Application.cpp new file mode 100644 index 0000000000..1f236d11d6 --- /dev/null +++ b/src/apps/3dmov/Application.cpp @@ -0,0 +1,63 @@ +/* PROJECT: 3Dmov + AUTHORS: Zenja Solaja + COPYRIGHT: 2009 Haiku Inc + DESCRIPTION: Haiku version of the famous BeInc demo 3Dmov + + There is an issue in Haiku when creating a 2nd BGLView which doesn't exist in Zeta + (see MainWindow.cpp for details). Until that issue is resolved, + I've allowed specifying of shape type (0-2) as a command line argument. +*/ + +#include + +#include "MainWindow.h" + +/***************************** + Main application +******************************/ +class MainApp : public BApplication +{ +public: + MainApp(MainWindow::SHAPE shape); + +private: + MainWindow *fWindow; +}; + +/* FUNCTION: MainApp :: MainApp + ARGS: none + RETURN: n/a + DESCRIPTION: Application constructor +*/ +MainApp :: MainApp(MainWindow::SHAPE shape) + : BApplication("application/x_vnd.Haiku") +{ + BRect frame(50, 50, 50+400, 50+300); + fWindow = new MainWindow(frame, shape); +} + +/****************************** + Program entry point +*******************************/ + +/* FUNCTION: main + ARGS: arc number of command line arguments + argv vector to arguments + RETURN: Exit status + DESCRIPTION: main program entry point +*/ +int main(int argc, char **argv) +{ + // Check if shape specified on command line + MainWindow::SHAPE shape = MainWindow::BOOK; + if (argc > 1) + { + int value = *argv[1] - '0'; + if (value >= 0 && value < MainWindow::NUMBER_OF_SHAPES) + shape = (MainWindow::SHAPE) value; + } + + MainApp *app = new MainApp(shape); + app->Run(); + delete app; +} diff --git a/src/apps/3dmov/GLUtility.cpp b/src/apps/3dmov/GLUtility.cpp new file mode 100644 index 0000000000..281d0fcc7c --- /dev/null +++ b/src/apps/3dmov/GLUtility.cpp @@ -0,0 +1,244 @@ +/* PROJECT: 3Dmov + AUTHORS: Zenja Solaja + COPYRIGHT: 2009 Haiku Inc + DESCRIPTION: Haiku version of the famous BeInc demo 3Dmov + Just drag'n'drop media files to the 3D objects +*/ + +#include +#include + +#include "GLUtility.h" + +// Local definitions + +// Local functions + +// Local variables + +/* FUNCTION: GLCreateIdentityMatrix + ARGUMENTS: m destination matrix + RETURN: n/a + DESCRIPTION: Create identity matrix +*/ +void GLCreateIdentityMatrix(float *m) +{ + const float kIdentity[16] = + { + 1,0,0,0, + 0,1,0,0, + 0,0,1,0, + 0,0,0,1, + }; + memcpy(m, kIdentity, 16*sizeof(float)); +} + +/* FUNCTION: GLMatrixMultiply + ARGUMENTS: product + m1, m2 + RETURN: product + DESCRIPTION: Perform a 4x4 matrix multiplication (product = m1 x m2) +*/ +void GLMatrixMultiply(float *product, float *m1, float *m2) +{ + product[0] = m1[0]*m2[0] + m1[4]*m2[1] + m1[8]*m2[2] + m1[12]*m2[3]; + product[1] = m1[1]*m2[0] + m1[5]*m2[1] + m1[9]*m2[2] + m1[13]*m2[3]; + product[2] = m1[2]*m2[0] + m1[6]*m2[1] + m1[10]*m2[2] + m1[14]*m2[3]; + product[3] = m1[3]*m2[0] + m1[7]*m2[1] + m1[11]*m2[2] + m1[15]*m2[3]; + + product[4] = m1[0]*m2[4] + m1[4]*m2[5] + m1[8]*m2[6] + m1[12]*m2[7]; + product[5] = m1[1]*m2[4] + m1[5]*m2[5] + m1[9]*m2[6] + m1[13]*m2[7]; + product[6] = m1[2]*m2[4] + m1[6]*m2[5] + m1[10]*m2[6] + m1[14]*m2[7]; + product[7] = m1[3]*m2[4] + m1[7]*m2[5] + m1[11]*m2[6] + m1[15]*m2[7]; + + product[8] = m1[0]*m2[8] + m1[4]*m2[9] + m1[8]*m2[10] + m1[12]*m2[11]; + product[9] = m1[1]*m2[8] + m1[5]*m2[9] + m1[9]*m2[10] + m1[13]*m2[11]; + product[10] = m1[2]*m2[8] + m1[6]*m2[9] + m1[10]*m2[10] + m1[14]*m2[11]; + product[11] = m1[3]*m2[8] + m1[7]*m2[9] + m1[11]*m2[10] + m1[15]*m2[11]; + + product[12] = m1[0]*m2[12] + m1[4]*m2[13] + m1[8]*m2[14] + m1[12]*m2[15]; + product[13] = m1[1]*m2[12] + m1[5]*m2[13] + m1[9]*m2[14] + m1[13]*m2[15]; + product[14] = m1[2]*m2[12] + m1[6]*m2[13] + m1[10]*m2[14] + m1[14]*m2[15]; + product[15] = m1[3]*m2[12] + m1[7]*m2[13] + m1[11]*m2[14] + m1[15]*m2[15]; +} + +/* FUNCTION: GLCreatePerspectiveMatrix + ARGUMENTS: m destination matrix + fov field of view + aspect aspect ratio + znear near plane + zfar far plane + RETURN: n/a + DESCRIPTION: Same as gluPerspective +*/ +void GLCreatePerspectiveMatrix(float *m, float fov, float aspect, float znear, float zfar) +{ + const float h = 1.0f/tand(fov/2.0f); + float neg_depth = znear-zfar; + + m[0] = h / aspect; + m[1] = 0; + m[2] = 0; + m[3] = 0; + + m[4] = 0; + m[5] = h; + m[6] = 0; + m[7] = 0; + + m[8] = 0; + m[9] = 0; + m[10] = (zfar + znear)/neg_depth; + m[11] = -1; + + m[12] = 0; + m[13] = 0; + m[14] = 2.0f*(znear*zfar)/neg_depth; + m[15] = 0; +} + +/* FUNCTION: GLCreateModelViewMatrix + ARGUMENTS: m destination matrix + x,y,z position + yaw compass direction, 0-north, 90-east, 180-south, 270-west + pitch azimuth, -90 down, 0 forwad, 90 up + roll rotation around forward axis + RETURN: n/a + DESCRIPTION: Position camera +*/ +void GLCreateModelViewMatrix(float *m, float x, float y, float z, float yaw, float pitch, float roll) +{ + // same as gluLookAt + Quaternion q, qDir, qAzim; + qAzim.GenerateLocalRotation(pitch + 90.0f, -1.0f, 0.0f, 0.0f); + qDir.GenerateLocalRotation(yaw, 0.0f, 0.0f, 1.0f); + q = qAzim * qDir; + + if (roll != 0) + { + Quaternion qTilt; + qTilt.GenerateLocalRotation(roll, 0, 0, 1); + q = qTilt * q; + } + q.CreateRotatedQuaternion(m); + + // move camera + float mov[16]; + GLCreateIdentityMatrix(mov); + mov[12] = -x; + mov[13] = -y; + mov[14] = -z; + GLMatrixMultiply(m, m, mov); +} + +/************************** + Quaternion class +***************************/ + +/* FUNCTION: Quaternion + ARGUMENTS: none + RETURN: n/a + DESCRIPTION: Constructor. No rotation. +*/ +Quaternion :: Quaternion() +{ + x = y = z = 0.0f; + w = 1.0f; +} + +/* FUNCTION: Quaternion :: GenerateLocalRotation + ARGUMENTS: angle + x_axis, y_axis, z_axis axis of rotation + RETURN: n/a + DESCRIPTION: Create unit quaterion corresponding to a rotation through angle + about axis . + q = cos (angle/2) + A(a,b,c)*sin(angle/2) + +*/ +void Quaternion :: GenerateLocalRotation(float angle, float x_axis, float y_axis, float z_axis) +{ + float temp = sind(0.5f*angle); + + w = cosd(0.5f*angle); + x = x_axis * temp; + y = y_axis * temp; + z = z_axis * temp; +} + +/* FUNCTION: Quaternion :: GenerateFromEuler + ARGUMENTS: roll (degrees) + pitch (degrees) + yaw (degrees) + RETURN: n/a + DESCRIPTION: Creates quaternion from euler angles. Borowed from Bullet +*/ +void Quaternion :: GenerateFromEuler(float roll, float pitch, float yaw) +{ + float halfYaw = 0.5f * yaw; + float halfPitch = 0.5f * pitch; + float halfRoll = 0.5f * roll; + float cosYaw = cosd(halfYaw); + float sinYaw = sind(halfYaw); + float cosPitch = cosd(halfPitch); + float sinPitch = sind(halfPitch); + float cosRoll = cosd(halfRoll); + float sinRoll = sind(halfRoll); + + x = sinRoll*cosPitch*cosYaw - cosRoll*sinPitch*sinYaw; + y = cosRoll*sinPitch*cosYaw + sinRoll*cosPitch*sinYaw; + z = cosRoll*cosPitch*sinYaw - sinRoll*sinPitch*cosYaw; + w = cosRoll*cosPitch*cosYaw + sinRoll*sinPitch*sinYaw; +} + +/* FUNCTION: Quaternion :: CreateRotatedQuaternion + ARGUMENTS: matrix + RETURN: n/a + DESCRIPTION: Creates rotated quaterion. Always call GenerateLocalRotation() prior + to calling this method +*/ +void Quaternion :: CreateRotatedQuaternion(float *matrix) +{ + if (!matrix) + return; + + // first row + matrix[0] = 1.0f - 2.0f*(y*y + z*z); + matrix[1] = 2.0f*(x*y + w*z); + matrix[2] = 2.0f*(x*z - w*y); + matrix[3] = 0.0f; + + // second row + matrix[4] = 2.0f*(x*y - w*z); + matrix[5] = 1.0f - 2.0f*(x*x + z*z); + matrix[6] = 2.0f*(y*z + w*x); + matrix[7] = 0.0f; + + // third row + matrix[8] = 2.0f*(x*z + w*y); + matrix[9] = 2.0f*(y*z - w*x); + matrix[10] = 1.0f - 2.0f*(x*x + y*y); + matrix[11] = 0.0f; + + // forth row + matrix[12] = 0; + matrix[13] = 0; + matrix[14] = 0; + matrix[15] = 1.0f; +} + +/* FUNCTION: operator * + ARGUMENTS: q + RETURN: multiplied quaternion + DESCRIPTION: Creates rotated quaternion +*/ +const Quaternion Quaternion::operator *(Quaternion q) +{ + Quaternion p; + + p.x = w*q.x + x*q.w + y*q.z - z*q.y; // (w1x2 + x1w2 + y1z2 - z1y2)i + p.y = w*q.y - x*q.z + y*q.w + z*q.x; // (w1y2 - x1z2 + y1w2 + z1x2)j + p.z = w*q.z + x*q.y - y*q.x + z*q.w; // (w1z2 + x1y2 - y1x2 + z1w2)k + p.w = w*q.w - x*q.x - y*q.y - z*q.z; // (w1w2 - x1x2 - y1y2 - z1z2) + + return(p); +} diff --git a/src/apps/3dmov/GLUtility.h b/src/apps/3dmov/GLUtility.h new file mode 100644 index 0000000000..0bd11ccdda --- /dev/null +++ b/src/apps/3dmov/GLUtility.h @@ -0,0 +1,58 @@ +/* PROJECT: 3Dmov + AUTHORS: Zenja Solaja + COPYRIGHT: 2009 Haiku Inc + DESCRIPTION: Haiku version of the famous BeInc demo 3Dmov + Just drag'n'drop media files to the 3D objects +*/ + +#ifndef _GL_UTILITY_H_ +#define _GL_UTILITY_H_ + +#include + +/************************** + Definitions +***************************/ +#define Y_PI 3.14159265358979323846f +#define Y_PI_DIV_180 (Y_PI/180.0f) + + +/************************** + Macros +***************************/ +#define sind(a) (sinf((a) * Y_PI_DIV_180)) +#define cosd(a) (cosf((a) * Y_PI_DIV_180)) +#define tand(a) (tanf((a) * Y_PI_DIV_180)) + +/************************** + Functions +***************************/ +void GLCreateIdentityMatrix(float *m); +void GLMatrixMultiply(float *destination, float *matrix_a, float *matrix_b); +void GLCreatePerspectiveMatrix(float *m, float fov, float aspect, float znear, float zfar); +void GLCreateModelViewMatrix(float *m, float x, float y, float z, float yaw, float pitch, float roll=0.0f); + +/************************** + Quaternion class +***************************/ +class Quaternion +{ +public: + Quaternion(); + + void GenerateLocalRotation(float angle, float x_axis, float y_axis, float z_axis); + void CreateRotatedQuaternion(float *matrix); + void GenerateFromEuler(float roll, float pitch, float yaw); + + // Operators + inline const Quaternion operator+=(const Quaternion &q) {x += q.x; y += q.y; z += q.z; w += q.w; return *this;} + inline const Quaternion operator-=(const Quaternion &q) {x -= q.x; y -= q.y; z -= q.z; w -= q.w; return *this;} + const Quaternion operator *(Quaternion q); + +private: + float x; + float y; + float z; + float w; +}; +#endif //#ifndef _GL_UTILITY_H_ diff --git a/src/apps/3dmov/Jamfile b/src/apps/3dmov/Jamfile index 799b2ab6e8..1a2956b2a0 100644 --- a/src/apps/3dmov/Jamfile +++ b/src/apps/3dmov/Jamfile @@ -3,10 +3,14 @@ SubDir HAIKU_TOP src apps 3dmov ; SetSubDirSupportedPlatformsBeOSCompatible ; Application 3DMov : - CubeView.cpp - GLMovApp.cpp - GLMovView.cpp - GLMovWindow.cpp - : be GL game $(TARGET_LIBSUPC++) + Application.cpp + MainWindow.cpp + ViewObject.cpp + Video.cpp + ViewBook.cpp + ViewCube.cpp + ViewSphere.cpp + GLUtility.cpp + : be GL game media translation $(TARGET_LIBSUPC++) : 3dmov.rdef ; diff --git a/src/apps/3dmov/MainWindow.cpp b/src/apps/3dmov/MainWindow.cpp new file mode 100644 index 0000000000..2f250d6906 --- /dev/null +++ b/src/apps/3dmov/MainWindow.cpp @@ -0,0 +1,248 @@ +/* PROJECT: 3Dmov + AUTHORS: Zenja Solaja + COPYRIGHT: 2009 Haiku Inc + DESCRIPTION: Haiku version of the famous BeInc demo 3Dmov. + + TODO: + There is a bug in Haiku which doesn't allow a 2nd BGLView to be created. + Originally I tried AddChild(new BGLView), however nothing would be drawn in the view. + On Zeta, both AddChild(new BGLView) and new MainWindow work without any problems. +*/ + +#include +#include +#include + +#include "MainWindow.h" +#include "ViewObject.h" +#include "ViewBook.h" +#include "ViewCube.h" +#include "ViewSphere.h" + +// Local definitions +static const float MENU_BAR_HEIGHT = 19; + +static const unsigned int MSG_FILE_OPEN = 'fopn'; +static const unsigned int MSG_ABOUT = 'info'; +static const unsigned int MSG_SHAPE_BOOK = 'spbk'; +static const unsigned int MSG_SHAPE_CUBE = 'spcb'; +static const unsigned int MSG_SHAPE_SPHERE = 'spsp'; +static const unsigned int MSG_OPTION_WIREFRAME = 'opwf'; +static const unsigned int MSG_FULLSCREEN = 'full'; + +static const char *ABOUT_TEXT = "\ +Drag and drop media files (pictures and videos) onto the 3D shapes. \ +Use your mouse to interact with the 3D shape.\n\ +Pick up your jaw from the floor.\n\n\ +Inspired by the original BeInc 3dmov demo.\n\ +Written by Zenja Solaja, 2009"; + + +// Local functions +static int32 animation_thread(void *cookie); + +// Local variables +static int sMainWindowCount = 0; // keep track of number of spawned windows + +/* FUNCTION: MainWindow :: MainWindow + ARGUMENTS: frame + shape + RETURN: n/a + DESCRIPTION: Constructor +*/ +MainWindow :: MainWindow(BRect frame, SHAPE shape) + : BDirectWindow(frame, "3Dmov", B_TITLED_WINDOW, 0) +{ + sMainWindowCount++; + + fOptionWireframe = false; + + // Add menu bar + frame.OffsetTo(B_ORIGIN); + BRect aRect = frame; + aRect.bottom = MENU_BAR_HEIGHT; + frame.top = MENU_BAR_HEIGHT; + SetupMenuBar(aRect); + + // Add book view + switch (shape) + { + case BOOK: AddChild(fCurrentView = new ViewBook(frame)); break; + case CUBE: AddChild(fCurrentView = new ViewCube(frame)); break; + case SPHERE: AddChild(fCurrentView = new ViewSphere(frame)); break; + } + + //AddShortcut('f', B_COMMAND_KEY, new BMessage(MSG_FULLSCREEN)); + + // Window should never be larger than 2048 + SetSizeLimits(40.0, 2047.0, 40.0, 2047.0); + Show(); + + fAnimationThreadID = spawn_thread(animation_thread, "Animation Thread", B_NORMAL_PRIORITY, fCurrentView); + resume_thread(fAnimationThreadID); +} + +/* FUNCTION: MainWindow :: ~MainWindow + ARGUMENTS: n/a + RETURN: n/a + DESCRIPTION: Destructor +*/ +MainWindow :: ~MainWindow() +{ + kill_thread(fAnimationThreadID); + RemoveChild(fCurrentView); + delete fCurrentView; + + sMainWindowCount--; + if (sMainWindowCount == 0) + be_app->PostMessage(B_QUIT_REQUESTED); +} + +/* FUNCTION: MainWindow :: SetupMenuBar + ARGUMENTS: frame + RETURN: n/a + DESCRIPTION: Initialise menu bar +*/ +void MainWindow :: SetupMenuBar(BRect frame) +{ + // File + BMenu *menu_file = new BMenu("File"); + //menu_file->AddItem(new BMenuItem("Open", new BMessage(MSG_FILE_OPEN), 'N')); + menu_file->AddItem(new BMenuItem("About", new BMessage(MSG_ABOUT))); + menu_file->AddItem(new BMenuItem("Exit", new BMessage(B_QUIT_REQUESTED))); + + // Shape + BMenu *menu_shape = new BMenu("Shape"); + menu_shape->AddItem(new BMenuItem("Book", new BMessage(MSG_SHAPE_BOOK), '1')); + menu_shape->AddItem(new BMenuItem("Cube", new BMessage(MSG_SHAPE_CUBE), '2')); + menu_shape->AddItem(new BMenuItem("Sphere", new BMessage(MSG_SHAPE_SPHERE), '3')); + + // Options + BMenu *menu_options = new BMenu("Options"); + menu_options->AddItem(new BMenuItem("Wireframe", new BMessage(MSG_OPTION_WIREFRAME), 'S')); + //menu_options->AddItem(new BMenuItem("Fullscreen", new BMessage(MSG_FULLSCREEN), 'F')); + + // Menu bar + fMenuBar = new BMenuBar(frame, "menubar"); + fMenuBar->AddItem(menu_file); + fMenuBar->AddItem(menu_shape); + fMenuBar->AddItem(menu_options); + AddChild(fMenuBar); +} + +/* FUNCTION: MainWindow :: MessageReceived + ARGUMENTS: message + RETURN: n/a + DESCRIPTION: Called by BeOS +*/ +void MainWindow :: MessageReceived(BMessage *message) +{ + switch (message->what) + { + case MSG_FULLSCREEN: + SetFullScreen(!IsFullScreen()); + break; + + case MSG_ABOUT: + { + BAlert *about = new BAlert("3Dmov", ABOUT_TEXT, "Wow"); + about->SetShortcut(0, B_ESCAPE); + about->Go(NULL); + break; + } + + /* TODO - Due to a bug when creating a 2nd BGLView in Haiku, I've decided to spawn a new window + instead of creating a new BGLView (and using AddChild(new_view) / RemoveChild(old_view). + Under Zeta, there is no problem replacing the current view with a new view. + */ + + case MSG_SHAPE_BOOK: + new MainWindow(BRect(50, 50, 400+50, 300+50), BOOK); + break; + + case MSG_SHAPE_CUBE: + new MainWindow(BRect(50, 50, 400+50, 300+50), CUBE); + break; + + case MSG_SHAPE_SPHERE: + new MainWindow(BRect(50, 50, 400+50, 300+50), SPHERE); + break; + + case MSG_OPTION_WIREFRAME: + fOptionWireframe = !fOptionWireframe; + fCurrentView->ToggleWireframe(fOptionWireframe); + break; + + case 'DATA': // user drag/dropped file from Tracker + { + BPoint point; + message->FindPoint("_drop_point_", &point); + BRect frame = Frame(); + point.x -= frame.left; + point.y -= frame.top; + + entry_ref aRef; + message->FindRef("refs", &aRef); + + fCurrentView->DragDrop(&aRef, point.x, point.y); + break; + } + + default: + BDirectWindow::MessageReceived(message); + break; + } +} + +/* FUNCTION: MainWindow :: QuitRequested + ARGUMENTS: none + RETURN: true if success + DESCRIPTION: Called when window closed +*/ +bool MainWindow :: QuitRequested() +{ + //be_app->PostMessage(B_QUIT_REQUESTED); + return true; +} + +/* FUNCTION: MainWindow :: DirectConnected + ARGUMENTS: info + RETURN: n/a + DESCRIPTION: Hook functions called when full screen mode toggled +*/ +void MainWindow :: DirectConnected(direct_buffer_info *info) +{ + switch (info->buffer_state & B_DIRECT_MODE_MASK) + { + // start a direct screen connection. + case B_DIRECT_START : + break; + // stop a direct screen connection. + case B_DIRECT_STOP : + break; + // modify the state of a direct screen connection. + case B_DIRECT_MODIFY : + break; + default : + break; + } +} + +/* FUNCTION: animation_thread + ARGUMENTS: cookie + RETURN: exit status + DESCRIPTION: Main rendering thread +*/ +#include +static int32 animation_thread(void *cookie) +{ + ViewObject *view = (ViewObject *) cookie; + while (1) + { + view->Render(); + view->GLCheckError(); + snooze(1); + } + return B_OK; // keep compiler happy +} + diff --git a/src/apps/3dmov/MainWindow.h b/src/apps/3dmov/MainWindow.h new file mode 100644 index 0000000000..de5a4f1fc5 --- /dev/null +++ b/src/apps/3dmov/MainWindow.h @@ -0,0 +1,46 @@ +/* PROJECT: 3Dmov + AUTHORS: Zenja Solaja + COPYRIGHT: 2009 Haiku Inc + DESCRIPTION: Haiku version of the famous BeInc demo 3Dmov + Just drag'n'drop media files to the 3D objects +*/ + +#ifndef _MAIN_WINDOW_H_ +#define _MAIN_WINDOW_H_ + +#include "DirectWindow.h" + +class BMenuBar; +class ViewObject; + +/************************************ + Main application window +*************************************/ +class MainWindow : public BDirectWindow +{ +public: + enum SHAPE + { + BOOK, + CUBE, + SPHERE, + NUMBER_OF_SHAPES + }; + + MainWindow(BRect frame, SHAPE shape); + ~MainWindow(); + + void MessageReceived(BMessage *message); + void DirectConnected(direct_buffer_info *info); + bool QuitRequested(); + +private: + void SetupMenuBar(BRect frame); + + BMenuBar *fMenuBar; + bool fOptionWireframe; + int32 fAnimationThreadID; + ViewObject *fCurrentView; +}; + +#endif diff --git a/src/apps/3dmov/Video.cpp b/src/apps/3dmov/Video.cpp new file mode 100644 index 0000000000..07739a9d83 --- /dev/null +++ b/src/apps/3dmov/Video.cpp @@ -0,0 +1,208 @@ +/* PROJECT: 3Dmov + AUTHORS: Zenja Solaja + COPYRIGHT: 2009 Haiku Inc + DESCRIPTION: Haiku version of the famous BeInc demo 3Dmov + This file handles video playback for ViewObject base class. +*/ + +#include +#include +#include + +#include "ViewObject.h" +#include "Video.h" + +/* FUNCTION: Video :: Video + ARGUMENTS: ref + RETURN: n/a + DESCRIPTION: Constructor +*/ +Video :: Video(entry_ref *ref) +{ + fMediaFile = 0; + fVideoTrack = 0; + fBitmap = 0; + fVideoThread = 0; + + fMediaFile = new BMediaFile(ref, B_MEDIA_FILE_BIG_BUFFERS); + fStatus = fMediaFile->InitCheck(); + if (fStatus != B_OK) + return; + + int32 num_tracks = fMediaFile->CountTracks(); + for (int32 i=0; i < num_tracks; i++) + { + BMediaTrack *track = fMediaFile->TrackAt(i); + if (track == NULL) + { + fMediaFile->ReleaseAllTracks(); + printf("Media file claims to have %ld tracks, cannot find track %ld\n", num_tracks, i); + fVideoTrack = 0; + return; + } + + media_format mf; + fStatus = track->EncodedFormat(&mf); + if (fStatus == B_OK) + { + switch (mf.type) + { + case B_MEDIA_ENCODED_VIDEO: + case B_MEDIA_RAW_VIDEO: + if (fVideoTrack == 0) + { + fVideoTrack = track; + InitPlayer(&mf); + } + else + printf("Multiple video tracks not supported\n"); + break; + default: + fStatus = B_ERROR; + } + } + + if (fStatus != B_OK) + fMediaFile->ReleaseTrack(track); + } + + if (fVideoTrack) + fStatus = B_OK; +} + +/* FUNCTION: Video :: ~Video + ARGUMENTS: n/a + RETURN: n/a + DESCRIPTION: Destructor +*/ +Video :: ~Video() +{ + if (fVideoThread > 0) + kill_thread(fVideoThread); + delete fMediaFile; + delete fBitmap; +} + +/* FUNCTION: Video :: InitPlayer + ARGUMENTS: format + RETURN: n/a + DESCRIPTION: Create frame buffer and init decoder +*/ +void Video :: InitPlayer(media_format *format) +{ + BRect frame(0, 0, + format->u.encoded_video.output.display.line_width - 1.0f, + format->u.encoded_video.output.display.line_count - 1.0f); + + BScreen screen; + color_space cs = screen.ColorSpace(); + + // Loop asking the track for a format we can deal with + for (;;) + { + fBitmap = new BBitmap(frame, cs); + + media_format mf, old_mf; + memset(&mf, 0, sizeof(media_format)); + media_raw_video_format *rvf = &mf.u.raw_video; + rvf->last_active = (uint32)(frame.Height() - 1.0f); + rvf->orientation = B_VIDEO_TOP_LEFT_RIGHT; + rvf->pixel_width_aspect = 1; + rvf->pixel_height_aspect = 3; + rvf->display.format = cs; + rvf->display.line_width = (int32)frame.Width(); + rvf->display.line_count = (int32)frame.Height(); + rvf->display.bytes_per_row = fBitmap->BytesPerRow(); + + old_mf = mf; + fVideoTrack->DecodedFormat(&mf); + // check if match found + if (old_mf.u.raw_video.display.format == mf.u.raw_video.display.format) + break; + + // otherwise, change colour space + cs = mf.u.raw_video.display.format; + delete fBitmap; + } + + media_header mh; + fVideoTrack->SeekToTime(0); + int64 dummy_num_frames; + fVideoTrack->ReadFrames((char *)fBitmap->Bits(), &dummy_num_frames, &mh); + fVideoTrack->SeekToTime(0); +} + +/* FUNCTION: Video :: Start + ARGUMENTS: none + RETURN: n/a + DESCRIPTION: Start playing video +*/ +void Video :: Start() +{ + fVideoThread = spawn_thread(Video::VideoThread, "Video thread", B_NORMAL_PRIORITY, this); + if (fVideoThread > 0) + resume_thread(fVideoThread); +} + +/* FUNCTION: Video :: ShowNextFrame + ARGUMENTS: none + RETURN: status + DESCRIPTION: Read next frame, update texture +*/ +status_t Video :: ShowNextFrame() +{ + status_t err; + media_header mh; + int64 dummy = 0; + + fBitmap->LockBits(); + err = fVideoTrack->ReadFrames((char *)fBitmap->Bits(), &dummy, &mh); + fBitmap->UnlockBits(); + + if (err != B_OK) + { + // restart video + fVideoTrack->SeekToTime(0); + return B_OK; + } + + fMediaSource->mOwner->UpdateFrame(fMediaSource); + return B_OK; +} + +/* FUNCTION: Video :: VideoThread + ARGUMENTS: cookie + RETURN: thread exit status + DESCRIPTION: Video playback thread +*/ +int32 Video :: VideoThread(void *cookie) +{ + Video *video = (Video *) cookie; + if (video->fVideoTrack == NULL) + { + exit_thread(B_ERROR); + return B_ERROR; + } + + float frames_per_second = (float)video->fVideoTrack->CountFrames() / (float)video->fVideoTrack->Duration() * 1000000.0f; + bigtime_t frame_time = (bigtime_t) (1000000.0f / frames_per_second); + video->fPerformanceTime = real_time_clock_usecs() + frame_time; + status_t err = B_OK; + printf("frame_rate = %f\n", frames_per_second); + + while (1) + { + err = video->ShowNextFrame(); + bigtime_t zzz = video->fPerformanceTime - real_time_clock_usecs(); + if (zzz < 0) + zzz = 1; + video->fPerformanceTime += frame_time; + snooze(zzz); + } + exit_thread(err); + return err; +} + + + + \ No newline at end of file diff --git a/src/apps/3dmov/Video.h b/src/apps/3dmov/Video.h new file mode 100644 index 0000000000..2173f89550 --- /dev/null +++ b/src/apps/3dmov/Video.h @@ -0,0 +1,45 @@ +/* PROJECT: 3Dmov + AUTHORS: Zenja Solaja + COPYRIGHT: 2009 Haiku Inc + DESCRIPTION: Haiku version of the famous BeInc demo 3Dmov + Media playback support. +*/ + +#ifndef _VIDEO_H_ +#define _VIDEO_H_ + +#include + +class BBitmap; +class MediaSource; + +/***************************** + Video class handles media playback +******************************/ +class Video +{ +public: + Video(entry_ref *ref); + ~Video(); + + status_t GetStatus() {return fStatus;} + BBitmap *GetBitmap() {return fBitmap;} + void Start(); + void SetMediaSource(MediaSource *source) {fMediaSource = source;} + +private: + MediaSource *fMediaSource; + status_t fStatus; + BMediaFile *fMediaFile; + BMediaTrack *fVideoTrack; + BBitmap *fBitmap; + thread_id fVideoThread; + bigtime_t fPerformanceTime; + + void InitPlayer(media_format *format); + status_t ShowNextFrame(); + static int32 VideoThread(void *arg); // one thread per object is spawned +}; + +#endif //#ifndef _VIDEO_H_ + diff --git a/src/apps/3dmov/ViewBook.cpp b/src/apps/3dmov/ViewBook.cpp new file mode 100644 index 0000000000..69875adba0 --- /dev/null +++ b/src/apps/3dmov/ViewBook.cpp @@ -0,0 +1,525 @@ +/* PROJECT: 3Dmov + AUTHORS: Zenja Solaja + COPYRIGHT: 2009 Haiku Inc + DESCRIPTION: Haiku version of the famous BeInc demo 3Dmov + The book has 4 pages with images, and the user can move pages 2/3. + Texture management is really primitive - only the default image is + shared, while the other page images are unique instances. +*/ + +#include +#include + +#include + +#include "GLUtility.h" +#include "ViewBook.h" + +// Local definitions + +// Local functions +// void GetPage(Paper * + +// Local variables + +/******************************** + Paper scene +*********************************/ +class Paper +{ +public: + enum ORIENTATION + { + FRONT_FACING, + BACK_FACING, + FRONT_AND_BACK_FACING, + }; + enum TEXTURE_SIDE + { + FRONT_TEXTURE, + BACK_TEXTURE, + UNASSIGNED_TEXTURE, + }; + Paper(float x_size, float y_size, ORIENTATION orientation, const int number_columns = 6, const int number_rows = 4); + ~Paper(); + + void Render(); + void SetMediaSource(TEXTURE_SIDE side, MediaSource *source); + void SetAngle(float angle); + +private: + ORIENTATION fOrientation; + MediaSource *fFrontMediaSource; + MediaSource *fBackMediaSource; + float *fGeometry; + float *fTextureCoords; + + int fNumberVertices; + float fWidth, fHeight; + int fNumberColumns, fNumberRows; + + void InitTextureCoordinates(); + void ModifyGeometry(); + + float fAngle; +}; + +/* FUNCTION: Paper :: Paper + ARGUMENTS: x_size, y_size + number_columns + number_rows; + RETURN: n/a + DESCRIPTION: Constructor +*/ +Paper :: Paper(float x_size, float y_size, ORIENTATION orientation, const int number_columns, const int number_rows) +{ + assert(number_rows%2 == 0); // rows must be divisible by 2 + + fOrientation = orientation; + fFrontMediaSource = 0; + fBackMediaSource = 0; + fWidth = x_size; + fHeight = y_size; + fNumberColumns = number_columns; + fNumberRows = number_rows; + + fNumberVertices = (fNumberColumns*2+1)*fNumberRows + 1; + fGeometry = new float [fNumberVertices*3]; + fTextureCoords = new float [fNumberVertices * 2]; + InitTextureCoordinates(); + + SetAngle(0); +} + +/* FUNCTION: Paper :: ~Paper + ARGUMENTS: n/a + RETURN: n/a + DESCRIPTION: Destructor. MediaSource destruction handled by ViewBook +*/ +Paper :: ~Paper() +{ + delete [] fGeometry; + delete [] fTextureCoords; +} + +/* FUNCTION: Paper :: SetMediaSource + ARGUMENTS: side + source + RETURN: n/a + DESCRIPTION: Assign paper texture id +*/ +void Paper :: SetMediaSource(TEXTURE_SIDE side, MediaSource *source) +{ + if (side == FRONT_TEXTURE) + fFrontMediaSource = source; + else + fBackMediaSource = source; +} + +/* FUNCTION: Paper :: SetAngle + ARGUMENTS: angle 0 - paper is on left side of book + 90 - paper is vertical + 180 - paper is on right side of book + RETURN: n/a + DESCRIPTION: Set paper angle and recalculate geometry +*/ +void Paper :: SetAngle(float angle) +{ + fAngle = angle; + ModifyGeometry(); +} + +/* FUNCTION: Paper :: Render + ARGUMENTS: none + RETURN: n/a + DESCRIPTION: Draw paper +*/ +void Paper :: Render() +{ + glPushMatrix(); + glTranslatef(-0.5f*fWidth, -0.5f*fHeight, 0); + + if ((fOrientation == FRONT_FACING) || (fOrientation == FRONT_AND_BACK_FACING)) + { + glColor4f(1,1,1,1); + glBindTexture(GL_TEXTURE_2D, fFrontMediaSource->mTextureID); + + glTexCoordPointer(2, GL_FLOAT, 0, fTextureCoords); + glVertexPointer(3, GL_FLOAT, 0, fGeometry); + glDrawArrays(GL_TRIANGLE_STRIP, 0, fNumberVertices); + } + + if ((fOrientation == BACK_FACING) || (fOrientation == FRONT_AND_BACK_FACING)) + { + glColor4f(1,1,1,1); + glBindTexture(GL_TEXTURE_2D, fBackMediaSource->mTextureID); + + // Mirror texture coordinates + glMatrixMode(GL_TEXTURE); + glScalef(-1,1,1); + + glFrontFace(GL_CW); + glTexCoordPointer(2, GL_FLOAT, 0, fTextureCoords); + glVertexPointer(3, GL_FLOAT, 0, fGeometry); + glDrawArrays(GL_TRIANGLE_STRIP, 0, fNumberVertices); + glFrontFace(GL_CCW); + + // Restore texture mirroring + glScalef(-1,1,1); + glMatrixMode(GL_MODELVIEW); + } + glPopMatrix(); +} + +/* FUNCTION: Paper :: InitTextureCoordinates + ARGUMENTS: none + RETURN: n/a + DESCRIPTION: Set texture coordinates (they never change) +*/ +void Paper :: InitTextureCoordinates() +{ + float *p = fTextureCoords; + for (int r = fNumberRows-2; r >= 0; r-=2) + { + if (r == fNumberRows-2) + { + *p++ = 0; + *p++ = 0; + } + for (int c=0; c < fNumberColumns; c++) + { + *p++ = (float)c/fNumberColumns; + *p++ = 1 - (float)(r+1)/fNumberRows; + + *p++ = (float)(c+1)/fNumberColumns; + *p++ = 1 - (float)(r+2)/fNumberRows; + } + for (int c = fNumberColumns; c >= 0; c--) + { + *p++ = (float)c/fNumberColumns; + *p++ = 1 - (float)(r+1)/fNumberRows; + + *p++ = (float)c/fNumberColumns; + *p++ = 1 - (float)r/fNumberRows; + } + } +} + +/* FUNCTION: Paper :: ModifyGeometry + ARGUMENTS: none + RETURN: n/a + DESCRIPTION: Modify vertices only +*/ +void Paper :: ModifyGeometry() +{ + float *p = fGeometry; + const float kPageConcaveHeight = 0.1f*fWidth; + + // Cache frequent calculations + float ca = cosd(fAngle); + float w = fWidth * sind(fAngle); + + for (int r = fNumberRows-2; r >= 0; r-=2) + { + if (r == fNumberRows-2) + { + *p++ = (1-ca)*fWidth; + *p++ = fHeight; + *p++ = w; + } + for (int c=0; c < fNumberColumns; c++) + { + *p++ = (1-ca)*fWidth + ca*(float)c/fNumberColumns * fWidth; + *p++ = (float)(r+1)/fNumberRows * fHeight; + *p++ = (1-(float)c/fNumberColumns) * w + kPageConcaveHeight*sind(180*(float)c/fNumberColumns); + + *p++ = (1-ca)*fWidth + ca*(float)(c+1)/fNumberColumns * fWidth; + *p++ = (float)(r+2)/fNumberRows * fHeight; + *p++ = (1-(float)(c+1)/fNumberColumns) * w + + kPageConcaveHeight*sind(180*(float)(c+1)/fNumberColumns); + } + for (int c = fNumberColumns; c >= 0; c--) + { + *p++ = (1-ca)*fWidth + ca*(float)c/fNumberColumns * fWidth; + *p++ = (float)(r+1)/fNumberRows * fHeight; + *p++ = (1-(float)c/fNumberColumns) * w + kPageConcaveHeight*sind(180*(float)c/fNumberColumns); + + *p++ = (1-ca)*fWidth + ca*(float)c/fNumberColumns * fWidth; + *p++ = (float)r/fNumberRows * fHeight; + *p++ = (1-(float)c/fNumberColumns) * w + kPageConcaveHeight*sind(180*(float)c/fNumberColumns); + } + } +} + +/******************************** + ViewBook +*********************************/ + +static const float kPageWidth = 0.210f; // A4 book +static const float kPageHeight = 0.297f; +static const float kPageMinAngle = 4.0f; +static const float kPageMaxAngle = 176.0f; + +/* FUNCTION: ViewBook :: ViewBook + ARGUMENTS: frame + RETURN: n/a + DESCRIPTION: Constructor +*/ +ViewBook :: ViewBook(BRect frame) + : ViewObject(frame) +{ + fStartTime = real_time_clock_usecs(); + for (int i=0; i < NUMBER_SOURCES; i++) + fMediaSources[i] = 0; + for (int i=0; i < NUMBER_PAGES; i++) + fPages[i] = 0; + + fMouseTracking = false; + fPageAngle = kPageMaxAngle; +} + +/* FUNCTION: ViewBook :: ~ViewBook + ARGUMENTS: n/a + RETURN: n/a + DESCRIPTION: Destructor +*/ +ViewBook :: ~ViewBook() +{ + for (int i=0; i < NUMBER_SOURCES; i++) + { + if (fMediaSources[i] != GetDefaultMediaSource()) + delete fMediaSources[i]; + } + + for (int i=0; i < NUMBER_PAGES; i++) + delete fPages[i]; +} + +/* FUNCTION: ViewBook :: AttachedToWindow + ARGUMENTS: none + RETURN: n/a + DESCRIPTION: Hook function called when view attached to window (looper) +*/ +void ViewBook :: AttachedToWindow(void) +{ + ViewObject::AttachedToWindow(); + + LockGL(); + glClearColor(0,0,0,1); + + fPages[PAGE_MIDDLE] = new Paper(kPageWidth, kPageHeight, Paper::FRONT_AND_BACK_FACING); + fPages[PAGE_LEFT] = new Paper(kPageWidth, kPageHeight, Paper::FRONT_FACING); + fPages[PAGE_RIGHT] = new Paper(kPageWidth, kPageHeight, Paper::BACK_FACING); + + for (int i=0; i < NUMBER_SOURCES; i++) + fMediaSources[i] = GetDefaultMediaSource(); + + MediaSource *default_source = GetDefaultMediaSource(); + // left page + fPages[PAGE_LEFT]->SetMediaSource(Paper::FRONT_TEXTURE, fMediaSources[0]); + // middle page + fPages[PAGE_MIDDLE]->SetMediaSource(Paper::FRONT_TEXTURE, fMediaSources[1]); + fPages[PAGE_MIDDLE]->SetMediaSource(Paper::BACK_TEXTURE, fMediaSources[2]); + // right page + fPages[PAGE_RIGHT]->SetMediaSource(Paper::BACK_TEXTURE, fMediaSources[3]); + fPages[PAGE_RIGHT]->SetAngle(180); + + UnlockGL(); +} + +/* FUNCTION: ViewBook :: Render + ARGUMENTS: none + RETURN: n/a + DESCRIPTION: Draw view contents +*/ +void ViewBook :: Render(void) +{ + LockGL(); + + bigtime_t current_time = real_time_clock_usecs(); + bigtime_t delta = current_time - fStartTime; + fStartTime = current_time; + + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + // page freefall (gravity) + if (!fMouseTracking && (fPageAngle > kPageMinAngle) && (fPageAngle < kPageMaxAngle)) + { + if (fPageAngle < 90) + fPageAngle -= 30*(float)delta/1000000.0f; + else + fPageAngle += 30*(float)delta/1000000.0f; + } + fPages[PAGE_MIDDLE]->SetAngle(fPageAngle); + + // Draw pages + glPushMatrix(); + glTranslatef(-0.5f*kPageWidth, 0.5f*kPageHeight, 0); + fPages[PAGE_MIDDLE]->Render(); + glPopMatrix(); + + glPushMatrix(); + glTranslatef(-0.5f*kPageWidth, 0.5f*kPageHeight, 0); + fPages[PAGE_LEFT]->Render(); + glPopMatrix(); + + glPushMatrix(); + glTranslatef(-0.5f*kPageWidth, 0.5f*kPageHeight, 0); + fPages[PAGE_RIGHT]->Render(); + glPopMatrix(); + + + glFlush(); + SwapBuffers(); + + // Frame rate + /* + static int fps = 0; + static int time_delta = 0; + fps++; + time_delta += delta; + if (time_delta > 1000000) + { + printf("%d fps\n", fps); + fps = 0; + time_delta = 0; + } + */ + UnlockGL(); +} + +/* FUNCTION: ViewBook :: MouseDown + ARGUMENTS: p + RETURN: n/a + DESCRIPTION: Hook function called when mouse down detected +*/ +void ViewBook :: MouseDown(BPoint p) +{ + // Determine mouse button + BMessage* msg = Window()->CurrentMessage(); + uint32 buttons; + + msg->FindInt32("buttons", (int32*)&buttons); + + if (buttons & B_PRIMARY_MOUSE_BUTTON) + { + Paper::TEXTURE_SIDE side = Paper::UNASSIGNED_TEXTURE; + Paper *page = GetPage(p.x, p.y, (int *)&side); + if (page == fPages[PAGE_MIDDLE]) + { + SetMouseEventMask(B_POINTER_EVENTS, B_LOCK_WINDOW_FOCUS | B_NO_POINTER_HISTORY); + fMouseTracking = true; + } + } +} + +/* FUNCTION: ViewBook :: MouseMoved + ARGUMENTS: p + transit + message + RETURN: n/a + DESCRIPTION: Hook function called when mouse move detected +*/ +void ViewBook :: MouseMoved(BPoint p, uint32 transit, const BMessage *message) +{ + if (fMouseTracking) + { + if (transit == B_INSIDE_VIEW) + { + BRect frame = Bounds(); + fPageAngle = 180 * p.x / frame.Width(); + if (fPageAngle < kPageMinAngle) + fPageAngle = kPageMinAngle; + if (fPageAngle > kPageMaxAngle) + fPageAngle = kPageMaxAngle; + } + } +} + +/* FUNCTION: ViewBook :: MouseUp + ARGUMENTS: p + RETURN: n/a + DESCRIPTION: Hook function called when mouse up detected +*/ +void ViewBook :: MouseUp(BPoint p) +{ + fMouseTracking = false; +} + +/* FUNCTION: ViewBook :: GetPage + ARGUMENTS: mouse_x + mouse_y + RETURN: page corresponding to mouse_x, mouse_y + DESCRIPTION: Determine which page does mouse_x/mouse_y correspond to +*/ +Paper * ViewBook :: GetPage(float mouse_x, float mouse_y, int *side) +{ + BRect frame = Bounds(); + Paper *page = 0; + + if (fPageAngle > 90) + { + if (mouse_x < 0.5f*frame.Width()) + { + page = fPages[PAGE_LEFT]; + *side = Paper::FRONT_TEXTURE; + } + else + { + page = fPages[PAGE_MIDDLE]; + *side = Paper::BACK_TEXTURE; + } + } + else + { + if (mouse_x < 0.5f*frame.Width()) + { + page = fPages[PAGE_MIDDLE]; + *side = Paper::FRONT_TEXTURE; + } + else + { + page = fPages[PAGE_RIGHT]; + *side = Paper::BACK_TEXTURE; + } + } + return page; +} + +/* FUNCTION: ViewBook :: DragDropImage + ARGUMENTS: texture_id + mouse_x + mouse_y + RETURN: true if source ownership acquired + DESCRIPTION: Hook function called when user drags/drops image to app window +*/ +bool ViewBook :: SurfaceUpdate(MediaSource *source, float mouse_x, float mouse_y) +{ + Paper::TEXTURE_SIDE side = Paper::UNASSIGNED_TEXTURE; + Paper *page = GetPage(mouse_x, mouse_y, (int *)&side); + + LockGL(); + int index = -1; + if (page == fPages[PAGE_LEFT]) + index = 0; + else if (page == fPages[PAGE_RIGHT]) + index = 3; + else if (page == fPages[PAGE_MIDDLE]) + { + if (side == Paper::FRONT_TEXTURE) + index = 1; + else if (side == Paper::BACK_TEXTURE) + index = 2; + } + if (index >= 0) + { + if (fMediaSources[index] != GetDefaultMediaSource()) + delete fMediaSources[index]; + } + page->SetMediaSource(side, source); + fMediaSources[index] = source; + UnlockGL(); + + return true; +} + + diff --git a/src/apps/3dmov/ViewBook.h b/src/apps/3dmov/ViewBook.h new file mode 100644 index 0000000000..a384c3962d --- /dev/null +++ b/src/apps/3dmov/ViewBook.h @@ -0,0 +1,51 @@ +/* PROJECT: 3Dmov + AUTHORS: Zenja Solaja + COPYRIGHT: 2009 Haiku Inc + DESCRIPTION: Haiku version of the famous BeInc demo 3Dmov + Just drag'n'drop media files to the 3D objects +*/ + +#ifndef _VIEW_BOOK_H_ +#define _VIEW_BOOK_H_ + +#ifndef _VIEW_OBJECT_H_ +#include "ViewObject.h" +#endif + +class Paper; + +/********************************** + ViewBook displays a book with turnable pages +***********************************/ +class ViewBook : public ViewObject +{ +public: + ViewBook(BRect frame); + ~ViewBook(); + + void AttachedToWindow(); + void MouseDown(BPoint); + void MouseMoved(BPoint p, uint32 transit, const BMessage *message); + void MouseUp(BPoint); + void Render(); + bool SurfaceUpdate(MediaSource *source, float mouse_x, float mouse_y); + +private: + enum + { + PAGE_LEFT = 0, + PAGE_MIDDLE = 1, + PAGE_RIGHT = 2, + NUMBER_PAGES = 3, + NUMBER_SOURCES = 4, + }; + Paper *GetPage(float mouse_x, float mouse_y, int *side); + + bool fMouseTracking; + float fPageAngle; + bigtime_t fStartTime; + Paper *fPages[NUMBER_PAGES]; + MediaSource *fMediaSources[NUMBER_SOURCES]; +}; + +#endif //#ifndef _VIEW_BOOK_H_ diff --git a/src/apps/3dmov/ViewCube.cpp b/src/apps/3dmov/ViewCube.cpp new file mode 100644 index 0000000000..1f925c17ec --- /dev/null +++ b/src/apps/3dmov/ViewCube.cpp @@ -0,0 +1,353 @@ +/* PROJECT: 3Dmov + AUTHORS: Zenja Solaja + COPYRIGHT: 2009 Haiku Inc + DESCRIPTION: Haiku version of the famous BeInc demo 3Dmov + Just drag'n'drop media files to the 3D objects +*/ + +#include +#include +#include + +#include +#include + +#include "GLUtility.h" +#include "ViewCube.h" + +// Local definitions + +// Local functions + +// Local variables + +/******************************** + Cube scene +*********************************/ +class Cube +{ +public: + enum FACE + { + FACE_RIGHT, + FACE_LEFT, + FACE_FRONT, + FACE_BACK, + FACE_TOP, + FACE_BOTTOM, + NUMBER_CUBE_FACES, + }; + Cube(const float half_extent); + ~Cube(); + void Render(); + void SetMediaSource(FACE face, MediaSource *source) {fMediaSources[face] = source;} + void SetAngle(float angle_x, float angle_y, float angle_z); + +private: + MediaSource *fMediaSources[NUMBER_CUBE_FACES]; + float fRotationX, fRotationY, fRotationZ; + float *fGeometry; +}; + +/* FUNCTION: Cube :: Cube + ARGUMENTS: half_extent + RETURN: n/a + DESCRIPTION: Constructor +*/ +Cube :: Cube(const float half_extent) +{ + for (int i=0; i < NUMBER_CUBE_FACES; i++) + fMediaSources[i] = 0; + fRotationX = fRotationY = fRotationZ = 0; + + const float kVertices[NUMBER_CUBE_FACES][4][3] = + { + { // FACE_RIGHT + {half_extent, -half_extent, -half_extent}, + {half_extent, half_extent, -half_extent}, + {half_extent, -half_extent, half_extent}, + {half_extent, half_extent, half_extent}, + }, + { // FACE_LEFT + {-half_extent, half_extent, -half_extent}, + {-half_extent, -half_extent, -half_extent}, + {-half_extent, half_extent, half_extent}, + {-half_extent, -half_extent, half_extent}, + }, + { // FACE_FRONT + {-half_extent, -half_extent, -half_extent}, + {half_extent, -half_extent, -half_extent}, + {-half_extent, -half_extent, half_extent}, + {half_extent, -half_extent, half_extent}, + }, + { // FACE_BACK + {half_extent, half_extent, -half_extent}, + {-half_extent, half_extent, -half_extent}, + {half_extent, half_extent, half_extent}, + {-half_extent, half_extent, half_extent}, + }, + { // FACE_TOP + {-half_extent, -half_extent, half_extent}, + {half_extent, -half_extent, half_extent}, + {-half_extent, half_extent, half_extent}, + {half_extent, half_extent, half_extent}, + }, + { // FACE_BOTTOM + {-half_extent, half_extent, -half_extent}, + {half_extent, half_extent, -half_extent}, + {-half_extent, -half_extent, -half_extent}, + {half_extent, -half_extent, -half_extent}, + }, + }; + fGeometry = new float [sizeof(kVertices)/sizeof(float)]; + memcpy(fGeometry, kVertices, sizeof(kVertices)); +} + +/* FUNCTION: Cube :: ~Cube + ARGUMENTS: n/a + RETURN: n/a + DESCRIPTION: Destructor - fMediaSources owned by ViewCube +*/ +Cube :: ~Cube() +{ + delete fGeometry; +} + +/* FUNCTION: Cube :: SetAngle + ARGUMENTS: angle_x + angle_y + angle_z + RETURN: n/a + DESCRIPTION: Rotate cube by Euler angles +*/ +void Cube :: SetAngle(float angle_x, float angle_y, float angle_z) +{ + fRotationX = angle_x; + fRotationY = angle_y; + fRotationZ = angle_z; +} + +/* FUNCTION: Cube :: Render + ARGUMENTS: none + RETURN: n/a + DESCRIPTION: Draw sphere +*/ +void Cube :: Render() +{ + const float kTextureCoords[4*2] = + { + 0.0f, 1.0f, + 1.0f, 1.0f, + 0.0f, 0.0f, + 1.0f, 0.0f, + }; + + glPushMatrix(); + glRotatef(fRotationX, 1, 0, 0); + glRotatef(fRotationY, 0, 1, 0); + glRotatef(fRotationZ, 0, 0, 1); + + glColor4f(1,1,1,1); + for (int i=0; i < NUMBER_CUBE_FACES; i++) + { + glBindTexture(GL_TEXTURE_2D, fMediaSources[i]->mTextureID); + glTexCoordPointer(2, GL_FLOAT, 0, kTextureCoords); + glVertexPointer(3, GL_FLOAT, 0, fGeometry + i*3*4); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + } + glPopMatrix(); +} + +/******************************** + ViewCube +*********************************/ + +/* FUNCTION: ViewCube :: ViewCube + ARGUMENTS: frame + RETURN: n/a + DESCRIPTION: Constructor +*/ +ViewCube :: ViewCube(BRect frame) + : ViewObject(frame) +{ + fStartTime = real_time_clock_usecs(); + + for (int i=0; i < NUMBER_FACES; i++) + fMediaSources[i] = 0; + fCube = 0; + fSpeed = 10; + fMouseTracking = false; +} + +/* FUNCTION: ViewCube :: ~ViewCube + ARGUMENTS: n/a + RETURN: n/a + DESCRIPTION: Destructor +*/ +ViewCube :: ~ViewCube() +{ + delete fCube; + for (int i=0; i < NUMBER_FACES; i++) + { + if (fMediaSources[i] != GetDefaultMediaSource()) + delete fMediaSources[i]; + } +} + +/* FUNCTION: ViewCube :: ViewCube + ARGUMENTS: none + RETURN: n/a + DESCRIPTION: Hook function called when view attached to window (looper) +*/ +void ViewCube :: AttachedToWindow(void) +{ + ViewObject::AttachedToWindow(); + + LockGL(); + glClearColor(0,0,0,1); + + fCube = new Cube(0.075f); + + for (int i=0; i < NUMBER_FACES; i++) + { + fMediaSources[i] = GetDefaultMediaSource(); + fCube->SetMediaSource((Cube::FACE) i, fMediaSources[i]); + } + + UnlockGL(); +} + +/* FUNCTION: ViewCube :: Render + ARGUMENTS: none + RETURN: n/a + DESCRIPTION: Draw view contents +*/ +void ViewCube :: Render(void) +{ + LockGL(); + + bigtime_t current_time = real_time_clock_usecs(); + bigtime_t delta = current_time - fStartTime; + fStartTime = current_time; + + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + fCubeAngle += fSpeed*(float)delta/1000000.0f; + fCube->SetAngle(0, 0, fCubeAngle); + + glPushMatrix(); + glTranslatef(0.0f, 0.0f, 0.15f); + fCube->Render(); + glPopMatrix(); + + glFlush(); + SwapBuffers(); + + // Display frame rate + /* + static int fps = 0; + static int time_delta = 0; + fps++; + time_delta += delta; + if (time_delta > 1000000) + { + printf("%d fps\n", fps); + fps = 0; + time_delta = 0; + } + */ + UnlockGL(); +} + +/* FUNCTION: ViewCube :: MouseDown + ARGUMENTS: p + RETURN: n/a + DESCRIPTION: Hook function called when mouse down detected +*/ +void ViewCube :: MouseDown(BPoint p) +{ + // Determine mouse button + BMessage* msg = Window()->CurrentMessage(); + uint32 buttons; + + msg->FindInt32("buttons", (int32*)&buttons); + + if (buttons & B_PRIMARY_MOUSE_BUTTON) + { + SetMouseEventMask(B_POINTER_EVENTS, B_LOCK_WINDOW_FOCUS | B_NO_POINTER_HISTORY); + fMouseTracking = true; + fMousePosition = p; + } +} + +/* FUNCTION: ViewCube :: MouseMoved + ARGUMENTS: p + transit + message + RETURN: n/a + DESCRIPTION: Hook function called when mouse move detected +*/ +void ViewCube :: MouseMoved(BPoint p, uint32 transit, const BMessage *message) +{ + if (fMouseTracking) + { + if (transit == B_INSIDE_VIEW) + { + fSpeed = 5.0f*(p.x - fMousePosition.x); + fMousePosition = p; + } + } + +} + +/* FUNCTION: ViewSphere :: MouseUp + ARGUMENTS: p + RETURN: n/a + DESCRIPTION: Hook function called when mouse up detected +*/ +void ViewCube :: MouseUp(BPoint p) +{ + fMouseTracking = false; +} + +/* FUNCTION: ViewSphere :: DragDropImage + ARGUMENTS: texture_id + mouse_x + mouse_y + RETURN: true if source ownership acquired + DESCRIPTION: Hook function called when user drags/drops image to app window. + TODO - actually determine exact face where refs received. For this + release, we only rely on current fCubeAngle. +*/ +bool ViewCube :: SurfaceUpdate(MediaSource *source, float mouse_x, float mouse_y) +{ + // determine face + BRect frame = Bounds(); + Cube::FACE face = Cube::NUMBER_CUBE_FACES; + + if (mouse_y < frame.Height()*0.33f) + face = Cube::FACE_TOP; + else + { + if (fCubeAngle < 45) + face = Cube::FACE_FRONT; + else if (fCubeAngle < 135) + face = Cube::FACE_LEFT; + else if (fCubeAngle < 225) + face = Cube::FACE_BACK; + else if (fCubeAngle < 315) + face = Cube::FACE_RIGHT; + else + face = Cube::FACE_FRONT; + } + if (fMediaSources[face] != GetDefaultMediaSource()) + delete fMediaSources[face]; + fMediaSources[face] = source; + fCube->SetMediaSource(face, source); + + return true; +} + + + + diff --git a/src/apps/3dmov/ViewCube.h b/src/apps/3dmov/ViewCube.h new file mode 100644 index 0000000000..47d7702ac2 --- /dev/null +++ b/src/apps/3dmov/ViewCube.h @@ -0,0 +1,49 @@ +/* PROJECT: 3Dmov + AUTHORS: Zenja Solaja + COPYRIGHT: 2009 Haiku Inc + DESCRIPTION: Haiku version of the famous BeInc demo 3Dmov + Just drag'n'drop media files to the 3D objects +*/ + +#ifndef _VIEW_CUBE_H_ +#define _VIEW_CUBE_H_ + +#ifndef _VIEW_OBJECT_H_ +#include "ViewObject.h" +#endif + +class Cube; + +/********************************** + ViewCube displays a rotating box. +***********************************/ +class ViewCube : public ViewObject +{ +public: + ViewCube(BRect frame); + ~ViewCube(); + + void AttachedToWindow(); + void MouseDown(BPoint); + void MouseMoved(BPoint p, uint32 transit, const BMessage *message); + void MouseUp(BPoint); + void Render(); + +protected: + bool SurfaceUpdate(MediaSource *source, float mouse_x, float mouse_y); + +private: + enum + { + NUMBER_FACES = 6, + }; + bigtime_t fStartTime; + MediaSource *fMediaSources[NUMBER_FACES]; + Cube *fCube; + float fCubeAngle; + float fSpeed; + BPoint fMousePosition; + bool fMouseTracking; +}; + +#endif //#ifndef _VIEW_CUBE_H_ diff --git a/src/apps/3dmov/ViewObject.cpp b/src/apps/3dmov/ViewObject.cpp new file mode 100644 index 0000000000..e4fab35676 --- /dev/null +++ b/src/apps/3dmov/ViewObject.cpp @@ -0,0 +1,372 @@ +/* PROJECT: 3Dmov + AUTHORS: Zenja Solaja + COPYRIGHT: 2009 Haiku Inc + DESCRIPTION: Haiku version of the famous BeInc demo 3Dmov + This is the base class for all View shapes. +*/ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include "Video.h" + +#include "GLUtility.h" +#include "ViewObject.h" + +// Local definitions + +// Local functions + +// Local variables +MediaSource* ViewObject::sDefaultMediaSource = NULL; +BBitmap* ViewObject::sDefaultImage = NULL; + +static int sCountViewObjects = 0; + +/*********************************** + MediaSurface +************************************/ + +/* FUNCTION: MediaSource :: MediaSource + ARGUMENTS: owner + RETURN: n/a + DESCRIPTION: Constructor +*/ +MediaSource :: MediaSource(ViewObject *owner) +{ + mOwner = owner; + mTextureID = 0; + mVideo = 0; +} + +/* FUNCTION: MediaSource :: ~MediaSource + ARGUMENTS: n/a + RETURN: n/a + DESCRIPTION: Destructor +*/ +MediaSource :: ~MediaSource() +{ + if (mTextureID > 0) + glDeleteTextures(1, &mTextureID); + delete mVideo; +} + +/* FUNCTION: MediaSurface :: SetVideo + ARGUMENTS: video + RETURN: n/a + DESCRIPTION: Destructor +*/ +void MediaSource :: SetVideo(Video *video) +{ + mVideo = video; + mVideo->SetMediaSource(this); +} + +/*********************************** + ViewObject +************************************/ +/* FUNCTION: ViewObject :: ViewObject + ARGUMENTS: frame + RETURN: n/a + DESCRIPTION: Constructor +*/ +ViewObject :: ViewObject(BRect frame) + : BGLView(frame, (char *) "3Dmov_view", B_FOLLOW_ALL_SIDES, 0, BGL_RGB | BGL_DOUBLE | BGL_DEPTH) +{ + sCountViewObjects++; + + FrameResized(frame.Width(), frame.Height()); +} + +/* FUNCTION: ViewObject :: ~ViewObject + ARGUMENTS: n/a + RETURN: n/a + DESCRIPTION: Destructor +*/ +ViewObject :: ~ViewObject() +{ + if (--sCountViewObjects == 0) + { + delete sDefaultMediaSource; + sDefaultMediaSource = 0; + delete sDefaultImage; + } +} + +/* FUNCTION: ViewObject :: AttachedToWindow + ARGUMENTS: none + RETURN: n/a + DESCRIPTION: Hook function called when view attached to window (looper) +*/ +void ViewObject :: AttachedToWindow(void) +{ + LockGL(); + BGLView::AttachedToWindow(); + + // init OpenGL state + glClearColor(0, 0, 0, 1); + glEnable(GL_CULL_FACE); + glCullFace(GL_BACK); + glEnable(GL_DEPTH_TEST); + glEnable(GL_TEXTURE_2D); + glDepthFunc(GL_LESS); + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + + // init default texture (drag and drop media files) + if (sDefaultMediaSource == 0) + { + sDefaultImage = BTranslationUtils::GetBitmap(B_PNG_FORMAT, "instructions.png"); + + sDefaultMediaSource = new MediaSource(this); + glGenTextures(1, &sDefaultMediaSource->mTextureID); + glBindTexture(GL_TEXTURE_2D, sDefaultMediaSource->mTextureID); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + int num_bytes; + GLDetermineFormat(sDefaultImage->ColorSpace(), &sDefaultMediaSource->mTextureFormat, &num_bytes); + glTexImage2D(GL_TEXTURE_2D, + 0, // mipmap level + num_bytes, // internal format + sDefaultImage->Bounds().IntegerWidth() + 1, // width + sDefaultImage->Bounds().IntegerHeight() + 1, // height + 0, // border + sDefaultMediaSource->mTextureFormat, // format of pixel data + GL_UNSIGNED_BYTE, // data type + (unsigned char *) sDefaultImage->Bits()); // pixels + } + + MakeFocus(true); + UnlockGL(); +} + +/* FUNCTION: ViewObject :: FrameResized + ARGUMENTS: width + height + RETURN: n/a + DESCRIPTION: Hook function called when view resized. + Since we have a static camera, we configure it here. + If we had a dynamic camera, then we'd have to reposition + the camera in the derived view. +*/ +void ViewObject :: FrameResized(float width, float height) +{ + LockGL(); + BGLView::FrameResized(width, height); + glViewport(0, 0, (int)width, (int)height); + + // Camera projection + glMatrixMode(GL_PROJECTION); + float m[16]; + GLCreatePerspectiveMatrix(m, 30, width/height, 0.1f, 10); + glLoadMatrixf(m); + + // Camera position + glMatrixMode(GL_MODELVIEW); + GLCreateModelViewMatrix(m, 0, -0.45f, 0.4f, 0, -30); + glLoadMatrixf(m); + + UnlockGL(); +} + +/* FUNCTION: ViewObject :: GLDetermineFormat + ARGUMENTS: cs Haiku colour space + format Equivalent OpenGL format (see glPixelStore) + num_bytes number of bytes needed to store pixel at given format + RETURN: via argument pointers + DESCRIPTION: Haiku colour space is different to OpenGL colour space. + This function will convert it. +*/ +void ViewObject :: GLDetermineFormat(color_space cs, GLenum *format, int *num_bytes) +{ + switch (cs) + { + case B_RGBA32: + *num_bytes = 4; + *format = GL_BGRA; + break; + case B_RGB32: + *num_bytes = 4; + *format = GL_BGRA; + break; + case B_RGB24: + *num_bytes = 3; + *format = GL_BGR; + break; + case B_RGB15: + case B_RGB16: + *num_bytes = 2; + *format = GL_BGR; // OpenGL supports 4444, 5551, 444 and 555, but BBitmap doesn't + break; + case B_CMAP8: + *num_bytes = 1; + *format = GL_COLOR_INDEX; + break; + case B_GRAY8: + *num_bytes = 1; + *format = GL_LUMINANCE; + break; + default: // all other formats are unsupported + *num_bytes = 4; + *format = GL_RGBA; + } +} + +/* FUNCTION: ViewObject :: GLCreateTexture + ARGUMENTS: bitmap + RETURN: OpenGL texture reference + DESCRIPTION: Create an OpenGL texture from a BBitmap. + The client takes ownership of the create media source. +*/ +void ViewObject :: GLCreateTexture(MediaSource *media, BBitmap *bitmap) +{ + assert(media != 0); + assert(bitmap != 0); + + LockGL(); + glGenTextures(1, &media->mTextureID); + glBindTexture(GL_TEXTURE_2D, media->mTextureID); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + int num_bytes; + GLDetermineFormat(bitmap->ColorSpace(), &media->mTextureFormat, &num_bytes); + media->mTextureWidth = (int)(bitmap->Bounds().Width() + 1); + media->mTextureHeight = (int)(bitmap->Bounds().Height() + 1); + glTexImage2D(GL_TEXTURE_2D, + 0, // mipmap level + num_bytes, // internal format + media->mTextureWidth, // width + media->mTextureHeight, // height + 0, // border + media->mTextureFormat, // format of pixel data + GL_UNSIGNED_BYTE, // data type + (unsigned char *)bitmap->Bits()); // pixels + UnlockGL(); +} + +/* FUNCTION: ViewObject :: Render + ARGUMENTS: none + RETURN: n/a + DESCRIPTION: Should be overriden by derived class +*/ +void ViewObject :: Render(void) +{ + LockGL(); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + // derived class should draw something here + + glFlush(); + SwapBuffers(); + UnlockGL(); +} + +/* FUNCTION: ViewObject :: DragDrop + ARGUMENTS: none + RETURN: n/a + DESCRIPTION: Hook function called when user drops files in application window. + Essentially, determine if file is image or video. +*/ +void ViewObject :: DragDrop(entry_ref *ref, float mouse_x, float mouse_y) +{ + // Image file? + BPath path(ref); + BBitmap *bitmap = BTranslationUtils::GetBitmap(path.Path(), NULL); + if (bitmap) + { + MediaSource *media = new MediaSource(this); + GLCreateTexture(media, bitmap); + if (!SurfaceUpdate(media, mouse_x, mouse_y)) + delete media; + delete bitmap; + return; + } + + // Video file + Video *video = new Video(ref); + if (video->GetStatus() == B_OK) + { + MediaSource *media = new MediaSource(this); + GLCreateTexture(media, video->GetBitmap()); + media->SetVideo(video); + if (SurfaceUpdate(media, mouse_x, mouse_y)) + video->Start(); + else + delete media; + return; + } + else + delete video; + + printf("Unsupported file\n"); +} + +/* FUNCTION: ViewObject :: UpdateFrame + ARGUMENTS: source + RETURN: n/a + DESCRIPTION: Called by Video thread - update video texture +*/ +void ViewObject :: UpdateFrame(MediaSource *source) +{ + LockGL(); + glBindTexture(GL_TEXTURE_2D, source->mTextureID); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, source->mTextureWidth, source->mTextureHeight, + source->mTextureFormat, GL_UNSIGNED_BYTE, source->mVideo->GetBitmap()->Bits()); + UnlockGL(); +} + +/* FUNCTION: ViewObject :: ToggleWireframe + ARGUMENTS: enable + RETURN: n/a + DESCRIPTION: Switch beteeen wireframe and fill polygon modes +*/ +void ViewObject :: ToggleWireframe(bool enable) +{ + LockGL(); + if (enable) + glPolygonMode(GL_FRONT, GL_LINE); + else + glPolygonMode(GL_FRONT, GL_FILL); + UnlockGL(); +} + +/* FUNCTION: ViewObject :: GLCheckError + ARGUMENTS: none + RETURN: n/a + DESCRIPTION: Check for OpenGL errors +*/ +void ViewObject :: GLCheckError() +{ + GLenum err; + + LockGL(); + while ((err = glGetError()) != GL_NO_ERROR) + { + printf("[GL Error] "); + switch (err) + { + case GL_INVALID_ENUM: printf("GL_INVALID_ENUM\n"); break; + case GL_INVALID_VALUE: printf("GL_INVALID_VALUE\n"); break; + case GL_INVALID_OPERATION: printf("GL_INVALID_OPERATION\n"); break; + case GL_STACK_OVERFLOW: printf("GL_STACK_OVERFLOW\n"); break; + case GL_STACK_UNDERFLOW: printf("GL_STACK_UNDERFLOW\n"); break; + case GL_OUT_OF_MEMORY: printf("GL_OUT_OF_MEMORY\n"); break; + default: printf("%d\n", err); break; + } + } + UnlockGL(); +} + + diff --git a/src/apps/3dmov/ViewObject.h b/src/apps/3dmov/ViewObject.h new file mode 100644 index 0000000000..59ac65fcc8 --- /dev/null +++ b/src/apps/3dmov/ViewObject.h @@ -0,0 +1,77 @@ +/* PROJECT: 3Dmov + AUTHORS: Zenja Solaja + COPYRIGHT: 2009 Haiku Inc + DESCRIPTION: Haiku version of the famous BeInc demo 3Dmov + Just drag'n'drop media files to the 3D objects + + This is the base class for all 3Dmov objects. + As a bare minimum, the derived classes need to implement the Render() method. +*/ + +#ifndef _VIEW_OBJECT_H_ +#define _VIEW_OBJECT_H_ + +#include + +class BBitmap; +class Video; +class ViewObject; + +/********************************** + MediaSource can be an image or video. + A ViewObject can contain multiple media sources (eg. a cube has 6) +***********************************/ +class MediaSource +{ +public: + MediaSource(ViewObject *owner); + ~MediaSource(); + void SetVideo(Video *video); + + GLuint mTextureID; // OpenGL handle to texture + GLenum mTextureFormat; // OpenGL texture format + int mTextureWidth; + int mTextureHeight; + ViewObject *mOwner; + Video *mVideo; // video decoder +}; + + +/********************************** + A ViewObject is the base class for various 3Dmov shapes. + The shapes will typically override the Render method. +***********************************/ +class ViewObject : public BGLView +{ +public: + ViewObject(BRect frame); + virtual ~ViewObject(); + + virtual void AttachedToWindow(void); + virtual void FrameResized(float width, float height); + virtual void MouseDown(BPoint) {} + virtual void MouseMoved(BPoint p, uint32 transit, const BMessage *message = NULL) {} + virtual void MouseUp(BPoint) {} + virtual void Render(void); + + void DragDrop(entry_ref *ref, float mouse_x, float mouse_y); + void UpdateFrame(MediaSource *source); // called by video thread + void ToggleWireframe(bool enable); + void GLCheckError(); + +protected: + virtual bool SurfaceUpdate(MediaSource *source, float mouse_x, float mouse_y) {return false;} + MediaSource *GetDefaultMediaSource() {return sDefaultMediaSource;} + +private: + void GLDetermineFormat(color_space cs, GLenum *format, int *num_bytes); + void GLCreateTexture(MediaSource *source, BBitmap *bitmap); + + static MediaSource *sDefaultMediaSource; // instructions image, shared to conserve resources + static BBitmap* sDefaultImage; + +}; + +#endif //#ifndef _VIEW_OBJECT_H_ + + diff --git a/src/apps/3dmov/ViewSphere.cpp b/src/apps/3dmov/ViewSphere.cpp new file mode 100644 index 0000000000..abb9ec2b2a --- /dev/null +++ b/src/apps/3dmov/ViewSphere.cpp @@ -0,0 +1,322 @@ +/* PROJECT: 3Dmov + AUTHORS: Zenja Solaja + COPYRIGHT: 2009 Haiku Inc + DESCRIPTION: Haiku version of the famous BeInc demo 3Dmov + Just drag'n'drop media files to the 3D objects +*/ + +#include +#include + +#include +#include + +#include "GLUtility.h" +#include "ViewSphere.h" + +// Local definitions + +// Local functions + +// Local variables + +/******************************** + Sphere scene +*********************************/ +class Sphere +{ +public: + Sphere(const float radius, const int stacks = 10, const int slices = 10); + ~Sphere(); + void Render(); + void SetMediaSource(MediaSource *source) {fMediaSource = source;} + void SetAngle(float angle_y, float angle_z); + +private: + MediaSource *fMediaSource; + int fNumberVertices; + float *fGeometry; + float *fTextureCoords; + float fRotationY, fRotationZ; +}; + +/* FUNCTION: Sphere :: Sphere + ARGUMENTS: radius + stacks vertical stacks + slices horizontal slices + RETURN: n/a + DESCRIPTION: Constructor +*/ +Sphere :: Sphere(const float radius, const int stacks, const int slices) +{ + fGeometry = 0; + fTextureCoords = 0; + fMediaSource = 0; + fRotationY = 0; + fRotationZ = 0; + + fNumberVertices = 2*stacks*(slices+1); + fGeometry = new float [fNumberVertices*3]; + fTextureCoords = new float [fNumberVertices*2]; + + float *v = fGeometry; + float *t = fTextureCoords; + + float z0, z1, r0, r1; + float x, y, z; + //YVector3 normal; // not used in 3Dmov, but useful to know + + for (int i=0; i < stacks; i++) + { + z0 = (float)i/(float)stacks; + z1 = (float)(i+1)/(float)stacks; + r0 = radius * sind(180 * (float)i/(float)stacks); + r1 = radius * sind(180 * (float)(i+1)/(float)stacks); + + for (int j=0; j < (slices + 1); j++) + { + x = sind(360.0f * (float)j/(float)slices); + y = cosd(360.0f * (float)j/(float)slices); + z = radius * cosd(180*z0); + + // Vertices + *v++ = x * r0; + *v++ = -y * r0; + *v++ = z; + // Normal not used in 3Dmov, but if you ever need it + //normal.Set(x*r0, -y*r0, z); + //normal.Normalise(); + // Textures + *t++ = (float)j/(float)slices; + *t++ = z0; + + z = radius * cosd(180*z1); + // Vertices + *v++ = x * r1; + *v++ = -y * r1; + *v++ = z; + // Normals not used in 3Dmov, but if you ever need it + //normal.Set(x*r1, -y*r1, z); + //normal.Normalise(); + // Textures + *t++ = (float)j/(float)slices; + *t++ = z1; + } + } +} + +/* FUNCTION: Sphere :: ~Sphere + ARGUMENTS: n/a + RETURN: n/a + DESCRIPTION: Destructor. fMediaSource destroyed by ViewSphere +*/ +Sphere :: ~Sphere() +{ + delete [] fGeometry; + delete [] fTextureCoords; +} + +/* FUNCTION: Sphere :: SetAngle + ARGUMENTS: angle_y + angle_z + RETURN: n/a + DESCRIPTION: Rotate sphere by Euler angles +*/ +void Sphere :: SetAngle(float angle_y, float angle_z) +{ + fRotationY = angle_y; + fRotationZ = angle_z; +} + +/* FUNCTION: Sphere :: Render + ARGUMENTS: none + RETURN: n/a + DESCRIPTION: Draw sphere +*/ +void Sphere :: Render() +{ + glColor4f(1,1,1,1); + glBindTexture(GL_TEXTURE_2D, fMediaSource->mTextureID); + + glPushMatrix(); + glRotatef(-30, 1, 0, 0); + glRotatef(fRotationY, 0, 1, 0); + glRotatef(fRotationZ, 0, 0, 1); + glTexCoordPointer(2, GL_FLOAT, 0, fTextureCoords); + glVertexPointer(3, GL_FLOAT, 0, fGeometry); + glDrawArrays(GL_TRIANGLE_STRIP, 0, fNumberVertices); + glPopMatrix(); +} + +/******************************** + ViewSphere +*********************************/ + +/* FUNCTION: ViewSphere :: ViewSphere + ARGUMENTS: frame + RETURN: n/a + DESCRIPTION: Constructor +*/ +ViewSphere :: ViewSphere(BRect frame) + : ViewObject(frame) +{ + fStartTime = real_time_clock_usecs(); + fSphere = 0; + fMediaSource = 0; + fSpeedZ = 10.0f; + fSpeedY = 0.0f; + fAngleY = 0; + fAngleZ = 0; +} + +/* FUNCTION: ViewSphere :: ~ViewSphere + ARGUMENTS: n/a + RETURN: n/a + DESCRIPTION: Destructor +*/ +ViewSphere :: ~ViewSphere() +{ + delete fSphere; + if (fMediaSource != GetDefaultMediaSource()) + delete fMediaSource; +} + +/* FUNCTION: ViewSphere :: AttachedToWindow + ARGUMENTS: none + RETURN: n/a + DESCRIPTION: Hook function called when view attached to window (looper) +*/ +void ViewSphere :: AttachedToWindow(void) +{ + ViewObject::AttachedToWindow(); + + LockGL(); + glClearColor(0,0,0,1); + + fSphere = new Sphere(0.1f, 10, 10); + fMediaSource = GetDefaultMediaSource(); + fSphere->SetMediaSource(fMediaSource); + + UnlockGL(); +} + +/* FUNCTION: ViewSphere :: Render + ARGUMENTS: none + RETURN: n/a + DESCRIPTION: Draw view contents +*/ +void ViewSphere :: Render(void) +{ + LockGL(); + + bigtime_t current_time = real_time_clock_usecs(); + bigtime_t delta = current_time - fStartTime; + fStartTime = current_time; + + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + fAngleY += fSpeedY*(float)delta/1000000.0f; + fAngleZ += fSpeedZ*(float)delta/1000000.0f; + fSphere->SetAngle(fAngleY, fAngleZ); + + glPushMatrix(); + glTranslatef(0.0f, 0.0f, 0.15f); + fSphere->Render(); + glPopMatrix(); + + glFlush(); + SwapBuffers(); + + // Display frame rate + /* + static int fps = 0; + static int time_delta = 0; + fps++; + time_delta += delta; + if (time_delta > 1000000) + { + printf("%d fps\n", fps); + fps = 0; + time_delta = 0; + } + */ + UnlockGL(); +} + +/* FUNCTION: ViewSphere :: MouseDown + ARGUMENTS: p + RETURN: n/a + DESCRIPTION: Hook function called when mouse down detected +*/ +void ViewSphere :: MouseDown(BPoint p) +{ + // Determine mouse button + BMessage* msg = Window()->CurrentMessage(); + uint32 buttons; + + msg->FindInt32("buttons", (int32*)&buttons); + + if (buttons & B_PRIMARY_MOUSE_BUTTON) + { + SetMouseEventMask(B_POINTER_EVENTS, B_LOCK_WINDOW_FOCUS | B_NO_POINTER_HISTORY); + fMouseTracking = true; + fMousePosition = p; + } +} + +/* FUNCTION: ViewSphere :: MouseMoved + ARGUMENTS: p + transit + message + RETURN: n/a + DESCRIPTION: Hook function called when mouse move detected. + This demo is succeptable to Gimbal lock, but it doesn't really matter. +*/ +void ViewSphere :: MouseMoved(BPoint p, uint32 transit, const BMessage *message) +{ + if (fMouseTracking) + { + if (transit == B_INSIDE_VIEW) + { + fSpeedY = 5*(p.y - fMousePosition.y); + fSpeedZ = 5*(p.x - fMousePosition.x); + fMousePosition = p; + } + } +} + +/* FUNCTION: ViewSphere :: MouseUp + ARGUMENTS: p + RETURN: n/a + DESCRIPTION: Hook function called when mouse up detected +*/ +void ViewSphere :: MouseUp(BPoint p) +{ + fMouseTracking = false; +} + +/* FUNCTION: ViewSphere :: DragDropImage + ARGUMENTS: texture_id + mouse_x + mouse_y + RETURN: true if source ownership acquired + DESCRIPTION: Hook function called when user drags/drops image to app window +*/ +bool ViewSphere :: SurfaceUpdate(MediaSource *source, float mouse_x, float mouse_y) +{ + LockGL(); + + if (fMediaSource != GetDefaultMediaSource()) + delete fMediaSource; + fMediaSource = source; + fSphere->SetMediaSource(source); + + UnlockGL(); + + return true; + +} + + + + diff --git a/src/apps/3dmov/ViewSphere.h b/src/apps/3dmov/ViewSphere.h new file mode 100644 index 0000000000..abe8f92e0e --- /dev/null +++ b/src/apps/3dmov/ViewSphere.h @@ -0,0 +1,45 @@ +/* PROJECT: 3Dmov + AUTHORS: Zenja Solaja + COPYRIGHT: 2009 Haiku Inc + DESCRIPTION: Haiku version of the famous BeInc demo 3Dmov + Just drag'n'drop media files to the 3D objects +*/ + +#ifndef _VIEW_SPHERE_H_ +#define _VIEW_SPHERE_H_ + +#ifndef _VIEW_OBJECT_H_ +#include "ViewObject.h" +#endif + +class Sphere; + +/********************************** + ViewSphere displays a rotating sphere. +***********************************/ +class ViewSphere : public ViewObject +{ +public: + ViewSphere(BRect frame); + ~ViewSphere(); + + void AttachedToWindow(); + void MouseDown(BPoint); + void MouseMoved(BPoint p, uint32 transit, const BMessage *message); + void MouseUp(BPoint); + void Render(); + +protected: + bool SurfaceUpdate(MediaSource *source, float mouse_x, float mouse_y); + +private: + bigtime_t fStartTime; + MediaSource *fMediaSource; + Sphere *fSphere; + bool fMouseTracking; + BPoint fMousePosition; + float fAngleY, fAngleZ; + float fSpeedY, fSpeedZ; +}; + +#endif //#ifndef _VIEW_SPHERE_H_ diff --git a/src/apps/3dmov/instructions.png b/src/apps/3dmov/instructions.png new file mode 100644 index 0000000000..ae9712eadd Binary files /dev/null and b/src/apps/3dmov/instructions.png differ