/* Copyright (c) 2001, Stanford University
* All rights reserved
*
* See the file LICENSE.txt for information on redistributing this software.
*/
#include "server_dispatch.h"
#include "server.h"
#include "cr_error.h"
#include "state/cr_statetypes.h"
#include "cr_mem.h"
#include "cr_string.h"
/*
* This file provides implementations of the basic OpenGL matrix functions.
* We often need to twiddle with their operation in order to make tilesorting
* and non-planar projections work.
*/
/*
* Determine which view and projection matrices to use when in stereo mode.
* Return 0 = left eye, 1 = right eye.
*/
int crServerGetCurrentEye(void)
{
if (cr_server.currentEye != -1) {
/* current eye was specified by tilesort SPU */
return cr_server.currentEye;
}
else {
/* we have a quad-buffered window and we're watching glDrawBuffer */
GLenum drawBuffer = cr_server.curClient->currentCtxInfo->pContext->buffer.drawBuffer;
int eye = drawBuffer == GL_BACK_RIGHT || drawBuffer == GL_FRONT_RIGHT
|| drawBuffer == GL_RIGHT;
return eye;
}
}
void SERVER_DISPATCH_APIENTRY crServerDispatchLoadMatrixf( const GLfloat *m )
{
const GLenum matMode = cr_server.curClient->currentCtxInfo->pContext->transform.matrixMode;
const CRMuralInfo *mural = cr_server.curClient->currentMural;
crStateLoadMatrixf( m );
if (matMode == GL_MODELVIEW && cr_server.viewOverride) {
int eye = crServerGetCurrentEye();
crServerApplyViewMatrix(&cr_server.viewMatrix[eye]);
}
else {
cr_server.head_spu->dispatch_table.LoadMatrixf( m );
}
}
void SERVER_DISPATCH_APIENTRY crServerDispatchLoadMatrixd( const GLdouble *m )
{
const GLenum matMode = cr_server.curClient->currentCtxInfo->pContext->transform.matrixMode;
const CRMuralInfo *mural = cr_server.curClient->currentMural;
crStateLoadMatrixd( m );
if (matMode == GL_MODELVIEW && cr_server.viewOverride) {
int eye = crServerGetCurrentEye();
crServerApplyViewMatrix(&cr_server.viewMatrix[eye]);
}
else {
cr_server.head_spu->dispatch_table.LoadMatrixd( m );
}
}
void SERVER_DISPATCH_APIENTRY crServerDispatchMultMatrixf( const GLfloat *m )
{
const GLenum matMode = cr_server.curClient->currentCtxInfo->pContext->transform.matrixMode;
if (matMode == GL_PROJECTION && cr_server.projectionOverride) {
/* load the overriding projection matrix */
int eye = crServerGetCurrentEye();
crStateLoadMatrix( &cr_server.projectionMatrix[eye] );
}
else {
/* the usual case */
crStateMultMatrixf( m );
cr_server.head_spu->dispatch_table.MultMatrixf( m );
}
}
void SERVER_DISPATCH_APIENTRY crServerDispatchMultMatrixd( const GLdouble *m )
{
const GLenum matMode = cr_server.curClient->currentCtxInfo->pContext->transform.matrixMode;
if (matMode == GL_PROJECTION && cr_server.projectionOverride) {
/* load the overriding projection matrix */
int eye = crServerGetCurrentEye();
crStateLoadMatrix( &cr_server.projectionMatrix[eye] );
}
else {
/* the usual case */
crStateMultMatrixd( m );
cr_server.head_spu->dispatch_table.MultMatrixd( m );
}
}
void SERVER_DISPATCH_APIENTRY crServerDispatchLoadIdentity( void )
{
const GLenum matMode = cr_server.curClient->currentCtxInfo->pContext->transform.matrixMode;
const CRMuralInfo *mural = cr_server.curClient->currentMural;
crStateLoadIdentity();
if (matMode == GL_MODELVIEW && cr_server.viewOverride) {
int eye = crServerGetCurrentEye();
crServerApplyViewMatrix(&cr_server.viewMatrix[eye]);
}
else {
cr_server.head_spu->dispatch_table.LoadIdentity( );
}
}
/*
* The following code is used to deal with vertex programs.
* Basically, vertex programs might not directly use the usual
* OpenGL projection matrix to project vertices from eye coords to
* clip coords.
*
* If you're using Cg then the vertex programs it generates will have
* some comments that we can scan to figure out which program parameters
* contain the projection matrix.
* In this case, look at the Cg program code for a string like
* "ModelViewProj". Then set the crserver's 'vertprog_projection_param'
* config option to this name.
*
* If you're not using Cg, you may have to tell Chromium which program
* parameters contain the projection matrix.
* In this case, look at the OpenGL application's vertex program code to
* determine which program parameters contain the projection matrix.
* Then set the crserver's 'vertprog_projection_param' config option to
* the number of the parameter which holds the first row of the matrix.
*
* Yup, this is complicated.
*
*/
static void matmul(GLfloat r[16], const GLfloat p[16], const GLfloat q[16])
{
GLfloat tmp[16];
int i, j, k;
for (i = 0; i < 4; i++) {
for (j = 0; j < 4; j++) {
GLfloat dot = 0.0;
for (k = 0; k < 4; k++) {
dot += p[i+4*k] * q[k+4*j];
}
tmp[i+4*j] = dot;
}
}
for (i = 0; i < 16; i++)
r[i] = tmp[i];
}
static CRServerProgram *
LookupProgram(GLuint id)
{
CRServerProgram *prog = crHashtableSearch(cr_server.programTable, id);
if (!prog) {
prog = (CRServerProgram *) crAlloc(sizeof(CRServerProgram));
if (!prog)
return NULL;
prog->id = id;
prog->projParamStart = cr_server.vpProjectionMatrixParameter;
crHashtableAdd(cr_server.programTable, id, prog);
}
return prog;
}
void SERVER_DISPATCH_APIENTRY
crServerDispatchProgramLocalParameter4fARB(GLenum target, GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w)
{
#if 0
if (target == GL_VERTEX_PROGRAM_ARB) {
CRServerProgram *prog = LookupProgram(cr_server.currentProgram);
if (prog && prog->projParamStart != -1) {
if (index >= (GLuint) prog->projParamStart && index <= (GLuint) prog->projParamStart + 3) {
/* save the parameters as rows in the matrix */
const int i = index - prog->projParamStart;
prog->projMat[4*0+i] = x;
prog->projMat[4*1+i] = y;
prog->projMat[4*2+i] = z;
prog->projMat[4*3+i] = w;
}
/* When we get the 4th row (row==3) of the projection matrix we can
* then pre-multiply it by the base matrix and update the program
* parameters with the new matrix.
*/
if (index == (GLuint) (prog->projParamStart + 3)) {
const CRMuralInfo *mural = cr_server.curClient->currentMural;
const GLfloat *baseMat = (const GLfloat *) &(mural->extents[mural->curExtent].baseProjection);
int i;
GLfloat mat[16];
/* pre-mult the projection matrix by the base projection */
matmul(mat, baseMat, prog->projMat);
/* update the program parameters with the new matrix */
for (i = 0; i < 4; i++) {
cr_server.head_spu->dispatch_table.ProgramLocalParameter4fARB(target, index + i - 3, mat[4*0+i], mat[4*1+i], mat[4*2+i], mat[4*3+i]);
}
return; /* done */
}
}
}
#endif
/* if we get here, pass the call through unchanged */
cr_server.head_spu->dispatch_table.ProgramLocalParameter4fARB(target, index, x, y, z, w);
}
void SERVER_DISPATCH_APIENTRY
crServerDispatchProgramLocalParameter4dARB(GLenum target, GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w)
{
crServerDispatchProgramLocalParameter4fARB(target, index, (GLfloat) x, (GLfloat) y, (GLfloat) z, (GLfloat) w);
}
void SERVER_DISPATCH_APIENTRY
crServerDispatchProgramParameter4fNV(GLenum target, GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w)
{
#if 0
if (target == GL_VERTEX_PROGRAM_NV) {
CRServerProgram *prog = LookupProgram(cr_server.currentProgram);
if (prog && prog->projParamStart != -1) {
if (index >= (GLuint) prog->projParamStart && index <= (GLuint) prog->projParamStart + 3) {
/* save the parameters as rows in the matrix */
const int i = index - prog->projParamStart;
prog->projMat[4*0+i] = x;
prog->projMat[4*1+i] = y;
prog->projMat[4*2+i] = z;
prog->projMat[4*3+i] = w;
}
/* When we get the 4th row (row==3) of the projection matrix we can
* then pre-multiply it by the base matrix and update the program
* parameters with the new matrix.
*/
if (index == (GLuint) (prog->projParamStart + 3)) {
const CRMuralInfo *mural = cr_server.curClient->currentMural;
const GLfloat *baseMat = (const GLfloat *) &(mural->extents[mural->curExtent].baseProjection);
int i;
GLfloat mat[16];
/* pre-mult the projection matrix by the base projection */
matmul(mat, baseMat, prog->projMat);
/* update the program parameters with the new matrix */
for (i = 0; i < 4; i++) {
cr_server.head_spu->dispatch_table.ProgramParameter4fNV(target, index + i - 3, mat[4*0+i], mat[4*1+i], mat[4*2+i], mat[4*3+i]);
}
return; /* done */
}
}
}
#endif
/* if we get here, pass the call through unchanged */
cr_server.head_spu->dispatch_table.ProgramParameter4fNV(target, index, x, y, z, w);
}
void SERVER_DISPATCH_APIENTRY
crServerDispatchProgramParameter4dNV(GLenum target, GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w)
{
crServerDispatchProgramParameter4fNV(target, index, (GLfloat) x, (GLfloat) y, (GLfloat) z, (GLfloat) w);
}
void SERVER_DISPATCH_APIENTRY
crServerDispatchProgramStringARB(GLenum target, GLenum format, GLsizei len, const GLvoid *string)
{
if (target == GL_VERTEX_PROGRAM_ARB &&
cr_server.vpProjectionMatrixVariable != NULL) {
/* scan the program string looking for 'vertprog_projection'
* If the program was generated by Cg, the info we want will look
* something like this:
* #var float4x4 ModelViewProj : : c[0], 4 : 1 : 1
*/
CRServerProgram *prog = LookupProgram(cr_server.currentProgram);
CRASSERT(prog);
if (prog) {
const char *varPos, *paramPos;
varPos = crStrstr((const char *) string, cr_server.vpProjectionMatrixVariable);
if (varPos) {
paramPos = crStrstr(varPos, "c[");
if (paramPos) {
char number[10];
int i = 0;
paramPos += 2; /* skip "c[" */
while (crIsDigit(paramPos[i])) {
number[i] = paramPos[i];
i++;
}
number[i] = 0;
prog->projParamStart = crStrToInt(number);
}
}
else {
crWarning("Didn't find %s parameter in vertex program string",
cr_server.vpProjectionMatrixVariable);
}
}
}
/* pass through */
crStateProgramStringARB(target, format, len, string);
cr_server.head_spu->dispatch_table.ProgramStringARB(target, format, len, string);
}
void SERVER_DISPATCH_APIENTRY
crServerDispatchLoadProgramNV(GLenum target, GLuint id, GLsizei len, const GLubyte *string)
{
if (target == GL_VERTEX_PROGRAM_NV &&
cr_server.vpProjectionMatrixVariable != NULL) {
/* scan the program string looking for 'vertprog_projection'
* If the program was generated by Cg, the info we want will look
* something like this:
* #var float4x4 ModelViewProj : : c[0], 4 : 1 : 1
*/
CRServerProgram *prog = LookupProgram(id);
CRASSERT(prog);
if (prog) {
const char *varPos, *paramPos;
varPos = crStrstr((const char *) string, cr_server.vpProjectionMatrixVariable);
if (varPos) {
paramPos = crStrstr(varPos, "c[");
if (paramPos) {
char number[10];
int i = 0;
paramPos += 2; /* skip "c[" */
while (crIsDigit(paramPos[i])) {
number[i] = paramPos[i];
i++;
}
number[i] = 0;
prog->projParamStart = crStrToInt(number);
}
}
else {
crWarning("Didn't find %s parameter in vertex program string",
cr_server.vpProjectionMatrixVariable);
}
}
}
/* pass through */
crStateLoadProgramNV(target, id, len, string);
cr_server.head_spu->dispatch_table.LoadProgramNV(target, id, len, string);
}
void SERVER_DISPATCH_APIENTRY
crServerDispatchBindProgramARB(GLenum target, GLuint id)
{
id = crServerTranslateProgramID(id);
if (target == GL_VERTEX_PROGRAM_ARB) {
CRServerProgram *prog = LookupProgram(id);
(void) prog;
cr_server.currentProgram = id;
}
/* pass through */
crStateBindProgramARB(target, id);
cr_server.head_spu->dispatch_table.BindProgramARB(target, id);
}
void SERVER_DISPATCH_APIENTRY
crServerDispatchBindProgramNV(GLenum target, GLuint id)
{
if (target == GL_VERTEX_PROGRAM_NV) {
CRServerProgram *prog = LookupProgram(id);
(void) prog;
cr_server.currentProgram = id;
}
/* pass through */
crStateBindProgramNV(target, id);
cr_server.head_spu->dispatch_table.BindProgramNV(target, id);
}