
/*
 * perfdraw.c - $Revision: 1.1 $
 */

#include <GL/glut.h>
#include <math.h>
#include <stdio.h>
#include "skyfly.h"

#if !defined(GL_VERSION_1_1)
#if defined(GL_EXT_texture_object)
#define glBindTexture(A,B)     glBindTextureEXT(A,B)
#define glGenTextures(A,B)     glGenTexturesEXT(A,B)
#define glDeleteTextures(A,B)  glDeleteTexturesEXT(A,B)
#else
#define glBindTexture(A,B)
#define glGenTextures(A,B)
#define glDeleteTextures(A,B)
#endif
#endif

/* static routine decls */

extern int clouds;
static void drawlitmesh_11(float *objdata);
static void drawcolrtexmesh_10(float *objdata);
static void drawclouds(float *objdata);

void drawperfobj(perfobj_t *perfobj)
{
    float     *vdata_ptr =(float *) perfobj->vdata;
    extern void texenv(int), lightpos(void);

    unsigned int *flagsptr = perfobj->flags;
    float     *dp;

    for (;;) {
        switch (*flagsptr) {
		/*
		 * A paper plane is a single tmesh folded on itself so the orientations
		 * of some triangles in the mesh are incorrect with respect to
		 * their normals. This is solved by drawing the tmesh twice;
		 * first draw only backfaces, then only frontfaces.
		 */
		case PD_DRAW_PAPER_PLANE:
			flagsptr += 1;
			glCullFace(GL_FRONT);
			drawlitmesh_11(vdata_ptr);
			glCullFace(GL_BACK);
			drawlitmesh_11((float *)((perfobj_vert_t *) vdata_ptr + 11));
			glPopMatrix();
			break;

		case PD_DRAW_TERRAIN_CELL:
			dp = *(float **) (flagsptr + 1);
			flagsptr += 2;
			drawcolrtexmesh_10(dp);
			break;

		case PD_DRAW_CLOUDS:
                        if (rgbmode) {
#if 0
                            glColor3ub(0x30, 0x40, 0xb0);
#else
                            glColor3f(1.0f, 1.0f, 1.0f);
#endif
                        }
			glDisable(GL_CULL_FACE);
			glDisable(GL_DEPTH_TEST);
			/*texenv(2);*/
			drawclouds(vdata_ptr);
			glEnable(GL_DEPTH_TEST);
			glEnable(GL_CULL_FACE);
			flagsptr += 1;
			break;

		case PD_PAPER_PLANE_MODE:
			switch (*(flagsptr + 1)) {
			case PLANES_START:
                            glShadeModel(GL_FLAT);
                            glEnable(GL_LIGHTING);
                            glDisable(GL_TEXTURE_2D);
                            if (fog && !rgbmode)
                                glDisable(GL_FOG);
                            break;
			case PLANES_END:
                            glShadeModel(GL_SMOOTH);
                            glDisable(GL_LIGHTING);
                            if (fog && !rgbmode && FOG_LEVELS > 1)
                                glEnable(GL_FOG);
                            break;
                        }	
			flagsptr += 2;
			break;

		case PD_PAPER_PLANE_POS:        /* contains the pushmatrix */
			glPushMatrix();
			glTranslatef(*(vdata_ptr), *(vdata_ptr + 1), *(vdata_ptr + 2));
			glRotatef(*(vdata_ptr + 3), 0.0, 0.0, 1.0);
			glRotatef(*(vdata_ptr + 4), 0.0, 1.0, 0.0);
			glRotatef(*(vdata_ptr + 5), 1.0, 0.0, 0.0);
			flagsptr += 1;
			break;

		case PD_VIEWER_POS:
			glLoadIdentity();
			glRotatef(-90., 1.0, 0., 0.);
			glRotatef(*(vdata_ptr + 3) * RAD_TO_DEG, 0.0, 0.0, 1.0); /* yaw */
			lightpos();
			glTranslatef(-*(vdata_ptr), -*(vdata_ptr + 1), -*(vdata_ptr + 2));
			flagsptr += 1;
			break;

		case PD_TEXTURE_BIND:
			glBindTexture(GL_TEXTURE_2D, *(flagsptr + 1));
			texenv(*(flagsptr + 1));
			glEnable(GL_TEXTURE_2D);
			flagsptr += 2;
			break;

		case PD_END:
			return;

		default:
			fprintf(stderr, "Bad PD primitive %d\n", *flagsptr);
			flagsptr++;
			break;
		}
	}
}

/*
 * Notice how the following routines unwind loops and pre-compute indexes
 * at compile time. This is crucial in obtaining the maximum data transfer 
 * from cpu to the graphics pipe.
 */
static void drawlitmesh_11(float *op)
{
    glBegin(GL_TRIANGLE_STRIP);
    /* one */
    glNormal3fv((op + PD_V_NORMAL));
    glVertex3fv((op + PD_V_POINT));
    /* two */
    glNormal3fv((op + (PD_V_SIZE + PD_V_NORMAL)));
    glVertex3fv((op + (PD_V_SIZE + PD_V_POINT)));
    /* three */
    glNormal3fv((op + (2 * PD_V_SIZE + PD_V_NORMAL)));
    glVertex3fv((op + (2 * PD_V_SIZE + PD_V_POINT)));
    /* four */
    glNormal3fv((op + (3 * PD_V_SIZE + PD_V_NORMAL)));
    glVertex3fv((op + (3 * PD_V_SIZE + PD_V_POINT)));
    /* five */
    glNormal3fv((op + (4 * PD_V_SIZE + PD_V_NORMAL)));
    glVertex3fv((op + (4 * PD_V_SIZE + PD_V_POINT)));
    /* six */
    glNormal3fv((op + (5 * PD_V_SIZE + PD_V_NORMAL)));
    glVertex3fv((op + (5 * PD_V_SIZE + PD_V_POINT)));
    /* seven */
    glNormal3fv((op + (6 * PD_V_SIZE + PD_V_NORMAL)));
    glVertex3fv((op + (6 * PD_V_SIZE + PD_V_POINT)));
    /* eight */
    glNormal3fv((op + (7 * PD_V_SIZE + PD_V_NORMAL)));
    glVertex3fv((op + (7 * PD_V_SIZE + PD_V_POINT)));
    /* nine */
    glNormal3fv((op + (8 * PD_V_SIZE + PD_V_NORMAL)));
    glVertex3fv((op + (8 * PD_V_SIZE + PD_V_POINT)));
    /* ten */
    glNormal3fv((op + (9 * PD_V_SIZE + PD_V_NORMAL)));
    glVertex3fv((op + (9 * PD_V_SIZE + PD_V_POINT)));
    /* eleven */
    glNormal3fv((op + (10 * PD_V_SIZE + PD_V_NORMAL)));
    glVertex3fv((op + (10 * PD_V_SIZE + PD_V_POINT)));

	glEnd();

}

static void drawcolrtexmesh_10(float *op)
{
    glBegin(GL_TRIANGLE_STRIP);
	/* one */
    glTexCoord2fv((op + PD_V_TEX));
    glColor3fv((op + PD_V_COLOR));
    glVertex3fv((op + PD_V_POINT));
    /* two */
    glTexCoord2fv((op + (PD_V_SIZE + PD_V_TEX)));
    glColor3fv((op + (PD_V_SIZE + PD_V_COLOR)));
    glVertex3fv((op + (PD_V_SIZE + PD_V_POINT)));
    /* three */
    glTexCoord2fv((op + (2 * PD_V_SIZE + PD_V_TEX)));
    glColor3fv((op + (2 * PD_V_SIZE + PD_V_COLOR)));
    glVertex3fv((op + (2 * PD_V_SIZE + PD_V_POINT)));
    /* four */
    glTexCoord2fv((op + (3 * PD_V_SIZE + PD_V_TEX)));
    glColor3fv((op + (3 * PD_V_SIZE + PD_V_COLOR)));
    glVertex3fv((op + (3 * PD_V_SIZE + PD_V_POINT)));
    /* five */
    glTexCoord2fv((op + (4 * PD_V_SIZE + PD_V_TEX)));
    glColor3fv((op + (4 * PD_V_SIZE + PD_V_COLOR)));
    glVertex3fv((op + (4 * PD_V_SIZE + PD_V_POINT)));
    /* six */
    glTexCoord2fv((op + (5 * PD_V_SIZE + PD_V_TEX)));
    glColor3fv((op + (5 * PD_V_SIZE + PD_V_COLOR)));
    glVertex3fv((op + (5 * PD_V_SIZE + PD_V_POINT)));
    /* seven */
    glTexCoord2fv((op + (6 * PD_V_SIZE + PD_V_TEX)));
    glColor3fv((op + (6 * PD_V_SIZE + PD_V_COLOR)));
    glVertex3fv((op + (6 * PD_V_SIZE + PD_V_POINT)));
    /* eight */
    glTexCoord2fv((op + (7 * PD_V_SIZE + PD_V_TEX)));
    glColor3fv((op + (7 * PD_V_SIZE + PD_V_COLOR)));
    glVertex3fv((op + (7 * PD_V_SIZE + PD_V_POINT)));
    /* nine */
    glTexCoord2fv((op + (8 * PD_V_SIZE + PD_V_TEX)));
    glColor3fv((op + (8 * PD_V_SIZE + PD_V_COLOR)));
    glVertex3fv((op + (8 * PD_V_SIZE + PD_V_POINT)));
    /* ten */
    glTexCoord2fv((op + (9 * PD_V_SIZE + PD_V_TEX)));
    glColor3fv((op + (9 * PD_V_SIZE + PD_V_COLOR)));
    glVertex3fv((op + (9 * PD_V_SIZE + PD_V_POINT)));

    glEnd();
}

static void drawclouds(float *op)
{
#define SKY_STRIPS 24

	/* Break into quad strips so cheap fog works better */
	if (0 == clouds) {

        GLfloat *vc0, *vc1, *vc2;
        GLfloat *tc0, *tc1, *tc2;
        GLfloat sky_s0, sky_s1, sky_t0, sky_t1;
        GLfloat sky_x0, sky_x1, sky_y0, sky_y1, sky_z;
        int ii, jj;
        GLfloat s0, s1, t0, t1;
        GLfloat x0, x1, y0, y1, z0;

        vc0 = op + PD_V_POINT;
        vc1 = op + PD_V_POINT + PD_V_SIZE;
        vc2 = op + PD_V_POINT + PD_V_SIZE * 2;

        tc0 = op + PD_V_TEX;
        tc1 = op + PD_V_TEX + PD_V_SIZE;
        tc2 = op + PD_V_TEX + PD_V_SIZE * 2;

        sky_s0 = tc0[0];
        sky_s1 = tc1[0];
        sky_t0 = tc0[1];
        sky_t1 = tc2[1];

        sky_x0 = vc0[0];
        sky_x1 = vc1[0];
        sky_y0 = vc0[1];
        sky_y1 = vc2[1];
        sky_z  = vc0[2];

        clouds = glGenLists(1);

        glNewList(clouds, GL_COMPILE);

        s1 = (1.0f / SKY_STRIPS) * (sky_s1 - sky_s0);
        t1 = (1.0f / SKY_STRIPS) * (sky_t1 - sky_t0);
        x1 = (1.0f / SKY_STRIPS) * (sky_x1 - sky_x0);
        y1 = (1.0f / SKY_STRIPS) * (sky_y1 - sky_y0);

        z0 = sky_z;
        s0 = sky_s0;
        x0 = sky_x0;

        for (ii = 0; ii < SKY_STRIPS; ii++, s0 += s1, x0 += x1) {

            t0 = sky_t0;
            y0 = sky_y0;

            glBegin(GL_QUAD_STRIP);

            glTexCoord2f(s0, t0);
            glVertex3f(x0, y0, z0);

            glTexCoord2f(s0 + s1, t0);
            glVertex3f(x0 + x1, y0, z0);

            for (jj = 0; jj < SKY_STRIPS; jj++, t0 += t1, y0 += y1) {

                glTexCoord2f(s0 + s1, t0 + t1);
                glVertex3f(x0 + x1, y0 + y1, z0);

                glTexCoord2f(s0, t0 + t1);
                glVertex3f(x0, y0 + y1, z0);
            }

            glEnd();

        }

        glEndList();
    }

	glCallList(clouds);
}

void putv3fdata(float *v, perfobj_vert_t *ptr)
{
  ptr->vert[0] = v[0];
  ptr->vert[1] = v[1];
  ptr->vert[2] = v[2];
}

void putc3fdata(float *c, perfobj_vert_t *ptr)
{
  ptr->color[0] = c[0];
  ptr->color[1] = c[1];
  ptr->color[2] = c[2];
}

void putn3fdata(float *n, perfobj_vert_t *ptr)
{
  ptr->normal[0] = n[0];
  ptr->normal[1] = n[1];
  ptr->normal[2] = n[2];
}

void putt2fdata(float *t, perfobj_vert_t *ptr)
{
  ptr->texture[0] = t[0];
  ptr->texture[1] = t[1];
}

