scummvm/engine/backend/dc/texture_manager.cpp

232 lines
5.6 KiB
C++

/* Residual - Virtual machine to run LucasArts' 3D adventure games
*
* Residual is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the AUTHORS
* file distributed with this source distribution.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*
* $URL$
* $Id$
*
*/
#include "debug.h"
#include "colormap.h"
#include "material.h"
#include "texture_manager.h"
static int twiddletab[1024];
static void init_twiddletab()
{
int x;
for(x=0; x<1024; x++)
twiddletab[x] = (x&1)|((x&2)<<1)|((x&4)<<2)|((x&8)<<3)|((x&16)<<4)|
((x&32)<<5)|((x&64)<<6)|((x&128)<<7)|((x&256)<<8)|((x&512)<<9);
}
TextureManager::Texture::Texture(uint16 *tex, unsigned int fsz, int width, int height, int cnt)
: _data(tex), _width(width), _height(height), _cnt(cnt), _framesize(fsz)
{
_texture = TA_TEXTUREMODE_ARGB1555|TA_TEXTUREMODE_ADDRESS(_data);
_mode2 = TA_POLYMODE2_BLEND_SRC|TA_POLYMODE2_FOG_DISABLED|
TA_POLYMODE2_TEXTURE_REPLACE|TA_POLYMODE2_BILINEAR_FILTER;
switch(_width) {
case 4:
/* XXX */
case 8:
_mode2 |= TA_POLYMODE2_U_SIZE_8;
_uscale = 1.0/8;
break;
case 16:
_mode2 |= TA_POLYMODE2_U_SIZE_16;
_uscale = 1.0/16;
break;
case 32:
_mode2 |= TA_POLYMODE2_U_SIZE_32;
_uscale = 1.0/32;
break;
case 64:
_mode2 |= TA_POLYMODE2_U_SIZE_64;
_uscale = 1.0/64;
break;
case 128:
_mode2 |= TA_POLYMODE2_U_SIZE_128;
_uscale = 1.0/128;
break;
case 256:
_mode2 |= TA_POLYMODE2_U_SIZE_256;
_uscale = 1.0/256;
break;
default:
assert(0);
break;
}
switch(_height) {
case 4:
/* XXX */
case 8:
_mode2 |= TA_POLYMODE2_V_SIZE_8;
_vscale = 1.0/8;
break;
case 16:
_mode2 |= TA_POLYMODE2_V_SIZE_16;
_vscale = 1.0/16;
break;
case 32:
_mode2 |= TA_POLYMODE2_V_SIZE_32;
_vscale = 1.0/32;
break;
case 64:
_mode2 |= TA_POLYMODE2_V_SIZE_64;
_vscale = 1.0/64;
break;
case 128:
_mode2 |= TA_POLYMODE2_V_SIZE_128;
_vscale = 1.0/128;
break;
case 256:
_mode2 |= TA_POLYMODE2_V_SIZE_256;
_vscale = 1.0/256;
break;
default:
assert(0);
break;
}
assert((_width > 4 && _height > 4) || (_width == 4 && _height == 4));
}
void TextureManager::Texture::
setTextureTwiddled(uint16 *tex, const uint8 *data,
const CMap *cmap, int sz, int extra)
{
for(int y=0; y<sz; y++, data += extra)
for(int x=0; x<sz; x++) {
uint8 n = *data++;
uint16 t;
if(!n)
t = 0;
else {
uint8 *c = (uint8*)cmap->_colors+3*n;
t = 0x8000 |
((c[0]<<7)&0x7c00)|
((c[1]<<2)&0x03e0)|
((c[2]>>3)&0x001f);
}
*(uint16*)(((twiddletab[x]<<2)|(twiddletab[y]<<1))
+ (char *)tex) = t;
}
}
void TextureManager::Texture::setTexture(int n, const uint8 *data, const CMap *cmap)
{
uint16 *tex = (uint16 *)(n * _framesize + (char *)_data);
if(_width == _height)
setTextureTwiddled(tex, data, cmap, _width);
else if(_width > _height) {
const uint8 *d2 = data;
uint16 *t2 = tex;
unsigned int fsz = _height * _height * 2;
for(int x = 0; x < _width; x += _height) {
setTextureTwiddled(t2, d2, cmap, _height, _width - _height);
d2 += _height;
t2 += _height * _height;
}
} else {
const uint8 *d2 = data;
uint16 *t2 = tex;
for(int x = 0; x < _height; x += _width) {
setTextureTwiddled(t2, d2, cmap, _width);
d2 += _width * _width;
t2 += _width * _width;
}
}
}
TextureManager::Texture *TextureManager::allocateTexture(int width, int height, int cnt)
{
unsigned int framesize = width * height * 2;
uint16 *tex = (uint16 *) allocateTexMem(framesize * cnt);
return new Texture(tex, framesize, width, height, cnt);
}
void TextureManager::freeTexture(Texture *t)
{
freeTexMem(t->getMemory(), t->getFrameSize()*t->getCnt());
delete t;
}
void *TextureManager::allocateTexMem(unsigned int size)
{
assert(size > 0 && !(size&7));
// First fit
FreeListNode *n, **link;
for(link = &_freeListHead; (n = *link) != NULL; link = &n->next)
if(n->size >= size)
break;
assert(n != NULL);
if(n->size > size) {
FreeListNode *n2 = (FreeListNode *)(size + (char *)n);
n2->size = n->size - size;
n2->next = n->next;
*link = n2;
} else {
*link = n->next;
}
return (void *)n;
}
void TextureManager::freeTexMem(void *mem, unsigned int size)
{
assert(size > 0 && !(size&7) && mem != NULL);
FreeListNode *n, **link;
for(link = &_freeListHead; (n = *link) != NULL; link = &n->next)
if(mem == (n->size + (char *)n))
break;
else if(mem < n) {
n = NULL;
break;
} else
assert(mem > (n->size + (char *)n));
if(n == NULL) {
n = (FreeListNode *)mem;
n->size = size;
n->next = *link;
*link = n;
} else {
n->size += size;
}
// Join this block with the next one if possible
if((char *)n->next == n->size + (char *)n) {
n->size += n->next->size;
n->next = n->next->next;
}
assert(n->next == NULL || (char *)n->next > n->size + (char *)n);
}
void TextureManager::initTextures()
{
unsigned int aperture = 0x180000;
_freeListHead = (FreeListNode *)ta_txalloc(aperture);
_freeListHead->next = NULL;
_freeListHead->size = aperture;
init_twiddletab();
}