winamp/Src/Winamp/plush/MAT.C

454 lines
15 KiB
C

/******************************************************************************
Plush Version 1.2
mat.c
Material Control
Copyright (c) 1996-2000, Justin Frankel
******************************************************************************/
#include "plush.h"
static void _plGenerateSinglePalette(pl_Mat *);
static void _plGeneratePhongPalette(pl_Mat *);
static void _plGenerateTextureEnvPalette(pl_Mat *);
static void _plGenerateTexturePalette(pl_Mat *, pl_Texture *);
static void _plGeneratePhongTexturePalette(pl_Mat *, pl_Texture *);
static void _plGeneratePhongTransparentPalette(pl_Mat *m);
static void _plGenerateTransparentPalette(pl_Mat *);
static void _plSetMaterialPutFace(pl_Mat *m);
static void _plMatSetupTransparent(pl_Mat *m, pl_uChar *pal);
pl_Mat *plMatCreate() {
pl_Mat *m;
m = (pl_Mat *) malloc(sizeof(pl_Mat));
if (!m) return 0;
memset(m,0,sizeof(pl_Mat));
m->EnvScaling = 1.0f;
m->TexScaling = 1.0f;
m->Ambient[0] = m->Ambient[1] = m->Ambient[2] = 0;
m->Diffuse[0] = m->Diffuse[1] = m->Diffuse[2] = 128;
m->Specular[0] = m->Specular[1] = m->Specular[2] = 128;
m->Shininess = 4;
m->NumGradients = 32;
m->FadeDist = 1000.0;
m->zBufferable = 1;
return m;
}
void plMatDelete(pl_Mat *m) {
if (m) {
if (m->_ReMapTable) free(m->_ReMapTable);
if (m->_RequestedColors) free(m->_RequestedColors);
if (m->_AddTable) free(m->_AddTable);
free(m);
}
}
void plMatInit(pl_Mat *m) {
if (m->Shininess < 1) m->Shininess = 1;
m->_ft = ((m->Environment ? PL_FILL_ENVIRONMENT : 0) |
(m->Texture ? PL_FILL_TEXTURE : 0));
m->_st = m->ShadeType;
if (m->Transparent) m->_ft = PL_FILL_TRANSPARENT;
if (m->_ft == (PL_FILL_TEXTURE|PL_FILL_ENVIRONMENT))
m->_st = PL_SHADE_NONE;
if (m->_ft == PL_FILL_SOLID) {
if (m->_st == PL_SHADE_NONE) _plGenerateSinglePalette(m);
else _plGeneratePhongPalette(m);
} else if (m->_ft == PL_FILL_TEXTURE) {
if (m->_st == PL_SHADE_NONE)
_plGenerateTexturePalette(m,m->Texture);
else _plGeneratePhongTexturePalette(m,m->Texture);
} else if (m->_ft == PL_FILL_ENVIRONMENT) {
if (m->_st == PL_SHADE_NONE)
_plGenerateTexturePalette(m,m->Environment);
else _plGeneratePhongTexturePalette(m,m->Environment);
} else if (m->_ft == (PL_FILL_ENVIRONMENT|PL_FILL_TEXTURE))
_plGenerateTextureEnvPalette(m);
else if (m->_ft == PL_FILL_TRANSPARENT) {
if (m->_st == PL_SHADE_NONE) _plGenerateTransparentPalette(m);
else _plGeneratePhongTransparentPalette(m);
}
_plSetMaterialPutFace(m);
}
static void _plMatSetupTransparent(pl_Mat *m, pl_uChar *pal) {
pl_uInt x, intensity;
if (m->Transparent)
{
if (m->_AddTable) free(m->_AddTable);
m->_AddTable = (pl_uInt16 *) malloc(256*sizeof(pl_uInt16));
for (x = 0; x < 256; x ++) {
intensity = *pal++;
intensity += *pal++;
intensity += *pal++;
m->_AddTable[x] = ((intensity*(m->_ColorsUsed-m->_tsfact))/768);
}
}
}
void plMatMapToPal(pl_Mat *m, pl_uChar *pal, pl_sInt pstart, pl_sInt pend) {
pl_sInt32 j, r, g, b, bestdiff, r2, g2, b2;
pl_sInt bestpos,k;
pl_uInt32 i;
pl_uChar *p;
if (!m->_RequestedColors) plMatInit(m);
if (!m->_RequestedColors) return;
if (m->_ReMapTable) free(m->_ReMapTable);
m->_ReMapTable = (pl_uChar *) malloc(m->_ColorsUsed);
for (i = 0; i < m->_ColorsUsed; i ++) {
bestdiff = 1000000000;
bestpos = pstart;
r = m->_RequestedColors[i*3];
g = m->_RequestedColors[i*3+1];
b = m->_RequestedColors[i*3+2];
p = pal + pstart*3;
for (k = pstart; k <= (pl_sInt)pend; k ++) {
r2 = p[0] - r;
g2 = p[1] - g;
b2 = p[2] - b;
p += 3;
j = r2*r2+g2*g2+b2*b2;
if (j < bestdiff) {
bestdiff = j;
bestpos = k;
}
}
m->_ReMapTable[i] = bestpos;
}
_plMatSetupTransparent(m,pal);
}
static void _plGenerateSinglePalette(pl_Mat *m) {
m->_ColorsUsed = 1;
if (m->_RequestedColors) free(m->_RequestedColors);
m->_RequestedColors = (pl_uChar *) malloc(3);
m->_RequestedColors[0] = plMin(plMax(m->Ambient[0],0),255);
m->_RequestedColors[1] = plMin(plMax(m->Ambient[1],0),255);
m->_RequestedColors[2] = plMin(plMax(m->Ambient[2],0),255);
}
static void _plGeneratePhongPalette(pl_Mat *m) {
pl_uInt i = m->NumGradients, x;
pl_sInt c;
pl_uChar *pal;
double a, da, ca, cb;
m->_ColorsUsed = m->NumGradients;
if (m->_RequestedColors) free(m->_RequestedColors);
pal = m->_RequestedColors = (pl_uChar *) malloc(m->_ColorsUsed*3);
a = PL_PI/2.0;
if (m->NumGradients > 1) da = -PL_PI/((m->NumGradients-1)<<1);
else da=0.0;
do {
if (m->NumGradients == 1) ca = 1;
else {
ca = cos((double) a);
a += da;
}
cb = pow((double) ca, (double) m->Shininess);
for (x = 0; x < 3; x ++) {
c = (pl_sInt) ((cb*m->Specular[x])+(ca*m->Diffuse[x])+m->Ambient[x]);
*(pal++) = plMax(0,plMin(c,255));
}
} while (--i);
}
static void _plGenerateTextureEnvPalette(pl_Mat *m) {
pl_sInt c;
pl_uInt whichlevel,whichindex;
pl_uChar *texpal, *envpal, *pal;
m->_ColorsUsed = m->Texture->NumColors*m->Environment->NumColors;
if (m->_RequestedColors) free(m->_RequestedColors);
pal = m->_RequestedColors = (pl_uChar *) malloc(m->_ColorsUsed*3);
envpal = m->Environment->PaletteData;
if (m->_AddTable) free(m->_AddTable);
m->_AddTable = (pl_uInt16 *) malloc(m->Environment->NumColors*sizeof(pl_uInt16));
for (whichlevel = 0; whichlevel < m->Environment->NumColors; whichlevel++) {
texpal = m->Texture->PaletteData;
switch (m->TexEnvMode)
{
case PL_TEXENV_MUL: // multiply
for (whichindex = 0; whichindex < m->Texture->NumColors; whichindex++) {
*pal++ = (pl_uChar) (((pl_sInt) (*texpal++) * (pl_sInt) envpal[0])>>8);
*pal++ = (pl_uChar) (((pl_sInt) (*texpal++) * (pl_sInt) envpal[1])>>8);
*pal++ = (pl_uChar) (((pl_sInt) (*texpal++) * (pl_sInt) envpal[2])>>8);
}
break;
case PL_TEXENV_AVG: // average
for (whichindex = 0; whichindex < m->Texture->NumColors; whichindex++) {
*pal++ = (pl_uChar) (((pl_sInt) (*texpal++) + (pl_sInt) envpal[0])>>1);
*pal++ = (pl_uChar) (((pl_sInt) (*texpal++) + (pl_sInt) envpal[1])>>1);
*pal++ = (pl_uChar) (((pl_sInt) (*texpal++) + (pl_sInt) envpal[2])>>1);
}
break;
case PL_TEXENV_TEXMINUSENV: // tex-env
for (whichindex = 0; whichindex < m->Texture->NumColors; whichindex++) {
c = (pl_sInt) (*texpal++) - (pl_sInt) envpal[0]; *pal++ = plMax(0,plMin(255,c));
c = (pl_sInt) (*texpal++) - (pl_sInt) envpal[1]; *pal++ = plMax(0,plMin(255,c));
c = (pl_sInt) (*texpal++) - (pl_sInt) envpal[2]; *pal++ = plMax(0,plMin(255,c));
}
break;
case PL_TEXENV_ENVMINUSTEX: // env-tex
for (whichindex = 0; whichindex < m->Texture->NumColors; whichindex++) {
c = -(pl_sInt) (*texpal++) - (pl_sInt) envpal[0]; *pal++ = plMax(0,plMin(255,c));
c = -(pl_sInt) (*texpal++) - (pl_sInt) envpal[1]; *pal++ = plMax(0,plMin(255,c));
c = -(pl_sInt) (*texpal++) - (pl_sInt) envpal[2]; *pal++ = plMax(0,plMin(255,c));
}
break;
case PL_TEXENV_MIN:
for (whichindex = 0; whichindex < m->Texture->NumColors; whichindex++) {
*pal++ = plMin(texpal[0],envpal[0]);
*pal++ = plMin(texpal[1],envpal[1]);
*pal++ = plMin(texpal[2],envpal[2]);
texpal+=3;
}
break;
case PL_TEXENV_MAX:
break;
for (whichindex = 0; whichindex < m->Texture->NumColors; whichindex++) {
*pal++ = plMax(texpal[0],envpal[0]);
*pal++ = plMax(texpal[1],envpal[1]);
*pal++ = plMax(texpal[2],envpal[2]);
texpal+=3;
}
default: // add
for (whichindex = 0; whichindex < m->Texture->NumColors; whichindex++) {
c = (pl_sInt) (*texpal++) + (pl_sInt) envpal[0]; *pal++ = plMax(0,plMin(255,c));
c = (pl_sInt) (*texpal++) + (pl_sInt) envpal[1]; *pal++ = plMax(0,plMin(255,c));
c = (pl_sInt) (*texpal++) + (pl_sInt) envpal[2]; *pal++ = plMax(0,plMin(255,c));
}
break;
}
envpal += 3;
m->_AddTable[whichlevel] = whichlevel*m->Texture->NumColors;
}
}
static void _plGenerateTexturePalette(pl_Mat *m, pl_Texture *t) {
pl_uChar *ppal, *pal;
pl_sInt c, i, x;
m->_ColorsUsed = t->NumColors;
if (m->_RequestedColors) free(m->_RequestedColors);
pal = m->_RequestedColors = (pl_uChar *) malloc(m->_ColorsUsed*3);
ppal = t->PaletteData;
i = t->NumColors;
do {
for (x = 0; x < 3; x ++) {
c = m->Ambient[x] + *ppal++;
*(pal++) = plMax(0,plMin(c,255));
}
} while (--i);
}
static void _plGeneratePhongTexturePalette(pl_Mat *m, pl_Texture *t) {
double a, ca, da, cb;
pl_uInt16 *addtable;
pl_uChar *ppal, *pal;
pl_sInt c, i, i2, x;
pl_uInt num_shades;
if (t->NumColors) num_shades = (m->NumGradients / t->NumColors);
else num_shades=1;
if (!num_shades) num_shades = 1;
m->_ColorsUsed = num_shades*t->NumColors;
if (m->_RequestedColors) free(m->_RequestedColors);
pal = m->_RequestedColors = (pl_uChar *) malloc(m->_ColorsUsed*3);
a = PL_PI/2.0;
if (num_shades>1) da = (-PL_PI/2.0)/(num_shades-1);
else da=0.0;
i2 = num_shades;
do {
ppal = t->PaletteData;
ca = cos((double) a);
a += da;
cb = pow(ca, (double) m->Shininess);
i = t->NumColors;
do {
for (x = 0; x < 3; x ++) {
c = (pl_sInt) ((cb*m->Specular[x])+(ca*m->Diffuse[x])+m->Ambient[x] + *ppal++);
*(pal++) = plMax(0,plMin(c,255));
}
} while (--i);
} while (--i2);
ca = 0;
if (m->_AddTable) free(m->_AddTable);
m->_AddTable = (pl_uInt16 *) malloc(256*sizeof(pl_uInt16));
addtable = m->_AddTable;
i = 256;
do {
a = sin(ca) * num_shades;
ca += PL_PI/512.0;
*addtable++ = ((pl_sInt) a)*t->NumColors;
} while (--i);
}
static void _plGeneratePhongTransparentPalette(pl_Mat *m) {
m->_tsfact = (pl_sInt) (m->NumGradients*(1.0/(1+m->Transparent)));
_plGeneratePhongPalette(m);
}
static void _plGenerateTransparentPalette(pl_Mat *m) {
m->_tsfact = 0;
_plGeneratePhongPalette(m);
}
static void _plSetMaterialPutFace(pl_Mat *m) {
m->_PutFace = 0;
switch (m->_ft) {
case PL_FILL_TRANSPARENT: switch(m->_st) {
case PL_SHADE_NONE: case PL_SHADE_FLAT:
case PL_SHADE_FLAT_DISTANCE: case PL_SHADE_FLAT_DISTANCE|PL_SHADE_FLAT:
m->_PutFace = plPF_TransF;
break;
case PL_SHADE_GOURAUD: case PL_SHADE_GOURAUD_DISTANCE:
case PL_SHADE_GOURAUD|PL_SHADE_GOURAUD_DISTANCE:
m->_PutFace = plPF_TransG;
break;
}
break;
case PL_FILL_SOLID: switch(m->_st) {
case PL_SHADE_NONE: case PL_SHADE_FLAT:
case PL_SHADE_FLAT_DISTANCE: case PL_SHADE_FLAT_DISTANCE|PL_SHADE_FLAT:
m->_PutFace = plPF_SolidF;
break;
case PL_SHADE_GOURAUD: case PL_SHADE_GOURAUD_DISTANCE:
case PL_SHADE_GOURAUD|PL_SHADE_GOURAUD_DISTANCE:
m->_PutFace = plPF_SolidG;
break;
}
break;
case PL_FILL_ENVIRONMENT:
case PL_FILL_TEXTURE:
if (m->PerspectiveCorrect) switch (m->_st) {
case PL_SHADE_NONE: case PL_SHADE_FLAT:
case PL_SHADE_FLAT_DISTANCE: case PL_SHADE_FLAT_DISTANCE|PL_SHADE_FLAT:
m->_PutFace = plPF_PTexF;
break;
case PL_SHADE_GOURAUD: case PL_SHADE_GOURAUD_DISTANCE:
case PL_SHADE_GOURAUD|PL_SHADE_GOURAUD_DISTANCE:
m->_PutFace = plPF_PTexG;
break;
}
else switch (m->_st) {
case PL_SHADE_NONE: case PL_SHADE_FLAT:
case PL_SHADE_FLAT_DISTANCE: case PL_SHADE_FLAT_DISTANCE|PL_SHADE_FLAT:
m->_PutFace = plPF_TexF;
break;
case PL_SHADE_GOURAUD: case PL_SHADE_GOURAUD_DISTANCE:
case PL_SHADE_GOURAUD|PL_SHADE_GOURAUD_DISTANCE:
m->_PutFace = plPF_TexG;
break;
}
break;
case PL_FILL_TEXTURE|PL_FILL_ENVIRONMENT:
m->_PutFace = plPF_TexEnv;
break;
}
}
typedef struct __ct {
pl_uChar r,g,b;
pl_Bool visited;
struct __ct *next;
} _ct;
static int mdist(_ct *a, _ct *b) {
return ((a->r-b->r)*(a->r-b->r)+(a->g-b->g)*(a->g-b->g)+(a->b-b->b)*(a->b-b->b));
}
void plMatMakeOptPal(pl_uChar *p, pl_sInt pstart,
pl_sInt pend, pl_Mat **materials, pl_sInt nmats) {
pl_uChar *allColors = 0;
pl_sInt numColors = 0, nc, x;
pl_sInt len = pend + 1 - pstart;
pl_sInt32 current, newnext, bestdist, thisdist;
_ct *colorBlock, *best, *cp;
for (x = 0; x < nmats; x ++) {
if (materials[x]) {
if (!materials[x]->_RequestedColors) plMatInit(materials[x]);
if (materials[x]->_RequestedColors) numColors+=materials[x]->_ColorsUsed;
}
}
if (!numColors) return;
allColors=(pl_uChar*)malloc(numColors*3);
numColors=0;
for (x = 0; x < nmats; x ++) {
if (materials[x]) {
if (materials[x]->_RequestedColors)
memcpy(allColors + (numColors*3), materials[x]->_RequestedColors,
materials[x]->_ColorsUsed*3);
numColors += materials[x]->_ColorsUsed;
}
}
if (numColors <= len) {
memcpy(p+pstart*3,allColors,numColors*3);
free(allColors);
return;
}
colorBlock = (_ct *) malloc(sizeof(_ct)*numColors);
for (x = 0; x < numColors; x++) {
colorBlock[x].r = allColors[x*3];
colorBlock[x].g = allColors[x*3+1];
colorBlock[x].b = allColors[x*3+2];
colorBlock[x].visited = 0;
colorBlock[x].next = 0;
}
free(allColors);
/* Build a list, starting at color 0 */
current = 0;
nc = numColors;
do {
newnext = -1;
bestdist = 300000000;
colorBlock[current].visited = 1;
for (x = 0; x < nc; x ++) {
if (!colorBlock[x].visited) {
thisdist = mdist(colorBlock + x, colorBlock + current);
if (thisdist < 5) { colorBlock[x].visited = 1; numColors--; }
else if (thisdist < bestdist) { bestdist = thisdist; newnext = x; }
}
}
if (newnext != -1) {
colorBlock[current].next = colorBlock + newnext;
current = newnext;
}
} while (newnext != -1);
colorBlock[current].next = 0; /* terminate the list */
/* we now have a linked list starting at colorBlock, which is each one and
it's closest neighbor */
while (numColors > len) {
bestdist = mdist(colorBlock,colorBlock->next);
for (best = cp = colorBlock; cp->next; cp = cp->next) {
if (bestdist > (thisdist = mdist(cp,cp->next))) {
best = cp;
bestdist = thisdist;
}
}
best->r = ((int) best->r + (int) best->next->r)>>1;
best->g = ((int) best->g + (int) best->next->g)>>1;
best->b = ((int) best->b + (int) best->next->b)>>1;
best->next = best->next->next;
numColors--;
}
x = pstart*3;
for (cp = colorBlock; cp; cp = cp->next) {
p[x++] = cp->r;
p[x++] = cp->g;
p[x++] = cp->b;
}
free(colorBlock);
}