/* $Id: stereo.c,v 1.6 2005/04/23 07:49:13 gregcouch Exp $ */

/* 
 * Togl - a Tk OpenGL widget
 * Copyright (C) 1996-1997  Brian Paul and Ben Bederson
 * See the LICENSE file for copyright details.
 */

#include "togl.h"
#include <stdlib.h>
#include <string.h>

/* 
 * The following variable is a special hack that is needed in order for
 * Sun shared libraries to be used for Tcl.
 */
#ifdef SUN
extern int matherr();
int    *tclDummyMathPtr = (int *) matherr;
#endif


static GLuint FontBase;
static float xAngle = 0.0, yAngle = 0.0, zAngle = 0.0;
static GLfloat CornerX, CornerY, CornerZ;       /* where to print strings */
static GLfloat scale = 1.0;



/* 
 * Togl widget create callback.  This is called by Tcl/Tk when the widget has
 * been realized.  Here's where one may do some one-time context setup or
 * initializations.
 */
void
create_cb(Togl *togl)
{
    FontBase = Togl_LoadBitmapFont(togl, TOGL_BITMAP_8_BY_13);
    if (!FontBase) {
        printf("Couldn't load font!\n");
        exit(1);
    }
}


/* 
 * Togl widget reshape callback.  This is called by Tcl/Tk when the widget
 * has been resized.  Typically, we call glViewport and perhaps setup the
 * projection matrix.
 */
void
reshape_cb(Togl *togl)
{
    int     width = Togl_Width(togl);
    int     height = Togl_Height(togl);
    float   aspect = (float) width / (float) height;

    glViewport(0, 0, width, height);

    /* Set up projection transform */
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glFrustum(-aspect, aspect, -1.0, 1.0, 1.0, 10.0);

    CornerX = -aspect;
    CornerY = -1.0;
    CornerZ = -1.1;

    /* Change back to model view transform for rendering */
    glMatrixMode(GL_MODELVIEW);
}



static void
print_string(const char *s)
{
    glCallLists(strlen(s), GL_UNSIGNED_BYTE, s);
}


/* 
 * Togl widget display callback.  This is called by Tcl/Tk when the widget's
 * contents have to be redrawn.  Typically, we clear the color and depth
 * buffers, render our objects, then swap the front/back color buffers.
 */
void
display_cb(Togl *togl)
{
    const char *ident;
    GLfloat eyeDist = 2.0;
    GLfloat eyeOffset = 0.05;

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    glLoadIdentity();           /* Reset modelview matrix to the identity
                                 * matrix */
    glTranslatef(0.0, 0.0, -3.0);       /* Move the camera back three units */
    glScalef(scale, scale, scale);      /* Zoom in and out */
    glRotatef(xAngle, 1.0, 0.0, 0.0);   /* Rotate by X, Y, and Z angles */
    glRotatef(yAngle, 0.0, 1.0, 0.0);
    glRotatef(zAngle, 0.0, 0.0, 1.0);

    glEnable(GL_DEPTH_TEST);

    /* stereo right eye */
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    Togl_StereoFrustum(-1, 1, -1, 1, 1, 10, eyeDist, eyeOffset);
    glMatrixMode(GL_MODELVIEW);
#ifdef OLD_STEREO
    Togl_OldStereoDrawBuffer(GL_BACK_RIGHT);
    Togl_OldStereoClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
#else
    glDrawBuffer(GL_BACK_RIGHT);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
#endif

    /* Front face */
    glBegin(GL_QUADS);
    glColor3f(0.0, 0.7, 0.1);   /* Green */
    glVertex3f(-1.0, 1.0, 1.0);
    glVertex3f(1.0, 1.0, 1.0);
    glVertex3f(1.0, -1.0, 1.0);
    glVertex3f(-1.0, -1.0, 1.0);
    /* Back face */
    glColor3f(0.9, 1.0, 0.0);   /* Yellow */
    glVertex3f(-1.0, 1.0, -1.0);
    glVertex3f(1.0, 1.0, -1.0);
    glVertex3f(1.0, -1.0, -1.0);
    glVertex3f(-1.0, -1.0, -1.0);
    /* Top side face */
    glColor3f(0.2, 0.2, 1.0);   /* Blue */
    glVertex3f(-1.0, 1.0, 1.0);
    glVertex3f(1.0, 1.0, 1.0);
    glVertex3f(1.0, 1.0, -1.0);
    glVertex3f(-1.0, 1.0, -1.0);
    /* Bottom side face */
    glColor3f(0.7, 0.0, 0.1);   /* Red */
    glVertex3f(-1.0, -1.0, 1.0);
    glVertex3f(1.0, -1.0, 1.0);
    glVertex3f(1.0, -1.0, -1.0);
    glVertex3f(-1.0, -1.0, -1.0);
    glEnd();

    /* stereo left eye */
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    Togl_StereoFrustum(-1, 1, -1, 1, 1, 10, eyeDist, -eyeOffset);
    glMatrixMode(GL_MODELVIEW);

#ifdef OLD_STEREO
    Togl_OldStereoDrawBuffer(GL_BACK_LEFT);
    Togl_OldStereoClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
#else
    glDrawBuffer(GL_BACK_LEFT);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
#endif

    /* Front face */
    glBegin(GL_QUADS);
    glColor3f(0.0, 0.7, 0.1);   /* Green */
    glVertex3f(-1.0, 1.0, 1.0);
    glVertex3f(1.0, 1.0, 1.0);
    glVertex3f(1.0, -1.0, 1.0);
    glVertex3f(-1.0, -1.0, 1.0);
    /* Back face */
    glColor3f(0.9, 1.0, 0.0);   /* Yellow */
    glVertex3f(-1.0, 1.0, -1.0);
    glVertex3f(1.0, 1.0, -1.0);
    glVertex3f(1.0, -1.0, -1.0);
    glVertex3f(-1.0, -1.0, -1.0);
    /* Top side face */
    glColor3f(0.2, 0.2, 1.0);   /* Blue */
    glVertex3f(-1.0, 1.0, 1.0);
    glVertex3f(1.0, 1.0, 1.0);
    glVertex3f(1.0, 1.0, -1.0);
    glVertex3f(-1.0, 1.0, -1.0);
    /* Bottom side face */
    glColor3f(0.7, 0.0, 0.1);   /* Red */
    glVertex3f(-1.0, -1.0, 1.0);
    glVertex3f(1.0, -1.0, 1.0);
    glVertex3f(1.0, -1.0, -1.0);
    glVertex3f(-1.0, -1.0, -1.0);
    glEnd();


    glDisable(GL_DEPTH_TEST);
    glLoadIdentity();
    glColor3f(1.0, 1.0, 1.0);
    glRasterPos3f(CornerX, CornerY, CornerZ);
    glListBase(FontBase);
    /* ident = Togl_Ident( togl ); if (strcmp(ident,"Single")==0) {
     * print_string( "Single buffered" ); } else { print_string( "Double
     * buffered" ); } */
    print_string(Togl_Ident(togl));
    Togl_SwapBuffers(togl);
}


int
setXrot_cb(Togl *togl, int argc, CONST84 char *argv[])
{
    Tcl_Interp *interp = Togl_Interp(togl);

    /* error checking */
    if (argc != 3) {
        Tcl_SetResult(interp,
                "wrong # args: should be \"pathName setXrot ?angle?\"",
                TCL_STATIC);
        return TCL_ERROR;
    }

    xAngle = atof(argv[2]);

    /* printf( "before %f ", xAngle ); */

    if (xAngle < 0.0) {
        xAngle += 360.0;
    } else if (xAngle > 360.0) {
        xAngle -= 360.0;
    }

    /* printf( "after %f \n", xAngle ); */

    Togl_PostRedisplay(togl);

    /* Let result string equal value */
    strcpy(interp->result, argv[2]);
    return TCL_OK;
}


int
setYrot_cb(Togl *togl, int argc, CONST84 char *argv[])
{
    Tcl_Interp *interp = Togl_Interp(togl);

    /* error checking */
    if (argc != 3) {
        Tcl_SetResult(interp,
                "wrong # args: should be \"pathName setYrot ?angle?\"",
                TCL_STATIC);
        return TCL_ERROR;
    }

    yAngle = atof(argv[2]);

    if (yAngle < 0.0) {
        yAngle += 360.0;
    } else if (yAngle > 360.0) {
        yAngle -= 360.0;
    }

    Togl_PostRedisplay(togl);

    /* Let result string equal value */
    strcpy(interp->result, argv[2]);
    return TCL_OK;
}


int
getXrot_cb(ClientData clientData, Tcl_Interp *interp,
        int argc, CONST84 char *argv[])
{
    sprintf(interp->result, "%d", (int) xAngle);
    return TCL_OK;
}


int
getYrot_cb(ClientData clientData, Tcl_Interp *interp,
        int argc, CONST84 char *argv[])
{
    sprintf(interp->result, "%d", (int) yAngle);
    return TCL_OK;
}


int
scale_cb(Togl *togl, int argc, CONST84 char *argv[])
{
    Tcl_Interp *interp = Togl_Interp(togl);

    /* error checking */
    if (argc != 3) {
        Tcl_SetResult(interp,
                "wrong # args: should be \"pathName scale ?value?\"",
                TCL_STATIC);
        return TCL_ERROR;
    }

    scale = atof(argv[2]);

    Togl_PostRedisplay(togl);

    /* Let result string equal value */
    strcpy(interp->result, argv[2]);
    return TCL_OK;
}


TOGL_EXTERN int
Stereo_Init(Tcl_Interp *interp)
{
    /* 
     * Initialize Tcl, Tk, and the Togl widget module.
     */
#ifdef USE_TCL_STUBS
    if (Tcl_InitStubs(interp, "8.1", 0) == NULL) {
        return TCL_ERROR;
    }
#endif
#ifdef USE_TK_STUBS
    if (Tk_InitStubs(interp, "8.1", 0) == NULL) {
        return TCL_ERROR;
    }
#endif

    if (Togl_Init(interp) == TCL_ERROR) {
        return TCL_ERROR;
    }

    /* 
     * Specify the C callback functions for widget creation, display,
     * and reshape.
     */
    Togl_CreateFunc(create_cb);
    Togl_DisplayFunc(display_cb);
    Togl_ReshapeFunc(reshape_cb);

    /* 
     * Make a new Togl widget command so the Tcl code can set a C variable.
     */

    Togl_CreateCommand("setXrot", setXrot_cb);
    Togl_CreateCommand("setYrot", setYrot_cb);
    Togl_CreateCommand("scale", scale_cb);

    /* 
     * Call Tcl_CreateCommand for application-specific commands, if
     * they weren't already created by the init procedures called above.
     */

    Tcl_CreateCommand(interp, "getXrot", getXrot_cb, (ClientData) NULL,
            (Tcl_CmdDeleteProc *) NULL);
    Tcl_CreateCommand(interp, "getYrot", getYrot_cb, (ClientData) NULL,
            (Tcl_CmdDeleteProc *) NULL);

    return TCL_OK;
}