Skip to content

Commit aa66653

Browse files
authored
Merge pull request openscenegraph#31 from bmdhacks/bmd-openmw-astc-support
ASTC texture support
2 parents 43faf6f + f64cc4e commit aa66653

3 files changed

Lines changed: 109 additions & 20 deletions

File tree

src/osg/Image.cpp

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -806,15 +806,15 @@ unsigned int Image::computePixelSizeInBits(GLenum format,GLenum type)
806806
{
807807
osg::Vec3i footprint = computeBlockFootprint(format);
808808
unsigned int pixelsPerBlock = footprint.x() * footprint.y();
809-
unsigned int bitsPerBlock = computeBlockSize(format, 0);//16 x 8 = 128
809+
unsigned int bitsPerBlock = computeBlockSize(format, 0) * 8; // Convert bytes to bits
810810
unsigned int bitsPerPixel = bitsPerBlock / pixelsPerBlock;
811811
if (bitsPerBlock == bitsPerPixel * pixelsPerBlock) {
812-
OSG_WARN << "Image::computePixelSizeInBits(format,type) : bits per pixel (" << bitsPerPixel << ") is not an integer for GL_KHR_texture_compression_astc_hdr sizes other than 4x4 and 8x8." << std::endl;
812+
// Integer division worked perfectly
813813
return bitsPerPixel;
814814
} else {
815815
OSG_WARN << "Image::computePixelSizeInBits(format,type) : bits per pixel (" << bitsPerBlock << "/" << pixelsPerBlock << ") is not an integer for GL_KHR_texture_compression_astc_hdr size" << footprint.x() << "x" << footprint.y() << "." << std::endl;
816+
return 0;
816817
}
817-
return 0;
818818
}
819819
default: break;
820820
}
@@ -1827,6 +1827,7 @@ void Image::flipVertical()
18271827

18281828
const bool dxtc(dxtc_tool::isDXTC(_pixelFormat));
18291829
const bool rgtc(dxtc_tool::isRGTC(_pixelFormat));
1830+
18301831
if (_mipmapData.empty())
18311832
{
18321833
// no mipmaps,
@@ -1842,7 +1843,7 @@ void Image::flipVertical()
18421843
}
18431844
else
18441845
{
1845-
if (isCompressed()) OSG_NOTICE << "Notice Image::flipVertical(): image is compressed but normal v-flip is used" << std::endl;
1846+
if (isCompressed()) OSG_NOTICE << "Notice Image::flipVertical(): file=" << _fileName << " image is compressed but normal v-flip is used" << std::endl;
18461847
// its not a compressed image, so implement flip oursleves.
18471848
unsigned char* top = data(0,0,r);
18481849
unsigned char* bottom = top + (_t-1)*rowStep;

src/osg/Texture.cpp

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1995,85 +1995,85 @@ void Texture::getCompressedSize(GLenum internalFormat, GLint width, GLint height
19951995
else if (internalFormat == GL_COMPRESSED_RGBA_ASTC_4x4_KHR || internalFormat == GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR)
19961996
{
19971997
blockSize = 16;
1998-
size = ceil(width/4.0)*ceil(height/4.0)*blockSize;
1998+
size = ((width+3)/4)*((height+3)/4)*depth*blockSize;
19991999
return;
20002000
}
20012001
else if (internalFormat == GL_COMPRESSED_RGBA_ASTC_5x4_KHR || internalFormat == GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR)
20022002
{
20032003
blockSize = 16;
2004-
size = ceil(width/5.0)*ceil(height/4.0)*blockSize;
2004+
size = ((width+4)/5)*((height+3)/4)*depth*blockSize;
20052005
return;
20062006
}
20072007
else if (internalFormat == GL_COMPRESSED_RGBA_ASTC_5x5_KHR || internalFormat == GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR)
20082008
{
20092009
blockSize = 16;
2010-
size = ceil(width/5.0)*ceil(height/5.0)*blockSize;
2010+
size = ((width+4)/5)*((height+4)/5)*depth*blockSize;
20112011
return;
20122012
}
20132013
else if (internalFormat == GL_COMPRESSED_RGBA_ASTC_6x5_KHR || internalFormat == GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR)
20142014
{
20152015
blockSize = 16;
2016-
size = ceil(width/6.0)*ceil(height/5.0)*blockSize;
2016+
size = ((width+5)/6)*((height+4)/5)*depth*blockSize;
20172017
return;
20182018
}
20192019
else if (internalFormat == GL_COMPRESSED_RGBA_ASTC_6x6_KHR || internalFormat == GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR)
20202020
{
20212021
blockSize = 16;
2022-
size = ceil(width/6.0)*ceil(height/6.0)*blockSize;
2022+
size = ((width+5)/6)*((height+5)/6)*depth*blockSize;
20232023
return;
20242024
}
20252025
else if (internalFormat == GL_COMPRESSED_RGBA_ASTC_8x5_KHR || internalFormat == GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR)
20262026
{
20272027
blockSize = 16;
2028-
size = ceil(width/8.0)*ceil(height/5.0)*blockSize;
2028+
size = ((width+7)/8)*((height+4)/5)*depth*blockSize;
20292029
return;
20302030
}
20312031
else if (internalFormat == GL_COMPRESSED_RGBA_ASTC_8x6_KHR || internalFormat == GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR)
20322032
{
20332033
blockSize = 16;
2034-
size = ceil(width/8.0)*ceil(height/6.0)*blockSize;
2034+
size = ((width+7)/8)*((height+5)/6)*depth*blockSize;
20352035
return;
20362036
}
20372037
else if (internalFormat == GL_COMPRESSED_RGBA_ASTC_8x8_KHR || internalFormat == GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR)
20382038
{
20392039
blockSize = 16;
2040-
size = ceil(width/8.0)*ceil(height/8.0)*blockSize;
2040+
size = ((width+7)/8)*((height+7)/8)*depth*blockSize;
20412041
return;
20422042
}
20432043
else if (internalFormat == GL_COMPRESSED_RGBA_ASTC_10x5_KHR || internalFormat == GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR)
20442044
{
20452045
blockSize = 16;
2046-
size = ceil(width/10.0)*ceil(height/5.0)*blockSize;
2046+
size = ((width+9)/10)*((height+4)/5)*depth*blockSize;
20472047
return;
20482048
}
20492049
else if (internalFormat == GL_COMPRESSED_RGBA_ASTC_10x6_KHR || internalFormat == GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR)
20502050
{
20512051
blockSize = 16;
2052-
size = ceil(width/10.0)*ceil(height/6.0)*blockSize;
2052+
size = ((width+9)/10)*((height+5)/6)*depth*blockSize;
20532053
return;
20542054
}
20552055
else if (internalFormat == GL_COMPRESSED_RGBA_ASTC_10x8_KHR || internalFormat == GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR)
20562056
{
20572057
blockSize = 16;
2058-
size = ceil(width/10.0)*ceil(height/8.0)*blockSize;
2058+
size = ((width+9)/10)*((height+7)/8)*depth*blockSize;
20592059
return;
20602060
}
20612061
else if (internalFormat == GL_COMPRESSED_RGBA_ASTC_10x10_KHR || internalFormat == GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR)
20622062
{
20632063
blockSize = 16;
2064-
size = ceil(width/10.0)*ceil(height/10.0)*blockSize;
2064+
size = ((width+9)/10)*((height+9)/10)*depth*blockSize;
20652065
return;
20662066
}
20672067
else if (internalFormat == GL_COMPRESSED_RGBA_ASTC_12x10_KHR || internalFormat == GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR)
20682068
{
20692069
blockSize = 16;
2070-
size = ceil(width/12.0)*ceil(height/10.0)*blockSize;
2070+
size = ((width+11)/12)*((height+9)/10)*depth*blockSize;
20712071
return;
20722072
}
20732073
else if (internalFormat == GL_COMPRESSED_RGBA_ASTC_12x12_KHR || internalFormat == GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR)
20742074
{
20752075
blockSize = 16;
2076-
size = ceil(width/12.0)*ceil(height/12.0)*blockSize;
2076+
size = ((width+11)/12)*((height+11)/12)*depth*blockSize;
20772077
return;
20782078
}
20792079
else

src/osgPlugins/ktx/ReaderWriterKTX.cpp

Lines changed: 90 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,13 @@
1313

1414
#include "ReaderWriterKTX.h"
1515
#include <osg/Endian>
16+
#include <osg/ValueObject>
1617
#include <osgDB/FileNameUtils>
1718
#include <osgDB/FileUtils>
1819
#include <istream>
20+
#include <vector>
21+
#include <cstring>
22+
#include <map>
1923

2024
// Macro similar to what's in FLT/TRP plugins (except it uses wide char under Windows if OSG_USE_UTF8_FILENAME)
2125
#if defined(_WIN32)
@@ -115,8 +119,66 @@ osgDB::ReaderWriter::ReadResult ReaderWriterKTX::readKTXStream(std::istream& fin
115119
if (header.numberOfMipmapLevels == 0)
116120
header.numberOfMipmapLevels = 1;
117121

118-
//read keyvalue data. Will be ignoring for now
119-
fin.ignore(header.bytesOfKeyValueData);
122+
// Parse key-value metadata
123+
std::map<std::string, std::string> ktxMetadata;
124+
125+
if (header.bytesOfKeyValueData > 0)
126+
{
127+
std::vector<char> kvData(header.bytesOfKeyValueData);
128+
fin.read(kvData.data(), header.bytesOfKeyValueData);
129+
if (!fin.good())
130+
{
131+
OSG_WARN << "Failed to read KTX key-value data." << std::endl;
132+
return ReadResult(ReadResult::ERROR_IN_READING_FILE);
133+
}
134+
135+
// Parse key-value pairs
136+
size_t offset = 0;
137+
while (offset < header.bytesOfKeyValueData)
138+
{
139+
if (offset + sizeof(uint32_t) > header.bytesOfKeyValueData)
140+
break;
141+
142+
uint32_t keyAndValueByteSize;
143+
memcpy(&keyAndValueByteSize, kvData.data() + offset, sizeof(uint32_t));
144+
if (header.endianness != MyEndian)
145+
osg::swapBytes4(reinterpret_cast<char*>(&keyAndValueByteSize));
146+
offset += sizeof(uint32_t);
147+
148+
if (offset + keyAndValueByteSize > header.bytesOfKeyValueData)
149+
break;
150+
151+
// Find the null terminator to separate key from value
152+
std::string key;
153+
size_t keyStart = offset;
154+
size_t keyEnd = keyStart;
155+
while (keyEnd < offset + keyAndValueByteSize && kvData[keyEnd] != '\0')
156+
keyEnd++;
157+
158+
if (keyEnd < offset + keyAndValueByteSize)
159+
{
160+
key = std::string(kvData.data() + keyStart, keyEnd - keyStart);
161+
162+
// Extract the value (everything after the null terminator)
163+
size_t valueStart = keyEnd + 1;
164+
size_t valueSize = keyAndValueByteSize - (valueStart - keyStart);
165+
std::string value(kvData.data() + valueStart, valueSize);
166+
167+
// Remove any trailing null bytes from the value
168+
size_t nullPos = value.find('\0');
169+
if (nullPos != std::string::npos)
170+
value = value.substr(0, nullPos);
171+
172+
// Store the metadata
173+
ktxMetadata[key] = value;
174+
}
175+
176+
// Align to 4 bytes
177+
offset += keyAndValueByteSize;
178+
uint32_t padding = (4 - (keyAndValueByteSize % 4)) % 4;
179+
offset += padding;
180+
}
181+
}
120182

121183
uint32_t imageSize;
122184
uint32_t totalImageSize = fileLength -
@@ -210,9 +272,35 @@ osgDB::ReaderWriter::ReadResult ReaderWriterKTX::readKTXStream(std::istream& fin
210272
header.glInternalFormat, header.glFormat,
211273
header.glType, totalImageData, osg::Image::USE_NEW_DELETE);
212274

275+
// Set origin based on KTXorientation metadata
276+
// S=r,T=u means OpenGL orientation (bottom-left origin)
277+
// S=r,T=d means standard image orientation (top-left origin)
278+
// Note: some files incorrectly use KTXOrientation with capital O
279+
auto orientIt = ktxMetadata.find("KTXorientation");
280+
if (orientIt == ktxMetadata.end())
281+
orientIt = ktxMetadata.find("KTXOrientation");
282+
283+
if (orientIt != ktxMetadata.end() &&
284+
(orientIt->second == "S=r,T=u" || orientIt->second == "S=r,T=u,R=o"))
285+
{
286+
image->setOrigin(osg::Image::BOTTOM_LEFT);
287+
}
288+
else
289+
{
290+
// Default to TOP_LEFT for S=r,T=d or when no orientation is specified
291+
image->setOrigin(osg::Image::TOP_LEFT);
292+
}
293+
213294
if (header.numberOfMipmapLevels > 1)
214295
image->setMipmapLevels(mipmapData);
215296

297+
// Store all KTX metadata in the image's userdata with "KTX:" prefix
298+
// This avoids conflicts with other OSG metadata
299+
for (const auto& kv : ktxMetadata)
300+
{
301+
image->setUserValue("KTX:" + kv.first, kv.second);
302+
}
303+
216304
return image.get();
217305
}
218306

0 commit comments

Comments
 (0)