root/src/apps/haiku3d/MeshInstance.cpp
/*
 * Copyright 2008, Haiku Inc. All rights reserved.
 * Distributed under the terms of the MIT License.
 *
 * Authors:
 *              Alexandre Deckner <alex@zappotek.com>
 */

#include "MeshInstance.h"
#include "MathUtils.h"
#include <GL/gl.h>

//#include <stdio.h> // debug


MeshInstance::MeshInstance(Mesh* mesh, Texture* texture,
        const Vector3& position, const Quaternion& orientation, float animOffset)
        :
        fMeshReference(mesh),
        fTextureReference(texture),
        fPosition(position),
        fOrientation(orientation),
        fScale(1.0f),
        fTime(0.0f),
        fAnimOffset(animOffset),
        fDoubleSided(true),
        fDrawNormals(false)
{
}


MeshInstance::~MeshInstance()
{
}


void
MeshInstance::Update(float dt)
{
        fTextureReference->Update(dt);

        float animDuration = 4.0f;

        fTime += dt;
        if (fTime >= fAnimOffset) {
                float animTime = (fTime - fAnimOffset);

                float rotAngle = MathUtils::EaseInOutQuart(animTime, 0,
                        3 * 2 * 3.14159, animDuration);

                fOrientation = Quaternion(Vector3(0.0f, 1.0f, 0.0f), rotAngle);
        }

        if (fTime >= fAnimOffset + animDuration) {
                fOrientation = Quaternion(Vector3(0.0f, 1.0f, 0.0f), 0.0);
        }

        if (fTime >= animDuration * 6) {
                fTime = 0.0;
        }
}


void
MeshInstance::Render()
{
        if (fMeshReference->FaceCount() == 0)
                return;

        glPushMatrix();
        glTranslatef(fPosition.x(), fPosition.y(), fPosition.z());
        float mat[4][4];
        fOrientation.toOpenGLMatrix(mat);
        glMultMatrixf((GLfloat*) mat);
        glScalef(fScale, fScale, fScale);
        glBindTexture(GL_TEXTURE_2D, fTextureReference->Id());

        int lastVertexCount = 0;

        for (uint32 i = 0; i < fMeshReference->FaceCount(); i++) {

                const Face& face = fMeshReference->GetFace(i);

                // switch face mode
                if (face.vertexCount != lastVertexCount) {
                        if (lastVertexCount != 0)
                                glEnd();

                        if (face.vertexCount == 3)
                                glBegin(GL_TRIANGLES);
                        else
                                glBegin(GL_QUADS);
                }

                // calculate normal
                Vector3 lu(face.v[0].p - face.v[1].p);
                Vector3 lv(face.v[1].p - face.v[2].p);
                Vector3 normal(lu.cross(lv));
                if (normal.length() <= 0.000001)
                        normal.setValue(0, 0, -1.0);
                normal.normalize();

                // draw face
                glNormal3f(normal.x(), normal.y(),  normal.z());
                glTexCoord2f(face.v[0].u, face.v[0].v);
                glVertex3f(face.v[0].p.x(), face.v[0].p.y(), face.v[0].p.z());

                glNormal3f(normal.x(), normal.y(),  normal.z());
                glTexCoord2f(face.v[1].u, face.v[1].v);
                glVertex3f(face.v[1].p.x(), face.v[1].p.y(), face.v[1].p.z());

                glNormal3f(normal.x(), normal.y(),  normal.z());
                glTexCoord2f(face.v[2].u, face.v[2].v);
                glVertex3f(face.v[2].p.x(), face.v[2].p.y(), face.v[2].p.z());

                if (face.vertexCount == 4) {
                        glNormal3f(normal.x(), normal.y(), normal.z());
                        glTexCoord2f(face.v[3].u, face.v[3].v);
                        glVertex3f(face.v[3].p.x(), face.v[3].p.y(), face.v[3].p.z());
                }

                if (fDoubleSided) {
                        if (face.vertexCount == 4) {
                                glNormal3f(-normal.x(), -normal.y(), -normal.z());
                                glTexCoord2f(face.v[3].u, face.v[3].v);
                                glVertex3f(face.v[3].p.x(), face.v[3].p.y(), face.v[3].p.z());
                        }

                        glNormal3f(-normal.x(), -normal.y(), -normal.z());
                        glTexCoord2f(face.v[2].u, face.v[2].v);
                        glVertex3f(face.v[2].p.x(), face.v[2].p.y(), face.v[2].p.z());

                        glNormal3f(-normal.x(), -normal.y(), -normal.z());
                        glTexCoord2f(face.v[1].u, face.v[1].v);
                        glVertex3f(face.v[1].p.x(), face.v[1].p.y(), face.v[1].p.z());

                        glNormal3f(-normal.x(), -normal.y(), -normal.z());
                        glTexCoord2f(face.v[0].u, face.v[0].v);
                        glVertex3f(face.v[0].p.x(), face.v[0].p.y(), face.v[0].p.z());
                }
                lastVertexCount = face.vertexCount;
        }
        glEnd();

        if (fDrawNormals) {
                glBegin(GL_LINES);
                for (uint32 i = 0; i < fMeshReference->FaceCount(); i++) {

                        const Face& face = fMeshReference->GetFace(i);

                        if (face.vertexCount == 4) {

                                // calculate normal
                                Vector3 lu(face.v[0].p - face.v[1].p);
                                Vector3 lv(face.v[1].p - face.v[2].p);
                                Vector3 normal(lu.cross(lv));
                                if (normal.length() <= 0.000001)
                                        normal.setValue(0, 0, -1.0);
                                normal.normalize();

                                // center of the face
                                Vector3 g;
                                if (face.vertexCount == 4)
                                        g = (face.v[0].p + face.v[1].p + face.v[2].p + face.v[3].p)
                                                / 4.0;
                                else
                                        g = (face.v[0].p + face.v[1].p + face.v[2].p) / 3.0;

                                Vector3 h(g + normal);

                                glVertex3f(g.x(), g.y(), g.z());
                                glVertex3f(h.x(), h.y(), h.z());
                        }
                }
                glEnd();
        }

        glPopMatrix();
}