root/src/apps/glteapot/GLObject.cpp
/*
 * 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 "GLObject.h"

#include <Application.h>
#include <GL/gl.h>
#include <InterfaceKit.h>
#include <Resources.h>

#include "glob.h"


struct material {
        float ambient[3], diffuse[3], specular[3];
};

float *colors[] = {NULL, white, yellow, blue, red, green};

material materials[] = {
        // Null
        {
                {0.1745, 0.03175, 0.03175},
                {0.61424, 0.10136, 0.10136},
                {0.727811, 0.626959, 0.626959}
        },
        // White
        {
                {0.1745, 0.1745, 0.1745},
                {0.61424, 0.61424, 0.61424},
                {0.727811, 0.727811, 0.727811}
        },
        // Yellow
        {
                {0.1745, 0.1745, 0.03175},
                {0.61424, 0.61424, 0.10136},
                {0.727811, 0.727811, 0.626959}
        },
        // Blue
        {
                {0.03175, 0.03175, 0.1745},
                {0.10136, 0.10136, 0.61424},
                {0.626959, 0.626959, 0.727811}
        },
        // Red
        {
                {0.1745, 0.03175, 0.03175},
                {0.61424, 0.10136, 0.10136},
                {0.727811, 0.626959, 0.626959}
        },
        // Green
        {
                {0.03175, 0.1745, 0.03175},
                {0.10136, 0.61424, 0.10136},
                {0.626959, 0.727811, 0.626959}
        },
};

#define USE_QUAD_STRIPS 1

extern long setEvent(sem_id event);


GLObject::GLObject(ObjectView* ov)
        :
        x(0),
        y(0),
        z(-2.0),
        fRotation(0.0f, 0.0f, 0.0f, 1.0f),
        spinX(2),
        spinY(2),
        solidity(0),
        color(4),
        changed(false),
        fObjView(ov)
{
}


GLObject::~GLObject()
{
}


void
GLObject::MenuInvoked(BPoint point)
{
        BPopUpMenu* m = new BPopUpMenu("Object",false,false);
        BMenuItem* i;

        int c = 1;
        m->AddItem(i = new BMenuItem("White",NULL));
        if (color == c++)
                i->SetMarked(true);
        m->AddItem(i = new BMenuItem("Yellow",NULL));
        if (color == c++)
                i->SetMarked(true);
        m->AddItem(i = new BMenuItem("Blue",NULL));
        if (color == c++)
                i->SetMarked(true);
        m->AddItem(i = new BMenuItem("Red",NULL));
        if (color == c++)
                i->SetMarked(true);
        m->AddItem(i = new BMenuItem("Green",NULL));
        if (color == c++)
                i->SetMarked(true);
        m->AddSeparatorItem();

        c = 0;
        m->AddItem(i = new BMenuItem("Solid",NULL));
        if (solidity == c++)
                i->SetMarked(true);
        m->AddItem(i = new BMenuItem("Translucent",NULL));
        if (solidity == c++)
                i->SetMarked(true);
        m->AddItem(i = new BMenuItem("Transparent",NULL));
        if (solidity == c++)
                i->SetMarked(true);

        i = m->Go(point);
        int32 index = m->IndexOf(i);
        delete m;

        if (index < 5) {
                color = index+1;
        } else if (index > 5) {
                solidity = index-6;
        }
        changed = true;
        setEvent(fObjView->drawEvent);
}


int
GLObject::Solidity() const
{
        return solidity;
}



bool
GLObject::SpinIt()
{
        bool c = changed;
        c = c || ((spinX != 0.0f) || (spinY != 0.0f));

        if (c)
                RotateWorldSpace(spinY, spinX);

        return c;
}


void
GLObject::Spin(float rx, float ry)
{
        spinX = rx;
        spinY = ry;
}


void
GLObject::RotateWorldSpace(float rx, float ry)
{
        fRotation = Quaternion(Vector3(0.0f, 1.0f, 0.0f), 0.01f * rx) * fRotation;
        fRotation = Quaternion(Vector3(1.0f, 0.0f, 0.0f), 0.01f * ry) * fRotation;
        fRotation.normalize();
        changed = true;
}


void
GLObject::Draw(bool forID, float IDcolor[])
{
        glPushMatrix();
                glTranslatef(x, y, z);

                float mat[4][4];
                fRotation.toOpenGLMatrix(mat);
                glMultMatrixf((GLfloat*)mat);

                if (forID) {
                        glColor3fv(IDcolor);
                }

                DoDrawing(forID);

        glPopMatrix();

        changed = false;
}


TriangleObject::TriangleObject(ObjectView* ov)
                :
                GLObject(ov),
                fStatus(B_NO_INIT),
                fPoints(100, 100),
                fTriangles(100, 100),
                fQs(50, 50)
{
        BResources *res = BApplication::AppResources();
        if (res == NULL)
                return;

        size_t size = 0;
        int32 *arrayOfPoints
                                        = (int32*)res->LoadResource(B_RAW_TYPE, "points", &size);
        if (arrayOfPoints == NULL)
                return;

        float maxp = 0;
        size_t numPt = size / sizeof(int32);
        for (size_t i = 0; i < numPt; i += 6) {
                point p;
                p.x = 1e-6 * arrayOfPoints[i];
                p.y = 1e-6 * arrayOfPoints[i + 1];
                p.z = 1e-6 * arrayOfPoints[i + 2];
                p.nx = 1e-6 * arrayOfPoints[i + 3];
                p.ny = 1e-6 * arrayOfPoints[i + 4];
                p.nz = 1e-6 * arrayOfPoints[i + 5];

                if (fabs(p.x) > maxp)
                        maxp = fabs(p.x);
                if (fabs(p.y) > maxp)
                        maxp = fabs(p.y);
                if (fabs(p.z) > maxp)
                        maxp = fabs(p.z);

                fPoints.add(p);
        }

        for (int i = 0; i < fPoints.num_items; i++) {
                fPoints[i].x /= maxp;
                fPoints[i].y /= maxp;
                fPoints[i].z /= maxp;
        }

        int32 *arrayOfTriangles
                                        = (int32*)res->LoadResource(B_RAW_TYPE, "triangles", &size);
        if (arrayOfTriangles == NULL)
                return;

        size_t numTriPoints = size / sizeof(int32);
        for (size_t i = 0; i < numTriPoints; i += 3) {
                tri t;
                t.p1 = arrayOfTriangles[i];
                t.p2 = arrayOfTriangles[i + 1];
                t.p3 = arrayOfTriangles[i + 2];
                fTriangles.add(t);
        }

        size_t numTri = numTriPoints / 3;

        int qpts = 4;
        int qp[1024];
        quadStrip q;
        q.pts = qp;
        q.numpts = 4;
        q.pts[2] = fTriangles[0].p1;
        q.pts[0] = fTriangles[0].p2;
        q.pts[1] = fTriangles[0].p3;
        q.pts[3] = fTriangles[1].p3;

        for (size_t i = 2; i < numTri; i += 2) {
                if ((fTriangles[i - 1].p1 == fTriangles[i].p2) &&
                        (fTriangles[i - 1].p3 == fTriangles[i].p3)) {
                        q.pts[q.numpts++] = fTriangles[i + 1].p1;
                        q.pts[q.numpts++] = fTriangles[i + 1].p3;
                        qpts+=2;
                } else {
                        int *np = (int*)malloc(sizeof(int)*q.numpts);
                        memcpy(np, qp, q.numpts * sizeof(int));
                        quadStrip nqs;
                        nqs.numpts = q.numpts;
                        nqs.pts = np;
                        fQs.add(nqs);

                        qpts += 4;
                        q.numpts = 4;
                        q.pts[2] = fTriangles[i].p1;
                        q.pts[0] = fTriangles[i].p2;
                        q.pts[1] = fTriangles[i].p3;
                        q.pts[3] = fTriangles[i + 1].p3;
                }
        }

        int* np = (int*)malloc(sizeof(int)*q.numpts);
        memcpy(np, qp, q.numpts * sizeof(int));
        quadStrip nqs;
        nqs.numpts = q.numpts;
        nqs.pts = np;
        fQs.add(nqs);

        fStatus = B_OK;
}


TriangleObject::~TriangleObject()
{
        for (int i = 0; i < fQs.num_items; i++) {
                free(fQs[i].pts);
        }
}


status_t
TriangleObject::InitCheck() const
{
        return fStatus;
}


void
TriangleObject::DoDrawing(bool forID)
{
        if (!forID) {
                float c[3][4];
                c[0][0] = materials[color].ambient[0];
                c[0][1] = materials[color].ambient[1];
                c[0][2] = materials[color].ambient[2];
                c[1][0] = materials[color].diffuse[0];
                c[1][1] = materials[color].diffuse[1];
                c[1][2] = materials[color].diffuse[2];
                c[2][0] = materials[color].specular[0];
                c[2][1] = materials[color].specular[1];
                c[2][2] = materials[color].specular[2];

                float alpha = 1;
                if (solidity == 0)
                        alpha = 1.0;
                else if (solidity == 1)
                        alpha = 0.95;
                else if (solidity == 2)
                        alpha = 0.6;
                c[0][3] = c[1][3] = c[2][3] = alpha;
                if (solidity != 0) {
                        glBlendFunc(GL_SRC_ALPHA,GL_ONE);
                        glEnable(GL_BLEND);
                        glDepthMask(GL_FALSE);
                        glDisable(GL_CULL_FACE);
                } else {
                        glDisable(GL_BLEND);
                        glDepthMask(GL_TRUE);
                }
                glMaterialfv(GL_FRONT, GL_AMBIENT, c[0]);
                glMaterialfv(GL_FRONT, GL_DIFFUSE, c[1]);
                glMaterialfv(GL_FRONT, GL_SPECULAR, c[2]);
        } else {
                glDisable(GL_BLEND);
                glDepthMask(GL_TRUE);
        }

#if USE_QUAD_STRIPS
                for (int i = 0; i < fQs.num_items; i++) {
                        glBegin(GL_QUAD_STRIP);
                        for (int j = 0; j < fQs[i].numpts; j++) {
                                glNormal3f(
                                        fPoints[fQs[i].pts[j]].nx,
                                        fPoints[fQs[i].pts[j]].ny,
                                        fPoints[fQs[i].pts[j]].nz
                                );
                                glVertex3f(
                                        fPoints[fQs[i].pts[j]].x,
                                        fPoints[fQs[i].pts[j]].y,
                                        fPoints[fQs[i].pts[j]].z
                                );
                        }
                        glEnd();
                }
#else
                glBegin(GL_TRIANGLES);
                for (int i = 0; i < fTriangles.num_items; i++) {
                        int v3 = fTriangles[i].p1;
                        int v1 = fTriangles[i].p2;
                        int v2 = fTriangles[i].p3;
                        glNormal3f(
                                fPoints[v1].nx,
                                fPoints[v1].ny,
                                fPoints[v1].nz
                        );
                        glVertex3f(
                                fPoints[v1].x,
                                fPoints[v1].y,
                                fPoints[v1].z
                        );
                        glNormal3f(
                                fPoints[v2].nx,
                                fPoints[v2].ny,
                                fPoints[v2].nz
                        );
                        glVertex3f(
                                fPoints[v2].x,
                                fPoints[v2].y,
                                fPoints[v2].z
                        );
                        glNormal3f(
                                fPoints[v3].nx,
                                fPoints[v3].ny,
                                fPoints[v3].nz
                        );
                        glVertex3f(
                                fPoints[v3].x,
                                fPoints[v3].y,
                                fPoints[v3].z
                        );
                }
                glEnd();
 #endif
}