git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@35341 a95241bf-73f2-0310-859d-f6bbb57e9c96
526 lines
12 KiB
C++
526 lines
12 KiB
C++
/* 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 <stdio.h>
|
|
#include <assert.h>
|
|
|
|
#include <Bitmap.h>
|
|
|
|
#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;
|
|
}
|
|
|
|
|