0ad/source/tools/atlas/DatafileIO/DDT/DDT.cpp
Ykkrosh aa118403bb Terrain: CalcFromPosition, to convert world-space to tile-space.
ScEd: Compilation fixes.
Atlas: Screen-space to world-space conversion when editing terrain.
Wireframe option. Minor wxWidgets 2.6.1 fixes. AoE3Ed.

This was SVN commit r2698.
2005-09-12 20:04:26 +00:00

431 lines
10 KiB
C++

#include "stdafx.h"
#include "DDT.h"
#include "Stream/Stream.h"
#include "Util.h"
#include "IL/il.h"
#include "IL/ilu.h"
#include <cassert>
using namespace DatafileIO;
DDTFile::DDTFile(SeekableInputStream& stream)
: m_Stream(stream)
{
// TODO: allow multiple nested DDTFiles, with ref-counted init
ilInit();
ilGenImages(1, &m_Image);
}
DDTFile::~DDTFile()
{
ilDeleteImages(1, &m_Image);
ilShutDown();
}
struct DDTImage
{
int width, height;
off_t offset;
size_t length;
};
static void LoadDXT(int dxtType, unsigned char* oldData);
static void SaveDXT(int dxtType); // saves the currently bound image
static void ToggleOrigin(); // urgh
static void SwizzleAGBR();
bool DDTFile::Read(FileType type)
{
ilBindImage(m_Image);
if (type == DDT)
{
char head[4];
m_Stream.Read(head, 4);
if (strncmp(head, "RTS3", 4) != 0)
{
// TODO: report helpful error message
return false;
}
char format[3];
m_Stream.Read(format, 3);
m_Type_Usage = (Type_Usage)format[0];
m_Type_Alpha = (Type_Alpha)format[1];
m_Type_Format = (Type_Format)format[2];
char mipmapLevels;
m_Stream.Read(&mipmapLevels, 1);
m_Type_Levels = mipmapLevels;
uint32_t baseWidth, baseHeight;
m_Stream.Read(&baseWidth, 4);
m_Stream.Read(&baseHeight, 4);
int numImagesPerLevel = (m_Type_Usage == CUBE ? 6 : 1);
int numImages = mipmapLevels * numImagesPerLevel;
std::vector<DDTImage> Images;
Images.resize(numImages);
for (int i = 0; i < numImages; ++i)
{
int width = baseWidth >> (i/numImagesPerLevel); if (width < 1) width = 1;
int height = baseHeight >> (i/numImagesPerLevel); if (height < 1) height = 1;
uint32_t offset, length;
m_Stream.Read(&offset, 4);
m_Stream.Read(&length, 4);
Images[i].width = width;
Images[i].height = height;
Images[i].offset = offset;
Images[i].length = length;
}
// Read the first image. (TODO: cubemaps)
int w = Images[0].width;
int h = Images[0].height;
ilTexImage(w,h,1, 4, IL_RGBA, IL_UNSIGNED_BYTE, NULL);
unsigned char* newData = (unsigned char*)ilGetData();
switch (m_Type_Format)
{
case BGRA:
{
unsigned char* oldData = new unsigned char[w*h*4];
m_Stream.Read(oldData, w*h*4);
for (int i = 0; i < w*h; ++i)
{
newData[i*4+0] = oldData[i*4+2];
newData[i*4+1] = oldData[i*4+1];
newData[i*4+2] = oldData[i*4+0];
newData[i*4+3] = oldData[i*4+3];
}
delete[] oldData;
}
break;
case DXT1:
case DXT3:
case NORMSPEC:
{
int dxtType = (m_Type_Format == DXT1 ? 1 : m_Type_Format == DXT3 ? 3 : 5);
int pixPerByte = (m_Type_Format == DXT1 ? 2 : 1);
unsigned char* oldData = new unsigned char[w*h/pixPerByte];
m_Stream.Read(oldData, w*h/pixPerByte);
LoadDXT(dxtType, oldData);
if (m_Type_Format == NORMSPEC)
SwizzleAGBR();
delete[] oldData;
}
break;
case GREY:
{
unsigned char* oldData = new unsigned char[w*h];
m_Stream.Read(oldData, w*h);
for (int i = 0; i < w*h; ++i)
{
newData[i*4+0] =
newData[i*4+1] =
newData[i*4+2] = oldData[i];
newData[i*4+3] = 255;
}
delete[] oldData;
}
break;
default:
//assert(! "Unhandled format");
ilClearColour(1.0f, 0.0f, 1.0f, 1.0f);
ilClearImage();
break;
}
ToggleOrigin(); // use this instead of iluFlip because we don't want to change the actual data
}
else if (type == TGA)
{
void* buffer;
size_t size;
m_Stream.AcquireBuffer(buffer, size);
ilLoadL(IL_TGA, buffer, (ILuint)size);
m_Stream.ReleaseBuffer(buffer);
ilConvertImage(IL_RGBA, IL_UNSIGNED_BYTE);
iluFlipImage();
}
else
{
assert(! "Invalid type");
}
return true;
}
bool DDTFile::GetImageData(void*& buffer, int& width, int& height, bool realAlpha)
{
ilBindImage(m_Image);
width = ilGetInteger(IL_IMAGE_WIDTH);
height = ilGetInteger(IL_IMAGE_HEIGHT);
if (realAlpha)
{
buffer = malloc(width*height * 4);
memcpy(buffer, ilGetData(), width*height * 4);
}
else
{
buffer = malloc(width*height * 3 * 2);
unsigned char* newData = (unsigned char*)buffer;
unsigned char* oldData = (unsigned char*)ilGetData();
for (int i = 0; i < width*height; ++i)
{
newData[i*3+0] = oldData[i*4+0];
newData[i*3+1] = oldData[i*4+1];
newData[i*3+2] = oldData[i*4+2];
}
for (int i = 0; i < width*height; ++i)
{
newData[(i+width*height)*3+0] =
newData[(i+width*height)*3+1] =
newData[(i+width*height)*3+2] = oldData[i*4+3];
}
}
height *= 2;
return true;
}
//////////////////////////////////////////////////////////////////////////
// DevIL code: (slightly nasty, since DevIL doesn't seem to be flexible enough
// to do what I need it to do...)
struct ILOutputStream
{
static OutputStream* stream;
static ILHANDLE ILAPIENTRY Open(const ILstring)
{
return (void*)-1;
}
static ILvoid ILAPIENTRY Close(ILHANDLE)
{
}
static ILint ILAPIENTRY Putc(ILubyte c, ILHANDLE)
{
stream->Write(&c, 1);
return c;
}
static ILint ILAPIENTRY Seek(ILHANDLE, ILint /*offset*/, ILint /*whence*/)
{
assert(! "Not implemented");
return 0;
}
static ILint ILAPIENTRY Tell(ILHANDLE)
{
return stream->Tell();
}
static ILint ILAPIENTRY Write(const void* data, ILuint size, ILuint count, ILHANDLE)
{
if (size*count)
stream->Write(data, size*count);
return count;
}
};
OutputStream* ILOutputStream::stream = NULL;
extern "C" {
extern ILboolean ilSaveTargaF(ILHANDLE File);
// because DevIL doesn't want to write to things that aren't
// really files, so we have to use its internal writing functions
extern ILvoid iSetOutputFile(ILHANDLE File);
}
static void ToggleOrigin(); // urgh
bool DDTFile::SaveFile(OutputStream& stream, FileType outputType)
{
ilBindImage(m_Image);
ilSetWrite(&ILOutputStream::Open, &ILOutputStream::Close, &ILOutputStream::Putc,
&ILOutputStream::Seek, &ILOutputStream::Tell, &ILOutputStream::Write);
iSetOutputFile(NULL); // make sure it's using the right output functions
ILOutputStream::stream = &stream;
if (outputType == TGA)
{
ilSaveTargaF(NULL);
}
else if (outputType == DDT)
{
int bpp;
switch (m_Type_Format)
{
case BGRA: bpp = 32; break;
case GREY: bpp = 8; break;
case DXT1: bpp = 4; break;
case DXT3: bpp = 8; break;
case NORMSPEC: bpp = 8; break;
default: assert(! "Invalid format"); return false;
}
stream.Write("RTS3", 4);
char format[4];
format[0] = (char)m_Type_Usage;
format[1] = (char)m_Type_Alpha;
format[2] = (char)m_Type_Format;
format[3] = (char)m_Type_Levels;
stream.Write(format, 4);
uint32_t baseWidth, baseHeight;
baseWidth = ilGetInteger(IL_IMAGE_WIDTH);
baseHeight = ilGetInteger(IL_IMAGE_HEIGHT);
stream.Write(&baseWidth, 4);
stream.Write(&baseHeight, 4);
int numImagesPerLevel = 1; // TODO: cubemaps
int numImages = m_Type_Levels * numImagesPerLevel;
uint32_t imgOffset = 16 + 8*numImages;
for (int i = 0; i < numImages; ++i)
{
int width = baseWidth >> (i/numImagesPerLevel); if (width < 1) width = 1;
int height = baseHeight >> (i/numImagesPerLevel); if (height < 1) height = 1;
uint32_t length = width*height * bpp / 8;
stream.Write(&imgOffset, 4);
stream.Write(&length, 4);
imgOffset += length;
}
for (int i = 0; i < numImages; ++i)
{
int width = baseWidth >> (i/numImagesPerLevel); if (width < 1) width = 1;
int height = baseHeight >> (i/numImagesPerLevel); if (height < 1) height = 1;
ilBindImage(m_Image);
ILuint img = ilCloneCurImage();
ilBindImage(img);
iluImageParameter(ILU_FILTER, ILU_SCALE_BOX); // TODO - proper mipmapping
iluScale(width, height, 1);
switch (m_Type_Format)
{
case BGRA:
{
unsigned char* newData = new unsigned char[width*height*4];
unsigned char* oldData = (unsigned char*)ilGetData();
for (int i = 0; i < width*height; ++i)
{
newData[i*4+0] = oldData[i*4+2];
newData[i*4+1] = oldData[i*4+1];
newData[i*4+2] = oldData[i*4+0];
newData[i*4+3] = oldData[i*4+3];
}
stream.Write(newData, width*height*4);
delete[] newData;
break;
}
case GREY:
{
unsigned char* newData = new unsigned char[width*height];
unsigned char* oldData = (unsigned char*)ilGetData();
for (int i = 0; i < width*height; ++i)
{
newData[i] = oldData[i*4+0];
}
stream.Write(newData, width*height);
delete[] newData;
break;
}
case DXT1:
SaveDXT(1);
break;
case DXT3:
SaveDXT(3);
break;
case NORMSPEC:
SwizzleAGBR();
SaveDXT(5);
break;
}
ilDeleteImages(1, &img);
}
}
ilResetWrite();
return true;
}
static void SwizzleAGBR()
{
ILubyte* data = ilGetData();
ILint pixels = ilGetInteger(IL_IMAGE_WIDTH)*ilGetInteger(IL_IMAGE_HEIGHT);
for (ILint i = 0; i < pixels; ++i)
{
ILubyte t = data[i*4+0];
data[i*4+0] = data[i*4+3];
data[i*4+3] = t;
}
}
// Evilness:
#include "IL/devil_internal_exports.h"
extern "C"
{
extern ILboolean DecompressDXT1();
extern ILboolean DecompressDXT3();
extern ILboolean DecompressDXT5();
extern ILuint Compress(ILimage* Image, ILenum DXTCFormat);
extern ILubyte* CompData;
extern ILint Depth, Width, Height;
extern ILimage* Image;
extern ILimage* iCurImage;
}
static void LoadDXT(int dxtType, unsigned char* oldData)
{
// More evilness, that assumes a lot about DevIL's internals:
CompData = (ILubyte*)oldData;
Image = iCurImage;
Width = Image->Width;
Height = Image->Height;
Depth = Image->Depth;
switch (dxtType)
{
case 1: DecompressDXT1(); break;
case 3: DecompressDXT3(); break;
case 5: DecompressDXT5(); break;
default: assert(0);
}
CompData = NULL;
Image = NULL;
}
static void SaveDXT(int dxtType)
{
Compress(ilGetCurImage(), dxtType==1 ? IL_DXT1 : dxtType==3 ? IL_DXT3 : IL_DXT5);
}
static void ToggleOrigin()
{
iCurImage->Origin = (iCurImage->Origin == IL_ORIGIN_UPPER_LEFT ? IL_ORIGIN_LOWER_LEFT : IL_ORIGIN_UPPER_LEFT);
}