UCHAR* TextureTools::LoadDDS(CHAR* file, ImageInfoDDS &info) { const INT ddsHeightOffset = 12; const INT ddsWidthOffset = 16; const INT ddsLinearSizeOffset = 20; const INT ddsMipMapNumOffset = 28; const INT ddsFourCCOffset = 84; const INT ddsImageDataOffset = 128; EndianType e = GetEndian(); BOOL byteSwap = FALSE; if (e == ENDIAN_Big) byteSwap = TRUE; FILE *fp = fopen(file, "rb"); if (fp == nullptr) return nullptr; CHAR imageID[4]; fread(imageID, 1, 4, fp); if(strncmp(imageID, "DDS ", 4) != 0) { fclose(fp); return FALSE; } UINT dwHeight = 0, dwWidth = 0, dwLinearSize, dwMipMaps = 0, dwFourCC = 0; fseek(fp, ddsHeightOffset, SEEK_SET); fread(&dwHeight, sizeof(UINT), 1, fp); if(byteSwap == TRUE) SwapBytes((CHAR*)&dwHeight, sizeof(UINT)); fseek(fp, ddsWidthOffset, SEEK_SET); fread(&dwWidth, sizeof(UINT), 1, fp); if(byteSwap == TRUE) SwapBytes((CHAR*)&dwWidth, sizeof(UINT)); fseek(fp, ddsLinearSizeOffset, SEEK_SET); fread(&dwLinearSize, sizeof(UINT), 1, fp); if(byteSwap == TRUE) SwapBytes((CHAR*)&dwLinearSize, sizeof(UINT)); fseek(fp, ddsMipMapNumOffset, SEEK_SET); fread(&dwMipMaps, sizeof(UINT), 1, fp); if(byteSwap == TRUE) SwapBytes((CHAR*)&dwMipMaps, sizeof(UINT)); fseek(fp, ddsFourCCOffset, SEEK_SET); fread(&dwFourCC, sizeof(UINT), 1, fp); if(byteSwap == TRUE) SwapBytes((CHAR*)&dwFourCC, sizeof(UINT)); if(dwLinearSize == 0) dwLinearSize = dwHeight * dwWidth; if(dwLinearSize <= 0) { fclose(fp); return nullptr; } info.m_numMipMaps = dwMipMaps; info.m_width = dwWidth; info.m_height = dwHeight; INT mipFactor = 0; switch(dwFourCC) { case DS_FOURCC_DXT1: mipFactor = 2; info.m_components = 3; info.m_type = DDS_DXT1; break; case DS_FOURCC_DXT3: mipFactor = 4; info.m_components = 4; info.m_type = DDS_DXT3; break; case DS_FOURCC_DXT5: mipFactor = 4; info.m_components = 4; info.m_type = DDS_DXT5; break; default: fclose(fp); return nullptr; break; } INT totalSize = 0; // Take into account multiple mipmaps. if(dwMipMaps > 1) totalSize = dwLinearSize * mipFactor; else totalSize = dwLinearSize; UCHAR* image = nullptr; image = new UCHAR[totalSize * sizeof(UCHAR)]; if(image != nullptr) { fseek(fp, ddsImageDataOffset, SEEK_SET); fread(image, 1, totalSize, fp); } fclose(fp); return image; };
/** KTXファイルを読み込む. */ TexturePtr LoadKTX(const char* filename, GLint minFilter, GLint magFilter) { auto file = Mai::FileSystem::Open(filename); if (!file) { LOGW("cannot open:'%s'", filename); return nullptr; } const size_t size = file->Size(); if (size <= sizeof(KTXHeader)) { LOGW("illegal size:'%s'", filename); return nullptr; } KTXHeader header; int result = file->Read(&header, sizeof(KTXHeader)); if (result < 0 || !IsKTXHeader(header)) { LOGW("illegal header:'%s'", filename); return nullptr; } TexturePtr p = std::make_shared<Texture>(); Texture& tex = static_cast<Texture&>(*p); const Endian endianness = GetEndian(header); const GLenum type = GetValue(&header.glType, endianness); const GLenum format = GetValue(type ? &header.glFormat : &header.glBaseInternalFormat, endianness); const int faceCount = GetValue(&header.numberOfFaces, endianness); const int mipCount = GetValue(&header.numberOfMipmapLevels, endianness); tex.internalFormat = GetValue(type ? &header.glBaseInternalFormat : &header.glInternalFormat, endianness); tex.width = GetValue(&header.pixelWidth, endianness); tex.height = GetValue(&header.pixelHeight, endianness); tex.target = faceCount == 6 ? GL_TEXTURE_CUBE_MAP : GL_TEXTURE_2D; const uint32_t off = GetValue(&header.bytesOfKeyValueData, endianness); file->Seek(file->Position() + off); glGenTextures(1, &tex.texId); glBindTexture(tex.Target(), tex.texId); std::vector<uint8_t> data; GLsizei curWidth = tex.Width(); GLsizei curHeight = tex.Height(); for (int mipLevel = 0; mipLevel < (mipCount ? mipCount : 1); ++mipLevel) { uint32_t imageSize; result = file->Read(&imageSize, 4); if (result < 0) { LOGW("can't read(miplevel=%d):'%s'", mipLevel, filename); return nullptr; } imageSize = GetValue(&imageSize, endianness); const uint32_t imageSizeWithPadding = (imageSize + 3) & ~3; data.resize(imageSizeWithPadding * faceCount); result = file->Read(&data[0], data.size()); if (result < 0) { LOGW("can't read(miplevel=%d):'%s'", mipLevel, filename); return nullptr; } const uint8_t* pImage = reinterpret_cast<const uint8_t*>(&data[0]); const GLenum target = tex.Target() == GL_TEXTURE_CUBE_MAP ? GL_TEXTURE_CUBE_MAP_POSITIVE_X : GL_TEXTURE_2D; for (int faceIndex = 0; faceIndex < faceCount; ++faceIndex) { if (type == 0) { glCompressedTexImage2D(target + faceIndex, mipLevel, tex.InternalFormat(), curWidth, curHeight, 0, imageSize, pImage); } else { glTexImage2D(target + faceIndex, mipLevel, tex.InternalFormat(), curWidth, curHeight, 0, format, type, pImage); } const GLenum result = glGetError(); switch(result) { case GL_NO_ERROR: break; case GL_INVALID_OPERATION: LOGW("glCompressed/TexImage2D return GL_INVALID_OPERATION"); break; default: LOGW("glCompressed/TexImage2D return error code 0x%04x", result); break; } pImage += imageSizeWithPadding; } curWidth = std::max(1, curWidth / 2); curHeight = std::max(1, curHeight / 2); } glTexParameteri(tex.Target(), GL_TEXTURE_MIN_FILTER, CorrectFilter(mipCount, minFilter)); glTexParameteri(tex.Target(), GL_TEXTURE_MAG_FILTER, CorrectFilter(mipCount, magFilter)); glTexParameteri(tex.Target(), GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(tex.Target(), GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glBindTexture(tex.Target(), 0); LOGI("Load %s(ID:%x).", filename, tex.texId); return p; }