nenuzhno-engine_iter1/src/resource/vtf_loader.cpp

263 lines
6.6 KiB
C++

#include <string>
#include <fstream>
#include <cstring>
using namespace std;
#include "log.h"
#include "system/FileSystem.h"
#include "graphics/platform_gl.h"
#include "graphics/gl_utils.h"
#include "graphics/texture.h"
#include "resource/vtf.h"
size_t computeMipsSize(int width, int height, int mipcount, float bpp);
bool VTFLoader::CheckExt(const char *name)
{
return strstr(name,".vtf")!=0;
}
Texture *VTFLoader::Load(const char *fileName)
{
return Load(fileName,0);
}
Texture *VTFLoader::Load(const char *fileName, int mip)
{
string filePath = "materials/"+string(fileName);
if(filePath.find(".vtf") == string::npos)
filePath += ".vtf";
IFile *input = g_fs.Open(filePath.c_str());
if(!input){
//LOG( "File: %s not found\n", path);
return 0;
}
VTFFileHeader_t header;
input->Read(&header, sizeof(VTFFileHeader_t));
if(header.width!=header.height){
//LOG("%s(%dx%d) is not quad\n",fileName,header.width,header.height);
}
if(header.flags&0x4000)
mip = 0;
if(mip >= header.numMipLevels)
mip = header.numMipLevels-1;
Texture *tex = new Texture();
tex->width = header.width>>mip;
tex->height = header.height>>mip;
if(tex->width < 4)
tex->width = 4;
if(tex->height < 4)
tex->height = 4;
size_t texSize = 0;
size_t offset = header.headerSize;
int faces = 1;
if(header.flags&0x4000){
if(header.version[1] < 5)
faces = 7;
else
faces = 6;
}else{
offset += ((header.lowResImageWidth+3)/4)*((header.lowResImageHeight+3)/4)*8;
}
int ifmt;
int fmt;
int type = GL_UNSIGNED_BYTE;
bool compressed = false;
if(header.imageFormat == IMAGE_FORMAT_DXT1){//4bpp
texSize = ((tex->width + 3) / 4) * ((tex->height + 3) / 4) * 8;
offset += computeMipsSize(tex->width,tex->height,header.numMipLevels-mip,0.5f)*faces;
ifmt = GL_COMPRESSED_RGB_S3TC_DXT1;
fmt = GL_RGB;
compressed=true;
}else if(header.imageFormat == IMAGE_FORMAT_DXT5){//8bpp
texSize = ((tex->width + 3) / 4) * ((tex->height + 3) / 4) * 16;
offset += computeMipsSize(tex->width,tex->height,header.numMipLevels-mip,1)*faces;
ifmt = GL_COMPRESSED_RGBA_S3TC_DXT5;
fmt = GL_RGBA;
compressed=true;
}else if(header.imageFormat == IMAGE_FORMAT_RGBA16161616F){//64bpp
Log("image format RGBA16F (%s)\n",fileName);
texSize = tex->width * tex->height * 8;
offset += computeMipsSize(tex->width,tex->height,header.numMipLevels-mip,8)*faces;
ifmt = GL_RGBA16F;
fmt = GL_RGBA;
type = GL_HALF_FLOAT;
}else if(header.imageFormat == IMAGE_FORMAT_BGRA8888){//32bpp
Log("image format BGRA8 (%s)\n",fileName);
texSize = tex->width * tex->height * 4;
offset += computeMipsSize(tex->width,tex->height,header.numMipLevels-mip,4)*faces;
ifmt = GL_RGBA;
fmt = GL_BGRA;
}else if(header.imageFormat == IMAGE_FORMAT_BGR888){//24bpp
Log("image format BGR8 (%s)\n",fileName);
texSize = tex->width * tex->height * 3;
offset += computeMipsSize(tex->width,tex->height,header.numMipLevels-mip,3)*faces;
ifmt = GL_RGB;
fmt = FMT_BGR8;
//need resample
}else{
Log("Unknown image format %d (%s)\n", header.imageFormat,fileName);
g_fs.Close(input);
delete tex;
return 0;
}
//LOG("texSize %d offset %d\n", texSize, offset);
GLubyte *texData = new GLubyte[texSize*faces];
input->Seek(offset);
input->Read(texData, texSize*faces);
g_fs.Close(input);
//add resample flag or something like that
if(fmt==FMT_BGR8){
ResampleBGR(texData,texSize*faces);
fmt=GL_RGB;
}
//TODO extension
if(fmt==GL_BGRA){
ResampleBGRA(texData,texSize*faces);
fmt=GL_RGBA;
}
if(header.flags&0x4000){
//LOG("%s is a cubemap! size %dx%d mips %d fmt %d ifmt %x type %x\n",fileName,width,height,header.numMipLevels, header.imageFormat,ifmt,type);
tex->target = GL_TEXTURE_CUBE_MAP;
tex->Create(tex->width, tex->height);
tex->SetFilter(GL_LINEAR, GL_LINEAR);
if(CheckGLError("LoadVTF cubemap create", __FILE__, __LINE__))
Log("GL error while loading %s\n",fileName);
glTexParameteri(tex->target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(tex->target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
//glTexParameteri(target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
CheckGLError("LoadVTF cubemap Parameters", __FILE__, __LINE__);
if(!compressed){
for(int i=0;i<6;i++){
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X+i, 0, ifmt, tex->width, tex->height, 0, fmt, type, texData+texSize*i);
}
}else{
//#ifdef ANDROID
//GLubyte *newData = new GLubyte[width*height*4];
for(int i=0;i<6;i++){
tex->target=GL_TEXTURE_CUBE_MAP_POSITIVE_X+i;
tex->UploadCompressed(ifmt,texSize,texData+texSize*i);
//DecompressDXT(this, texData+texSize*i, newData, texSize, header.imageFormat);
//glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X+i, 0, fmt, width, height, 0, fmt, type, newData);
}
tex->target = GL_TEXTURE_CUBE_MAP;
//delete[] newData;
/*#else
for(int i=0;i<6;i++)
{
glCompressedTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X+i,0,ifmt, width, height, 0, texSize, texData+texSize*i);
//glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X+i, 0, ifmt, width, height, 0, fmt, type, texData+texSize*i);
}
#endif*/
}
tex->SetFilter(GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR);
glGenerateMipmap(tex->target);
CheckGLError("LoadVTF env upload", __FILE__, __LINE__);
}else{
tex->Create(tex->width, tex->height);
tex->SetWrap(GL_REPEAT);
tex->SetFilter(GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR);
#ifndef ANDROID
glTexParameteri(tex->target, GL_TEXTURE_MAX_ANISOTROPY_EXT, 16);
#endif
//if(header.imageFormat == IMAGE_FORMAT_DXT1||header.imageFormat == IMAGE_FORMAT_DXT5)
if(compressed){
//Upload(0, GL_RGBA_DXT5_S3TC, texData);
tex->UploadCompressed(ifmt,texSize,texData);
}else if (header.imageFormat==IMAGE_FORMAT_BGRA8888||header.imageFormat==IMAGE_FORMAT_BGR888){
tex->Upload(0, ifmt,fmt,type, texData);
}
else
Log("wut?\n");
if(CheckGLError("LoadVTF Upload", __FILE__, __LINE__))
Log("GL error while loading %s\n",fileName);
glGenerateMipmap(tex->target);
}
delete[] texData;
return tex;
}
size_t computeMipsSize(int width, int height, int mipcount, float bpp)
{
size_t imgSize = 0;
width>>=1;
height>>=1;
if(bpp<3)
{
if(width < 4)
width = 4;
if(height < 4)
height = 4;
}
for(int i=0; i<mipcount-1; i++)
{
imgSize+=(width*height*bpp);
width>>=1;
height>>=1;
if(bpp<3)
{
if(width < 4)
width = 4;
if(height < 4)
height = 4;
}
}
return imgSize;
}
/*
size_t computeMipsSize(int width, int height, int mipcount, float bpp)
{
size_t imgSize = 0;
width>>=1;
height>>=1;
if(width < 4)
width = 4;
if(height < 4)
height = 4;
for(int i=1; i<mipcount; i++)
{
imgSize+=(width*height*bpp);
width>>=1;
height>>=1;
if(width < 4)
width = 4;
if(height < 4)
height = 4;
}
return imgSize;
}
*/