mirror of
https://github.com/WinampDesktop/winamp.git
synced 2024-11-24 15:00:12 +01:00
454 lines
15 KiB
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);
|
|
}
|