Skip to content
Snippets Groups Projects
textures.c 7.88 KiB
Newer Older
/*
 * OpenGL texture (etc.) handling.
 * Stuart Levy, slevy@ncsa.uiuc.edu
 * National Center for Supercomputing Applications,
 * University of Illinois 2001.
 * This file is part of partiview, released under the
 * Illinois Open Source License; see the file LICENSE.partiview for details.
teuben's avatar
teuben committed
#ifdef WIN32
# include "winjunk.h"
#else /*unix?*/
#include <unistd.h>
#endif /*!WIN32*/
#include <stdlib.h>
#include <stdio.h>

#ifdef __APPLE__
#include <OpenGL/gl.h>
#include <OpenGL/glu.h>
#else
#include <GL/gl.h>    /* for GLuint */
teuben's avatar
teuben committed
#include <GL/glu.h>
teuben's avatar
teuben committed
#include <string.h>
#include "textures.h"
#include "shmem.h"
#include "findfile.h"

#if sgi && mips
# define glBindTexture  glBindTextureEXT
# define glGenTextures  glGenTexturesEXT
#endif

teuben's avatar
teuben committed
#if CAVE
#include "vd_util.h"
#endif

extern int mg_inhaletexture( Texture *tx, int rgba );

static int dspcontext = 0;

void set_dsp_context( int ctxno )
{
    if(ctxno < 0 || ctxno >= MAXDSPCTX) {
	fprintf(stderr, "textures: set_dsp_context(%d): context out of range!\n", ctxno);
	ctxno = MAXDSPCTX - 1;
    }
    dspcontext = ctxno;
}

int get_dsp_context(void)
teuben's avatar
teuben committed
{
#if CAVE
    /* assume virdir */
    int id = CAT_get_display_slot();
    if(id < 0 || id >= MAXDSPCTX) id = MAXDSPCTX-1;
teuben's avatar
teuben committed
    return id;
#else /* stand-alone -- single GLX context */
teuben's avatar
teuben committed
#endif
}

#define TEXTURENO(enab)  ((enab) & 0xFFFFFF)
#define	BLEND_ON	 0x40000000
#define	BLEND_OFF	 0x20000000

teuben's avatar
teuben committed
int txbind( Texture *tx, int *enabled )
{
  static GLint format[5] =
	{ 0, GL_LUMINANCE, GL_LUMINANCE_ALPHA, GL_RGB, GL_RGBA };
  static GLfloat minfilts[] = {
	GL_NEAREST, GL_LINEAR,
	GL_NEAREST, GL_LINEAR,
	GL_NEAREST_MIPMAP_NEAREST, GL_NEAREST_MIPMAP_LINEAR, 
	GL_LINEAR_MIPMAP_NEAREST, GL_LINEAR_MIPMAP_LINEAR
  };
slevy's avatar
 
slevy committed
  static int ctx = -1;
  static int txenv = -1;
slevy's avatar
 
slevy committed
  int ena = -1;
slevy's avatar
slevy committed
  int txtarget;
  static int any3d = 0;
slevy's avatar
 
slevy committed
  int mustload;
  int wanted, wantenv;
teuben's avatar
teuben committed

  if(tx == NULL || txload(tx) == 0
#ifndef GL_TEXTURE_3D
	|| tx->flags & TXF_3D
#endif
		) {
slevy's avatar
 
slevy committed
    /* reset state, invalidate cache */
    if(enabled == NULL || *enabled != 0) {
	glBindTexture( GL_TEXTURE_2D, 0 );
	glDisable( GL_TEXTURE_2D );
slevy's avatar
slevy committed
	if(any3d) {
	    glDisable( GL_TEXTURE_3D );
	    glBindTexture( GL_TEXTURE_3D, 0 );
	}
	glDisable( GL_ALPHA_TEST );
    }
    txenv = -1;
slevy's avatar
 
slevy committed
    if(enabled) *enabled = 0;
teuben's avatar
teuben committed
    return 0;
  }

#ifdef GL_TEXTURE_3D
  txtarget = (tx->flags & TXF_3D) ? GL_TEXTURE_3D_EXT : GL_TEXTURE_2D;
slevy's avatar
slevy committed

#else
  txtarget = GL_TEXTURE_2D;
#endif

  if(tx->flags & TXF_3D)
slevy's avatar
slevy committed
      any3d = 1;

slevy's avatar
 
slevy committed
  if(ctx < 0)
    ctx = get_dsp_context();
teuben's avatar
teuben committed
  mustload = (tx->txid[ctx] == 0);
slevy's avatar
 
slevy committed

#if unix
  if(tx->report & (1<<ctx)) {
    fprintf(stderr, "tx %x pid %d ctx %d txid[ctx] %d data %x loaded %d isTexture %d\n",
	tx, getpid(), ctx, tx->txid[ctx], tx->data, tx->loaded,
	glIsTexture(tx->txid[ctx]));
    tx->report &= ~(1<<ctx);
  }
#endif
  wantblend = (tx->flags & (TXF_ALPHA|TXF_INTENSITY|TXF_ADD)) ? BLEND_ON : BLEND_OFF;
slevy's avatar
 
slevy committed

  wantenv =
	  tx->apply == TXF_DECAL ? GL_DECAL
	: tx->apply == TXF_BLEND ? GL_BLEND
				 : GL_MODULATE;

  if(txenv != wantenv) {
    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, wantenv);
    txenv = wantenv;
  }

  if(!mustload && TEXTURENO(*enabled) == tx->txid[ctx])
slevy's avatar
 
slevy committed
    return *enabled;		/* short-circuit! */

teuben's avatar
teuben committed
  if(mustload) {
    GLuint id;
    glGenTextures( 1, &id );
teuben's avatar
teuben committed
    tx->txid[ctx] = id;
  }


  wanted = tx->txid[ctx] | wantblend;
teuben's avatar
teuben committed

slevy's avatar
slevy committed
  glBindTexture( txtarget, TEXTURENO(wanted) );
teuben's avatar
teuben committed

  if(mustload) {
    if(tx->data == NULL) {
	fprintf(stderr,
	    "Hey! Why are we having to reload the data for texture \"%s\"?\n",
	    tx->filename);
	tx->loaded = 0;
	txload(tx);
	wanted = (tx->data != NULL);
    }

slevy's avatar
 
slevy committed

teuben's avatar
teuben committed
    if(wanted) {
	static float black[4] = {0,0,0,0};
	unsigned char *txdata = (unsigned char *)tx->data;
slevy's avatar
 
slevy committed
	int channels = tx->channels;

	if((tx->flags&TXF_INTENSITY) && (channels == 1 || channels == 3)) {
	    int npix = tx->xsize * tx->ysize;
	    int k;
slevy's avatar
 
slevy committed
	    channels++;
	    txdata = (unsigned char *)malloc( npix * channels );
	    k = npix;
	    ip = (unsigned char *)tx->data;
	    op = txdata;
slevy's avatar
 
slevy committed
	    switch(channels) {
	    case 2:
		do {
		    op[1] = *ip++;
		    op[0] = 255;
		    op += 2;
		} while(--k > 0);
		break;
	    case 4:
		do {
		    op[3] = 77*ip[0] + 150*ip[1] + 28*ip[2];
		    op[0] = ip[0]>=op[3] ? 255 : ip[0]/op[3];
		    op[1] = ip[1]>=op[3] ? 255 : ip[0]/op[3];
		    op[2] = ip[2]>=op[3] ? 255 : ip[2]/op[3];
slevy's avatar
 
slevy committed
		    ip += 3;
		    op += 4;
		} while(--k > 0);
		break;
	    }
	}

	if(tx->xsize % 4 != 0)
	    glPixelStorei( GL_PACK_ALIGNMENT, 1 );
slevy's avatar
slevy committed

	if(tx->flags & TXF_3D) {
slevy's avatar
slevy committed

	    tx->qualflags &= ~TXQ_MIPMAP;
	    glTexImage3D( GL_TEXTURE_3D, 0, channels,
		    tx->xsize, tx->ysize, tx->zsize, 
		    0, /* border */
		    channels==1 && (tx->flags&TXF_ALPHA)
			? GL_ALPHA : format[channels],
		    GL_UNSIGNED_BYTE,
		    txdata );
slevy's avatar
slevy committed

	} else {

	    gluBuild2DMipmaps( GL_TEXTURE_2D, channels,
teuben's avatar
teuben committed
		    tx->xsize, tx->ysize,
slevy's avatar
 
slevy committed
		    channels==1 && (tx->flags&TXF_ALPHA)
			? GL_ALPHA : format[channels],
		    GL_UNSIGNED_BYTE,
		    txdata);
	if(tx->xsize % 4 != 0)
	    glPixelStorei( GL_PACK_ALIGNMENT, 4 ); /* restore default */
slevy's avatar
 
slevy committed

	if(txdata != (unsigned char *)tx->data)
slevy's avatar
 
slevy committed
	    free(txdata);

slevy's avatar
slevy committed
	glTexParameterfv(txtarget, GL_TEXTURE_BORDER_COLOR,
teuben's avatar
teuben committed
	    black);
slevy's avatar
slevy committed
	glTexParameterf(txtarget, GL_TEXTURE_WRAP_S,
teuben's avatar
teuben committed
	    (tx->flags & TXF_SCLAMP) ? GL_CLAMP : GL_REPEAT);
slevy's avatar
slevy committed
	glTexParameterf(txtarget, GL_TEXTURE_WRAP_T,
teuben's avatar
teuben committed
	    (tx->flags & TXF_TCLAMP) ? GL_CLAMP : GL_REPEAT);
slevy's avatar
slevy committed
	glTexParameterf(txtarget, GL_TEXTURE_MIN_FILTER,
teuben's avatar
teuben committed
	    minfilts[tx->qualflags & 0x07]);
slevy's avatar
slevy committed
	glTexParameterf(txtarget, GL_TEXTURE_MAG_FILTER,
teuben's avatar
teuben committed
	    (tx->qualflags&0x07) ? GL_LINEAR : GL_NEAREST);

#if !CAVE
	/* We won't need texture data again -- opengl has it now */
	Free(tx->data);
	tx->data = NULL;
#endif

    }
  }

  if((wanted ^ *enabled) & (BLEND_ON|BLEND_OFF)) {
    if(wanted & BLEND_ON) {
	glEnable( GL_BLEND );
	if(tx->flags & TXF_ADD) {
	    glBlendFunc( GL_SRC_ALPHA, GL_ONE );
	    glDisable( GL_ALPHA_TEST );
	} else {
	    glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
	    glEnable( GL_ALPHA_TEST );
	    glAlphaFunc( GL_GREATER, 0.03 );
	}
    } else if(wanted & BLEND_OFF) {
	glDisable( GL_BLEND );
	glDisable(GL_ALPHA_TEST);
    }

    if(TEXTURENO(wanted)) {
slevy's avatar
slevy committed
	glEnable(txtarget);
    } else {
	glDisable(GL_TEXTURE_2D);
slevy's avatar
slevy committed
	if(any3d) glDisable(GL_TEXTURE_3D);
    }
    *enabled = wanted = tx->txid[ctx] | wantblend;
teuben's avatar
teuben committed
  }
  return wanted;
}

Texture *txmake( char *fname, int apply, int txflags, int qualflags )
{
  Texture *tx = NewN( Texture, 1 );
  memset(tx, 0, sizeof(*tx));
  tx->tfm = Tidentity;
  tx->filename = fname;
  tx->apply = apply;
  tx->flags = txflags;
  tx->qualflags = qualflags;
  return tx; 
}

int txload( Texture *tx )
{
  if(tx == NULL || tx->loaded < 0) return 0;
  if(tx->loaded == 1) return 1; /* already loaded */
  if(tx->loaded == 2) return 0;	/* busy loading now! */
  tx->loaded = 2;	/* set this to make multiprocess collisions unlikely */
  if(mg_inhaletexture( tx, TXF_RGBA ) <= 0) {
    tx->loaded = -1;
    return 0;
  }
  tx->loaded = 1;
  return 1;
}

int txaddentry( Texture ***tp, int *ntextures, char *fromfile,
		int txno, char *txfname, int apply, int txflags, int qualflags )
{
  char *realfname;
slevy's avatar
 
slevy committed
  if(txno < 0) {
    fprintf(stderr, "Texture indices must be >= 0 (texture %d %s)\n", txno, txfname);
teuben's avatar
teuben committed
    return 0;
  }

  realfname = findfile(fromfile, txfname);
  if(realfname == NULL) {
    fprintf(stderr, "Can't find texture file \"%s\", ignoring it.\n", txfname);
    return 0;
  }
  
  if(txno >= *ntextures) {
    int ontex = *ntextures;
    if(*ntextures == 0) {
	*ntextures = txno + 62;
	*tp = NewN( Texture *, *ntextures );
    } else {
	*ntextures = (txno > ontex*2+1 ? txno+ontex : ontex*2+1);
	*tp = RenewN( *tp, Texture *, *ntextures );
    }
    memset(&(*tp)[ontex], 0, (*ntextures - ontex) * sizeof(Texture *));
  }
slevy's avatar
 
slevy committed
  (*tp)[txno] = txmake( shmstrdup(realfname), apply, txflags, qualflags );
teuben's avatar
teuben committed
  return txno;
}