mirror of
https://github.com/thes3m/XNI
synced 2024-12-26 13:26:06 +01:00
Added support for .pvr textures. (Make sure you use texture tools as described in apple documentation)
This commit is contained in:
parent
0dfd64fc9c
commit
788b374269
@ -106,7 +106,7 @@
|
||||
|
||||
if ([extension isEqualToString:@"png"] || [extension isEqualToString:@"jpg"] || [extension isEqualToString:@"jpeg"] ||
|
||||
[extension isEqualToString:@"gif"] || [extension isEqualToString:@"tif"] || [extension isEqualToString:@"tiff"] ||
|
||||
[extension isEqualToString:@"ico"] || [extension isEqualToString:@"bmp"]) {
|
||||
[extension isEqualToString:@"ico"] || [extension isEqualToString:@"bmp"] || [extension isEqualToString:@"pvr"]) {
|
||||
// Texture content
|
||||
if (!importer) {
|
||||
importer = [[[TextureImporter alloc] init] autorelease];
|
||||
|
@ -12,7 +12,7 @@
|
||||
|
||||
@interface PixelBitmapContent : BitmapContent {
|
||||
SurfaceFormat format;
|
||||
int bytesPerPixel;
|
||||
float bytesPerPixel;
|
||||
}
|
||||
|
||||
- (id) initWithWidth:(int)theWidth height:(int)theHeight format:(SurfaceFormat)theFormat;
|
||||
|
@ -31,13 +31,13 @@
|
||||
- (Byte *) getPixelAtX:(int)x Y:(int)y {
|
||||
// Index into the data array at bytesPerPixel intervals.
|
||||
Byte *bytes = (Byte*)[pixelData bytes];
|
||||
return &bytes[(x + y * width) * bytesPerPixel];
|
||||
return &bytes[(int)((x + y * width) * bytesPerPixel)];
|
||||
}
|
||||
|
||||
- (void) setPixelAtX:(int)x Y:(int)y Value:(Byte *)value {
|
||||
// The value contains bytesPerPixel bytes.
|
||||
Byte *bytes = (Byte*)[pixelData bytes];
|
||||
memcpy(&bytes[(x + y * width) * bytesPerPixel], value, bytesPerPixel);
|
||||
memcpy(&bytes[(int)((x + y * width) * bytesPerPixel)], value, bytesPerPixel);
|
||||
}
|
||||
|
||||
@end
|
||||
|
@ -15,7 +15,7 @@
|
||||
|
||||
}
|
||||
|
||||
+ (BOOL) tryGetSizeInBytesOfSurfaceFormat:(SurfaceFormat)surfaceFormat sizeInBytes:(int*)sizeInBytes;
|
||||
+ (BOOL) tryGetSizeInBytesOfSurfaceFormat:(SurfaceFormat)surfaceFormat sizeInBytes:(float*)sizeInBytes;
|
||||
|
||||
+ (BOOL) tryGetVectorTypeOfSurfaceFormat:(SurfaceFormat)surfaceFormat vectorType:(Class*)type;
|
||||
|
||||
|
@ -13,7 +13,7 @@
|
||||
|
||||
@implementation VectorConverter
|
||||
|
||||
+ (BOOL) tryGetSizeInBytesOfSurfaceFormat:(SurfaceFormat)surfaceFormat sizeInBytes:(int*)sizeInBytes {
|
||||
+ (BOOL) tryGetSizeInBytesOfSurfaceFormat:(SurfaceFormat)surfaceFormat sizeInBytes:(float*)sizeInBytes {
|
||||
switch (surfaceFormat) {
|
||||
case SurfaceFormatColor:
|
||||
*sizeInBytes = sizeof(Byte4);
|
||||
@ -29,7 +29,15 @@
|
||||
return YES;
|
||||
case SurfaceFormatAlpha8:
|
||||
*sizeInBytes = sizeof(Alpha8);
|
||||
return YES;
|
||||
return YES;
|
||||
case SurfaceFormatPvrtc4bAlpha:
|
||||
case SurfaceFormatPvrtc4b:
|
||||
*sizeInBytes = 0.5f;
|
||||
return YES;
|
||||
case SurfaceFormatPvrtc2bAlpha:
|
||||
case SurfaceFormatPvrtc2b:
|
||||
*sizeInBytes = 0.25f;
|
||||
return YES;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -13,49 +13,202 @@
|
||||
#import "Retronator.Xni.Framework.Content.Pipeline.h"
|
||||
#import "Retronator.Xni.Framework.Content.Pipeline.Graphics.h"
|
||||
|
||||
#define PVR_TEXTURE_FLAG_TYPE_MASK 0xff
|
||||
|
||||
|
||||
static char gPVRTexIdentifier[4] = "PVR!";
|
||||
|
||||
enum
|
||||
{
|
||||
kPVRTextureFlagTypePVRTC_2 = 24,
|
||||
kPVRTextureFlagTypePVRTC_4
|
||||
};
|
||||
|
||||
typedef struct _PVRTexHeader
|
||||
{
|
||||
uint32_t headerLength;
|
||||
uint32_t height;
|
||||
uint32_t width;
|
||||
uint32_t numMipmaps;
|
||||
uint32_t flags;
|
||||
uint32_t dataLength;
|
||||
uint32_t bpp;
|
||||
uint32_t bitmaskRed;
|
||||
uint32_t bitmaskGreen;
|
||||
uint32_t bitmaskBlue;
|
||||
uint32_t bitmaskAlpha;
|
||||
uint32_t pvrTag;
|
||||
uint32_t numSurfs;
|
||||
} PVRTexHeader;
|
||||
|
||||
@implementation TextureImporter
|
||||
|
||||
- (TextureContent*) importFile:(NSString*)filename {
|
||||
NSData *textureData = [NSData dataWithContentsOfFile:filename];
|
||||
UIImage *image = [UIImage imageWithData:textureData];
|
||||
if (image == nil) {
|
||||
[NSException raise:@"InvalidArgumentException" format:@"The provided file is not a supported texture resource."];
|
||||
}
|
||||
|
||||
// Allocate space for raw data.
|
||||
GLuint width = CGImageGetWidth(image.CGImage);
|
||||
GLuint height = CGImageGetHeight(image.CGImage);
|
||||
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
|
||||
void *imageData = malloc(width * height * 4);
|
||||
- (TextureContent*) importFile:(NSString*)filename {
|
||||
NSData *textureData = [NSData dataWithContentsOfFile:filename];
|
||||
|
||||
// Create a context for drawing to the raw data space.
|
||||
CGContextRef textureContext = CGBitmapContextCreate(imageData, width, height, 8, 4 * width, colorSpace,
|
||||
kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big );
|
||||
if ([[filename pathExtension] isEqualToString:@"pvr"]) {
|
||||
//Import pvrtc texture
|
||||
NSMutableArray *images = [NSMutableArray array];
|
||||
SurfaceFormat format;
|
||||
int width;
|
||||
int height;
|
||||
|
||||
[self unpackPVR:textureData to:images format:&format width:&width height:&height];
|
||||
if (images.count == 0) {
|
||||
[NSException raise:@"InvalidArgumentException" format:@"Make sure that .pvr texture is properly compressed and that the file has correct header. Use this command in terminal:/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/texturetool -e PVRTC -f PVR --channel-weighting-perceptual --bits-per-pixel-4 -o output.pvr input.png"];
|
||||
}
|
||||
|
||||
// This bitmap is the only one in the mipmap chain.
|
||||
MipmapChain *mipmaps = [[[MipmapChain alloc] init] autorelease];
|
||||
|
||||
for(NSData *imgData in images){
|
||||
PixelBitmapContent *bitmap = [[PixelBitmapContent alloc] initWithWidth:(int)width height:(int)height format:format];
|
||||
NSLog(@"Storing compressed texture with data length:%d", [imgData length]);
|
||||
[bitmap setPixelData:[NSData dataWithBytes:[imgData bytes] length:[imgData length]]];
|
||||
[mipmaps addObject:bitmap];
|
||||
[bitmap release];
|
||||
}
|
||||
|
||||
// Create the texture content.
|
||||
Texture2DContent *content = [[[Texture2DContent alloc] init] autorelease];
|
||||
content.identity.sourceFilename = filename;
|
||||
content.mipmaps = mipmaps;
|
||||
return content;
|
||||
}else{
|
||||
|
||||
UIImage *image = [UIImage imageWithData:textureData];
|
||||
if (image == nil) {
|
||||
[NSException raise:@"InvalidArgumentException" format:@"The provided file is not a supported texture resource."];
|
||||
}
|
||||
|
||||
// Allocate space for raw data.
|
||||
GLuint width = CGImageGetWidth(image.CGImage);
|
||||
GLuint height = CGImageGetHeight(image.CGImage);
|
||||
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
|
||||
void *imageData = malloc(width * height * 4);
|
||||
|
||||
// Create a context for drawing to the raw data space.
|
||||
CGContextRef textureContext = CGBitmapContextCreate(imageData, width, height, 8, 4 * width, colorSpace,
|
||||
kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big );
|
||||
|
||||
// Draw the image to our context.
|
||||
CGContextClearRect(textureContext, CGRectMake(0, 0, width, height));
|
||||
CGContextTranslateCTM(textureContext, 0, 0);
|
||||
CGContextDrawImage(textureContext, CGRectMake(0, 0, width, height), image.CGImage);
|
||||
|
||||
// Create pixel bitmap content.
|
||||
PixelBitmapContent *bitmap = [[[PixelBitmapContent alloc] initWithWidth:(int)width height:(int)height format:SurfaceFormatColor] autorelease];
|
||||
[bitmap setPixelData:[NSData dataWithBytes:imageData length:width*height*4]];
|
||||
|
||||
// Clean up.
|
||||
CGColorSpaceRelease(colorSpace);
|
||||
CGContextRelease(textureContext);
|
||||
free(imageData);
|
||||
|
||||
// This bitmap is the only one in the mipmap chain.
|
||||
MipmapChain *mipmaps = [[[MipmapChain alloc] init] autorelease];
|
||||
[mipmaps addObject:bitmap];
|
||||
|
||||
// Create the texture content.
|
||||
Texture2DContent *content = [[[Texture2DContent alloc] init] autorelease];
|
||||
content.identity.sourceFilename = filename;
|
||||
content.mipmaps = mipmaps;
|
||||
|
||||
return content;
|
||||
// Draw the image to our context.
|
||||
CGContextClearRect(textureContext, CGRectMake(0, 0, width, height));
|
||||
CGContextTranslateCTM(textureContext, 0, 0);
|
||||
CGContextDrawImage(textureContext, CGRectMake(0, 0, width, height), image.CGImage);
|
||||
|
||||
// Create pixel bitmap content.
|
||||
PixelBitmapContent *bitmap = [[[PixelBitmapContent alloc] initWithWidth:(int)width height:(int)height format:SurfaceFormatColor] autorelease];
|
||||
[bitmap setPixelData:[NSData dataWithBytes:imageData length:width*height*4]];
|
||||
|
||||
// Clean up.
|
||||
CGColorSpaceRelease(colorSpace);
|
||||
CGContextRelease(textureContext);
|
||||
free(imageData);
|
||||
|
||||
// This bitmap is the only one in the mipmap chain.
|
||||
MipmapChain *mipmaps = [[[MipmapChain alloc] init] autorelease];
|
||||
[mipmaps addObject:bitmap];
|
||||
|
||||
// Create the texture content.
|
||||
Texture2DContent *content = [[[Texture2DContent alloc] init] autorelease];
|
||||
content.identity.sourceFilename = filename;
|
||||
content.mipmaps = mipmaps;
|
||||
|
||||
return content;
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL) unpackPVR:(NSData*)data to:(NSMutableArray*)imageData format:(GLenum*)formatOut width:(int*)widthOut height:(int*)heightOut{
|
||||
|
||||
BOOL success = FALSE;
|
||||
PVRTexHeader *header = NULL;
|
||||
uint32_t flags, pvrTag;
|
||||
uint32_t dataLength = 0, dataOffset = 0, dataSize = 0;
|
||||
uint32_t blockSize = 0, widthBlocks = 0, heightBlocks = 0;
|
||||
uint32_t width = 0, height = 0, bpp = 4;
|
||||
uint8_t *bytes = NULL;
|
||||
uint32_t formatFlags;
|
||||
|
||||
header = (PVRTexHeader *)[data bytes];
|
||||
|
||||
pvrTag = CFSwapInt32LittleToHost(header->pvrTag);
|
||||
|
||||
if (gPVRTexIdentifier[0] != ((pvrTag >> 0) & 0xff) ||
|
||||
gPVRTexIdentifier[1] != ((pvrTag >> 8) & 0xff) ||
|
||||
gPVRTexIdentifier[2] != ((pvrTag >> 16) & 0xff) ||
|
||||
gPVRTexIdentifier[3] != ((pvrTag >> 24) & 0xff))
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
flags = CFSwapInt32LittleToHost(header->flags);
|
||||
formatFlags = flags & PVR_TEXTURE_FLAG_TYPE_MASK;
|
||||
|
||||
if (formatFlags == kPVRTextureFlagTypePVRTC_4 || formatFlags == kPVRTextureFlagTypePVRTC_2)
|
||||
{
|
||||
[imageData removeAllObjects];
|
||||
|
||||
BOOL alpha = FALSE;
|
||||
|
||||
if (CFSwapInt32LittleToHost(header->bitmaskAlpha))
|
||||
alpha = TRUE;
|
||||
else
|
||||
alpha = FALSE;
|
||||
|
||||
if (formatFlags == kPVRTextureFlagTypePVRTC_4 && alpha)
|
||||
*formatOut = SurfaceFormatPvrtc4bAlpha;
|
||||
else if (formatFlags == kPVRTextureFlagTypePVRTC_4 && !alpha)
|
||||
*formatOut = SurfaceFormatPvrtc4b;
|
||||
else if (formatFlags == kPVRTextureFlagTypePVRTC_2 && alpha)
|
||||
*formatOut = SurfaceFormatPvrtc2bAlpha;
|
||||
else if (formatFlags == kPVRTextureFlagTypePVRTC_2 && !alpha)
|
||||
*formatOut = SurfaceFormatPvrtc2b;
|
||||
|
||||
*widthOut = width = CFSwapInt32LittleToHost(header->width);
|
||||
*heightOut = height = CFSwapInt32LittleToHost(header->height);
|
||||
|
||||
dataLength = CFSwapInt32LittleToHost(header->dataLength);
|
||||
|
||||
bytes = ((uint8_t *)[data bytes]) + sizeof(PVRTexHeader);
|
||||
|
||||
// Calculate the data size for each texture level and respect the minimum number of blocks
|
||||
while (dataOffset < dataLength)
|
||||
{
|
||||
if (formatFlags == kPVRTextureFlagTypePVRTC_4)
|
||||
{
|
||||
blockSize = 4 * 4; // Pixel by pixel block size for 4bpp
|
||||
widthBlocks = width / 4;
|
||||
heightBlocks = height / 4;
|
||||
bpp = 4;
|
||||
}
|
||||
else
|
||||
{
|
||||
blockSize = 8 * 4; // Pixel by pixel block size for 2bpp
|
||||
widthBlocks = width / 8;
|
||||
heightBlocks = height / 4;
|
||||
bpp = 2;
|
||||
}
|
||||
|
||||
// Clamp to minimum number of blocks
|
||||
if (widthBlocks < 2)
|
||||
widthBlocks = 2;
|
||||
if (heightBlocks < 2)
|
||||
heightBlocks = 2;
|
||||
|
||||
dataSize = widthBlocks * heightBlocks * ((blockSize * bpp) / 8);
|
||||
|
||||
[imageData addObject:[NSData dataWithBytes:bytes+dataOffset length:dataSize]];
|
||||
|
||||
dataOffset += dataSize;
|
||||
|
||||
width = MAX(width >> 1, 1);
|
||||
height = MAX(height >> 1, 1);
|
||||
}
|
||||
|
||||
success = TRUE;
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
@end
|
||||
|
@ -32,7 +32,7 @@
|
||||
|
||||
for (int i=0;i<[mipmaps count];i++) {
|
||||
bitmap = [mipmaps objectAtIndex:i];
|
||||
[texture setDataToLevel:i sourceRectangle:nil from:(void*)[[bitmap getPixelData] bytes]];
|
||||
[texture setDataToLevel:i sourceRectangle:nil from:(void*)[[bitmap getPixelData] bytes] ];
|
||||
}
|
||||
|
||||
return texture;
|
||||
|
@ -19,6 +19,7 @@
|
||||
#import "IndexBuffer+Internal.h"
|
||||
#import "VertexBuffer+Internal.h"
|
||||
#import "RenderTarget2D+Internal.h"
|
||||
#import "VectorConverter.h"
|
||||
|
||||
@interface GraphicsDevice(){
|
||||
BOOL rrt;
|
||||
@ -363,7 +364,7 @@
|
||||
glDeleteTextures(1, &textureId);
|
||||
}
|
||||
|
||||
- (void) setData:(void*)data toTexture2D:(Texture2D*)texture SourceRectangle:(Rectangle*)rect level:(int)level {
|
||||
- (void) setData:(void*)data toTexture2D:(Texture2D*)texture SourceRectangle:(Rectangle*)rect level:(int)level{
|
||||
GLenum format, type;
|
||||
[GraphicsDevice getFormat:&format AndType:&type ForSurfaceFormat:texture.format];
|
||||
|
||||
@ -374,12 +375,30 @@
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
|
||||
|
||||
if (rect) {
|
||||
glTexSubImage2D(GL_TEXTURE_2D, level, rect.x, rect.y, rect.width, rect.height, format, type, data);
|
||||
} else {
|
||||
glTexImage2D(GL_TEXTURE_2D, level, format, texture.width, texture.height, 0, format, type, data);
|
||||
if (texture.format == SurfaceFormatPvrtc4bAlpha || texture.format == SurfaceFormatPvrtc4b || texture.format == SurfaceFormatPvrtc2bAlpha || texture.format == SurfaceFormatPvrtc2b) {
|
||||
float bytesPerPixel = 0;
|
||||
BOOL hasValue = [VectorConverter tryGetSizeInBytesOfSurfaceFormat:texture.format sizeInBytes:&bytesPerPixel];
|
||||
if (hasValue) {
|
||||
int size = (int)(bytesPerPixel*texture.width*texture.height);
|
||||
glCompressedTexImage2D(GL_TEXTURE_2D, level, format, texture.width, texture.height, 0, size, data);
|
||||
|
||||
GLenum err = glGetError();
|
||||
if (err != GL_NO_ERROR)
|
||||
{
|
||||
NSLog(@"Error uploading compressed texture level: %d. glError: 0x%04X", level, err);
|
||||
}
|
||||
|
||||
}else{
|
||||
[NSException raise:@"ArgumentException" format:@"The provided format is not supported"];
|
||||
}
|
||||
}else{
|
||||
if (rect) {
|
||||
glTexSubImage2D(GL_TEXTURE_2D, level, rect.x, rect.y, rect.width, rect.height, format, type, data);
|
||||
} else {
|
||||
glTexImage2D(GL_TEXTURE_2D, level, format, texture.width, texture.height, 0, format, type, data);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
}
|
||||
|
||||
@ -480,6 +499,18 @@
|
||||
*format = GL_RGBA;
|
||||
*type = GL_UNSIGNED_SHORT_5_5_5_1;
|
||||
break;
|
||||
case SurfaceFormatPvrtc4bAlpha:
|
||||
*format = GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG;
|
||||
break;
|
||||
case SurfaceFormatPvrtc4b:
|
||||
*format = GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG;
|
||||
break;
|
||||
case SurfaceFormatPvrtc2bAlpha:
|
||||
*format = GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG;
|
||||
break;
|
||||
case SurfaceFormatPvrtc2b:
|
||||
*format = GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -187,7 +187,11 @@ typedef enum {
|
||||
//SurfaceFormatHalfSingle,
|
||||
//SurfaceFormatHalfVector2,
|
||||
//SurfaceFormatHalfVector4,
|
||||
//SurfaceFormatHdrBlendable
|
||||
//SurfaceFormatHdrBlendable
|
||||
SurfaceFormatPvrtc4b,
|
||||
SurfaceFormatPvrtc2b,
|
||||
SurfaceFormatPvrtc4bAlpha,
|
||||
SurfaceFormatPvrtc2bAlpha
|
||||
} SurfaceFormat;
|
||||
|
||||
typedef enum {
|
||||
|
Loading…
x
Reference in New Issue
Block a user