Compressed Textures from Apple's texturetool
3 posters
Compressed Textures from Apple's texturetool
So I thought I would have this out sooner, but I just found out that Imagination Tech provides some code in their SDK that will decompress images. Integrating this significantly streamlines the code and workflow, so I added it to SIO2. However it's unclear to me if a custom importer for Blender could be made, so the managing of images for Blender isn't as clean. In order to do this with Blender I've added a bit of meta data in the filename. Which also allows you know something about the images in Finder. I haven't integrated alpha yet, but I wanted to see if there is any feedback.
I should point out these changes are for a workflow which utilizes the raw PVRTC compressed data that Apple's texturetool produces. And not for Imagination Tech's pvrtc file format. Also mipmaps are not supported.
You should read this Q&A doc on Apple's texturetool if you haven't.
http://developer.apple.com/iphone/library/qa/qa2008/qa1611.html
Apple's texturetool will generate 2 files for each compression type - a png for previewing and an .att (Apple texture tool) file for the compressed data. The files will be of the following format [original filename][meta data].pvr.png for the preview png and [original filename][meta data].att for the compressed data.
meta data is ['_'][bits per pixel: '2' or '4'][channel weighting type: 'l' or 'p'][mipmap level][alpha: '1' or '0'][size of square image as a power of two -2]
Here is an example: checkerboard_4p006.pvr.png and checkerboard_4p006.att
The original image was checkerboard.jpg.
I should point out these changes are for a workflow which utilizes the raw PVRTC compressed data that Apple's texturetool produces. And not for Imagination Tech's pvrtc file format. Also mipmaps are not supported.
You should read this Q&A doc on Apple's texturetool if you haven't.
http://developer.apple.com/iphone/library/qa/qa2008/qa1611.html
Apple's texturetool will generate 2 files for each compression type - a png for previewing and an .att (Apple texture tool) file for the compressed data. The files will be of the following format [original filename][meta data].pvr.png for the preview png and [original filename][meta data].att for the compressed data.
meta data is ['_'][bits per pixel: '2' or '4'][channel weighting type: 'l' or 'p'][mipmap level][alpha: '1' or '0'][size of square image as a power of two -2]
Here is an example: checkerboard_4p006.pvr.png and checkerboard_4p006.att
The original image was checkerboard.jpg.
zzajin- Posts : 81
Join date : 2008-10-14
Workflow
The basic workflow is like this:
1. Create assets as normal.
2. To compress an image I have written a Blender script which uses the CURRENT IMAGE. The current image is selected in Blender's UV/Image Editor window. You will find the script in the Image menu of the UV/Image Editor window or in the sub-menu image in a Scripts window.
3. The script has a number of options and when loaded it will display the current image it will work on.
The first is the output directory for the images that Apple's texturetool will generate. This is not the same as SIO2's exporter: feel free to put these images where ever you want.
There are toggles for the 4 different types of compression.
Two toggles for loading the images into blender after they have been created. By default I've unselected .att since they can't be displayed anyway, but you may want to load them to pack them into a .blend file or keep reference of them.
Level is a placeholder for now - ignore it for now.
Run will run the texturetool. Keep in mind that it may take awhile. There is feedback on the progress.
4. After you have decided which compressed image to use, make sure the png preview is properly applied to your model: shows up in the UV window when the model is in edit mode and applied in the texture buttons window.
5. Export SIO2 as normal. When the preview texture is exported the script will look for the corresponding .att file and copy that into the SIO2 archive. The exporter will just change the .pvr.png extention to .att. So don't change the names of files that are produced by the texturetool and keep the .pvr.png and .att files in the same directory.
6. SIO2 will decompress any .att images in the simulator during loading and on an iPhone will load the compressed data .
1. Create assets as normal.
2. To compress an image I have written a Blender script which uses the CURRENT IMAGE. The current image is selected in Blender's UV/Image Editor window. You will find the script in the Image menu of the UV/Image Editor window or in the sub-menu image in a Scripts window.
3. The script has a number of options and when loaded it will display the current image it will work on.
The first is the output directory for the images that Apple's texturetool will generate. This is not the same as SIO2's exporter: feel free to put these images where ever you want.
There are toggles for the 4 different types of compression.
Two toggles for loading the images into blender after they have been created. By default I've unselected .att since they can't be displayed anyway, but you may want to load them to pack them into a .blend file or keep reference of them.
Level is a placeholder for now - ignore it for now.
Run will run the texturetool. Keep in mind that it may take awhile. There is feedback on the progress.
4. After you have decided which compressed image to use, make sure the png preview is properly applied to your model: shows up in the UV window when the model is in edit mode and applied in the texture buttons window.
5. Export SIO2 as normal. When the preview texture is exported the script will look for the corresponding .att file and copy that into the SIO2 archive. The exporter will just change the .pvr.png extention to .att. So don't change the names of files that are produced by the texturetool and keep the .pvr.png and .att files in the same directory.
6. SIO2 will decompress any .att images in the simulator during loading and on an iPhone will load the compressed data .
zzajin- Posts : 81
Join date : 2008-10-14
Add imagination tech's decompress code
Add the following 2 files to the project and make sure the file type is sourcecode.cpp.objcpp
sio2_decompresspvr.h:
Add the include in sio.h at line 160
sio2_decompresspvr.h:
- Code:
#ifndef SIO2_DECOMPRESSPVR_H
#define SIO2_DECOMPRESSPVR_H
#include <assert.h>
const unsigned int ETC_MIN_TEXWIDTH = 4;
const unsigned int ETC_MIN_TEXHEIGHT = 4;
typedef unsigned long U32;
typedef unsigned char U8;
typedef struct
{
/* Uses 64 bits pre block*/
U32 PackedData[2];
}AMTC_BLOCK_STRUCT;
static void Decompress(AMTC_BLOCK_STRUCT *pCompressedData,
const int Do2bitMode,
const int XDim,
const int YDim,
const int AssumeImageTiles,
unsigned char* pResultImage);
void PVRTCDecompress(const void *pCompressedData,
const int Do2bitMode,
const int XDim,
const int YDim,
unsigned char* pResultImage);
#endif
Add the include in sio.h at line 160
- Code:
#include "sio2_decompresspvr.h"
zzajin- Posts : 81
Join date : 2008-10-14
Re: Compressed Textures from Apple's texturetool
sio2_decompresspvr.cc
part 1
part 1
- Code:
/******************************************************************************
@File PVRTDecompress.cpp
@Title
@Copyright Copyright (C) 2000 - 2008 by Imagination Technologies Limited.
@Platform ANSI compatible
@Description PVRTC Texture Decompression.
******************************************************************************/
#include "sio2.h"
/*****************************************************************************
* defines and consts
*****************************************************************************/
#define PT_INDEX (2) /*The Punch-through index*/
#define BLK_Y_SIZE (4) /*always 4 for all 2D block types*/
#define BLK_X_MAX (8) /*Max X dimension for blocks*/
#define BLK_X_2BPP (8) /*dimensions for the two formats*/
#define BLK_X_4BPP (4)
#define _MIN(X,Y) (((X)<(Y))? (X):(Y))
#define _MAX(X,Y) (((X)>(Y))? (X):(Y))
#define WRAP_COORD(Val, Size) ((Val) & ((Size)-1))
#define CLAMP(X, lower, upper) (_MIN(_MAX((X),(lower)), (upper)))
#define POWER_OF_2(X) util_number_is_power_2(X)
//
// Define an expression to either wrap or clamp large or small vals to the
// legal coordinate range
//
#define LIMIT_COORD(Val, Size, AssumeImageTiles) \
((AssumeImageTiles)? WRAP_COORD((Val), (Size)): CLAMP((Val), 0, (Size)-1))
//#define RGBA(r, g, b, a) ((GLuint) (((a) << 24) | ((b) << 16) | ((g) << 8) | (r)))
#define _ASSERT(X) assert(X)
//
//
//------------------------------ Decompressing Textures --------------------------------------
//
//
//
//
//
void PVRTCDecompress(const void *pCompressedData,
const int Do2bitMode,
const int XDim,
const int YDim,
unsigned char* pResultImage)
{
Decompress((AMTC_BLOCK_STRUCT*)pCompressedData,Do2bitMode,XDim,YDim,1,pResultImage);
}
/******************************************************************************
* Function Name: util_number_is_power_2
*
* Inputs : input - A number.
* Outputs : -
* Returns : TRUE if the number is an integer power of two, else FALSE.
* Globals Used : -
*
* Description : Check that a number is an integer power of two, i.e.
* 1, 2, 4, 8, ... etc.
* Returns FALSE for zero.
* Pre-condition: -
*****************************************************************************/
int util_number_is_power_2( unsigned input )
{
unsigned minus1;
if( !input ) return 0;
minus1 = input - 1;
return ( (input | minus1) == (input ^ minus1) ) ? 1 : 0;
}
/***********************************************************/
/*
// Unpack5554Colour
//
// Given a block, extract the colour information and convert to 5554 formats
*/
/***********************************************************/
static void Unpack5554Colour(const AMTC_BLOCK_STRUCT *pBlock,
int ABColours[2][4])
{
U32 RawBits[2];
int i;
/*
// Extract A and B
*/
RawBits[0] = pBlock->PackedData[1] & (0xFFFE); /*15 bits (shifted up by one)*/
RawBits[1] = pBlock->PackedData[1] >> 16; /*16 bits*/
/*
//step through both colours
*/
for(i = 0; i < 2; i++)
{
/*
// if completely opaque
*/
if(RawBits[i] & (1<<15))
{
/*
// Extract R and G (both 5 bit)
*/
ABColours[i][0] = (RawBits[i] >> 10) & 0x1F;
ABColours[i][1] = (RawBits[i] >> 5) & 0x1F;
/*
// The precision of Blue depends on A or B. If A then we need to
// replicate the top bit to get 5 bits in total
*/
ABColours[i][2] = RawBits[i] & 0x1F;
if(i==0)
{
ABColours[0][2] |= ABColours[0][2] >> 4;
}
/*
// set 4bit alpha fully on...
*/
ABColours[i][3] = 0xF;
}
/*
// Else if colour has variable translucency
*/
else
{
/*
// Extract R and G (both 4 bit).
// (Leave a space on the end for the replication of bits
*/
ABColours[i][0] = (RawBits[i] >> (8-1)) & 0x1E;
ABColours[i][1] = (RawBits[i] >> (4-1)) & 0x1E;
/*
// replicate bits to truly expand to 5 bits
*/
ABColours[i][0] |= ABColours[i][0] >> 4;
ABColours[i][1] |= ABColours[i][1] >> 4;
/*
// grab the 3(+padding) or 4 bits of blue and add an extra padding bit
*/
ABColours[i][2] = (RawBits[i] & 0xF) << 1;
/*
// expand from 3 to 5 bits if this is from colour A, or 4 to 5 bits if from
// colour B
*/
if(i==0)
{
ABColours[0][2] |= ABColours[0][2] >> 3;
}
else
{
ABColours[0][2] |= ABColours[0][2] >> 4;
}
/*
// Set the alpha bits to be 3 + a zero on the end
*/
ABColours[i][3] = (RawBits[i] >> 11) & 0xE;
}/*end if variable alpha*/
}/*end for i*/
}
/***********************************************************/
/*
// UnpackModulations
//
// Given the block and the texture type and it's relative position in the
// 2x2 group of blocks, extract the bit patterns for the fully defined pixels.
*/
/***********************************************************/
static void UnpackModulations(const AMTC_BLOCK_STRUCT *pBlock,
const int Do2bitMode,
int ModulationVals[8][16],
int ModulationModes[8][16],
int StartX,
int StartY)
{
int BlockModMode;
U32 ModulationBits;
int x, y;
BlockModMode= pBlock->PackedData[1] & 1;
ModulationBits = pBlock->PackedData[0];
/*
// if it's in an interpolated mode
*/
if(Do2bitMode && BlockModMode)
{
/*
// run through all the pixels in the block. Note we can now treat all the
// "stored" values as if they have 2bits (even when they didn't!)
*/
for(y = 0; y < BLK_Y_SIZE; y++)
{
for(x = 0; x < BLK_X_2BPP; x++)
{
ModulationModes[y+StartY][x+StartX] = BlockModMode;
/*
// if this is a stored value...
*/
if(((x^y)&1) == 0)
{
ModulationVals[y+StartY][x+StartX] = ModulationBits & 3;
ModulationBits >>= 2;
}
}
}/*end for y*/
}
/*
// else if direct encoded 2bit mode - i.e. 1 mode bit per pixel
*/
else if(Do2bitMode)
{
for(y = 0; y < BLK_Y_SIZE; y++)
{
for(x = 0; x < BLK_X_2BPP; x++)
{
ModulationModes[y+StartY][x+StartX] = BlockModMode;
/*
// double the bits so 0=> 00, and 1=>11
*/
if(ModulationBits & 1)
{
ModulationVals[y+StartY][x+StartX] = 0x3;
}
else
{
ModulationVals[y+StartY][x+StartX] = 0x0;
}
ModulationBits >>= 1;
}
}/*end for y*/
}
/*
// else its the 4bpp mode so each value has 2 bits
*/
else
{
for(y = 0; y < BLK_Y_SIZE; y++)
{
for(x = 0; x < BLK_X_4BPP; x++)
{
ModulationModes[y+StartY][x+StartX] = BlockModMode;
ModulationVals[y+StartY][x+StartX] = ModulationBits & 3;
ModulationBits >>= 2;
}
}/*end for y*/
}
/*
// make sure nothing is left over
*/
_ASSERT(ModulationBits==0);
}
/***********************************************************/
/*
// Interpolate Colours
//
//
// This performs a HW bit accurate interpolation of either the
// A or B colours for a particular pixel
//
// NOTE: It is assumed that the source colours are in ARGB 5554 format -
// This means that some "preparation" of the values will be necessary.
*/
/***********************************************************/
static void InterpolateColours(const int ColourP[4],
const int ColourQ[4],
const int ColourR[4],
const int ColourS[4],
const int Do2bitMode,
const int x,
const int y,
int Result[4])
{
int u, v, uscale;
int k;
int tmp1, tmp2;
int P[4], Q[4], R[4], S[4];
/*
// Copy the colours
*/
for(k = 0; k < 4; k++)
{
P[k] = ColourP[k];
Q[k] = ColourQ[k];
R[k] = ColourR[k];
S[k] = ColourS[k];
}
/*
// put the x and y values into the right range
*/
v = (y & 0x3) | ((~y & 0x2) << 1);
if(Do2bitMode)
{
u = (x & 0x7) | ((~x & 0x4) << 1);
}
else
{
u = (x & 0x3) | ((~x & 0x2) << 1);
}
/*
// get the u and v scale amounts
*/
v = v - BLK_Y_SIZE/2;
if(Do2bitMode)
{
u = u - BLK_X_2BPP/2;
uscale = 8;
}
else
{
u = u - BLK_X_4BPP/2;
uscale = 4;
}
for(k = 0; k < 4; k++)
{
tmp1 = P[k] * uscale + u * (Q[k] - P[k]);
tmp2 = R[k] * uscale + u * (S[k] - R[k]);
tmp1 = tmp1 * 4 + v * (tmp2 - tmp1);
Result[k] = tmp1;
}
/*
// Lop off the appropriate number of bits to get us to 8 bit precision
*/
if(Do2bitMode)
{
/*
// do RGB
*/
for(k = 0; k < 3; k++)
{
Result[k] >>= 2;
}
Result[3] >>= 1;
}
else
{
/*
// do RGB (A is ok)
*/
for(k = 0; k < 3; k++)
{
Result[k] >>= 1;
}
}
/*
// sanity check
*/
for(k = 0; k < 4; k++)
{
_ASSERT(Result[k] < 256);
}
/*
// Convert from 5554 to 8888
//
// do RGB 5.3 => 8
*/
for(k = 0; k < 3; k++)
{
Result[k] += Result[k] >> 5;
}
Result[3] += Result[3] >> 4;
/*
// 2nd sanity check
*/
for(k = 0; k < 4; k++)
{
_ASSERT(Result[k] < 256);
}
}
/***********************************************************/
/*
// GetModulationValue
//
// Get the modulation value as a numerator of a fraction of 8ths
*/
/***********************************************************/
static void GetModulationValue(int x,
int y,
const int Do2bitMode,
const int ModulationVals[8][16],
const int ModulationModes[8][16],
int *Mod,
int *DoPT)
{
static const int RepVals0[4] = {0, 3, 5, 8};
static const int RepVals1[4] = {0, 4, 4, 8};
int ModVal;
/*
// Map X and Y into the local 2x2 block
*/
y = (y & 0x3) | ((~y & 0x2) << 1);
if(Do2bitMode)
{
x = (x & 0x7) | ((~x & 0x4) << 1);
zzajin- Posts : 81
Join date : 2008-10-14
Re: Compressed Textures from Apple's texturetool
part 2
- Code:
}
else
{
x = (x & 0x3) | ((~x & 0x2) << 1);
}
/*
// assume no PT for now
*/
*DoPT = 0;
/*
// extract the modulation value. If a simple encoding
*/
if(ModulationModes[y][x]==0)
{
ModVal = RepVals0[ModulationVals[y][x]];
}
else if(Do2bitMode)
{
/*
// if this is a stored value
*/
if(((x^y)&1)==0)
{
ModVal = RepVals0[ModulationVals[y][x]];
}
/*
// else average from the neighbours
//
// if H&V interpolation...
*/
else if(ModulationModes[y][x] == 1)
{
ModVal = (RepVals0[ModulationVals[y-1][x]] +
RepVals0[ModulationVals[y+1][x]] +
RepVals0[ModulationVals[y][x-1]] +
RepVals0[ModulationVals[y][x+1]] + 2) / 4;
}
/*
// else if H-Only
*/
else if(ModulationModes[y][x] == 2)
{
ModVal = (RepVals0[ModulationVals[y][x-1]] +
RepVals0[ModulationVals[y][x+1]] + 1) / 2;
}
/*
// else it's V-Only
*/
else
{
ModVal = (RepVals0[ModulationVals[y-1][x]] +
RepVals0[ModulationVals[y+1][x]] + 1) / 2;
}/*end if/then/else*/
}
/*
// else it's 4BPP and PT encoding
*/
else
{
ModVal = RepVals1[ModulationVals[y][x]];
*DoPT = ModulationVals[y][x] == PT_INDEX;
}
*Mod =ModVal;
}
/*****************************************************************************/
/*
// TwiddleUV
//
// Given the Block (or pixel) coordinates and the dimension of the texture
// in blocks (or pixels) this returns the twiddled offset of the block
// (or pixel) from the start of the map.
//
// NOTE the dimensions of the texture must be a power of 2
*/
/*****************************************************************************/
static int DisableTwiddlingRoutine = 0;
static U32 TwiddleUV(U32 YSize, U32 XSize, U32 YPos, U32 XPos)
{
U32 Twiddled;
U32 MinDimension;
U32 MaxValue;
U32 SrcBitPos;
U32 DstBitPos;
int ShiftCount;
_ASSERT(YPos < YSize);
_ASSERT(XPos < XSize);
_ASSERT(POWER_OF_2(YSize));
_ASSERT(POWER_OF_2(XSize));
if(YSize < XSize)
{
MinDimension = YSize;
MaxValue = XPos;
}
else
{
MinDimension = XSize;
MaxValue = YPos;
}
/*
// Nasty hack to disable twiddling
*/
if(DisableTwiddlingRoutine)
{
return (YPos* XSize + XPos);
}
/*
// Step through all the bits in the "minimum" dimension
*/
SrcBitPos = 1;
DstBitPos = 1;
Twiddled = 0;
ShiftCount = 0;
while(SrcBitPos < MinDimension)
{
if(YPos & SrcBitPos)
{
Twiddled |= DstBitPos;
}
if(XPos & SrcBitPos)
{
Twiddled |= (DstBitPos << 1);
}
SrcBitPos <<= 1;
DstBitPos <<= 2;
ShiftCount += 1;
}/*end while*/
/*
// prepend any unused bits
*/
MaxValue >>= ShiftCount;
Twiddled |= (MaxValue << (2*ShiftCount));
return Twiddled;
}
/***********************************************************/
/*
// Decompress
//
// Takes the compressed input data and outputs the equivalent decompressed
// image.
*/
/***********************************************************/
static void Decompress(AMTC_BLOCK_STRUCT *pCompressedData,
const int Do2bitMode,
const int XDim,
const int YDim,
const int AssumeImageTiles,
unsigned char* pResultImage)
{
int x, y;
int i, j;
int BlkX, BlkY;
int BlkXp1, BlkYp1;
int XBlockSize;
int BlkXDim, BlkYDim;
int StartX, StartY;
int ModulationVals[8][16];
int ModulationModes[8][16];
int Mod, DoPT;
unsigned int uPosition;
/*
// local neighbourhood of blocks
*/
AMTC_BLOCK_STRUCT *pBlocks[2][2];
AMTC_BLOCK_STRUCT *pPrevious[2][2] = {{NULL, NULL}, {NULL, NULL}};
/*
// Low precision colours extracted from the blocks
*/
struct
{
int Reps[2][4];
}Colours5554[2][2];
/*
// Interpolated A and B colours for the pixel
*/
int ASig[4], BSig[4];
int Result[4];
if(Do2bitMode)
{
XBlockSize = BLK_X_2BPP;
}
else
{
XBlockSize = BLK_X_4BPP;
}
/*
// For MBX don't allow the sizes to get too small
*/
BlkXDim = _MAX(2, XDim / XBlockSize);
BlkYDim = _MAX(2, YDim / BLK_Y_SIZE);
/*
// Step through the pixels of the image decompressing each one in turn
//
// Note that this is a hideously inefficient way to do this!
*/
for(y = 0; y < YDim; y++)
{
for(x = 0; x < XDim; x++)
{
/*
// map this pixel to the top left neighbourhood of blocks
*/
BlkX = (x - XBlockSize/2);
BlkY = (y - BLK_Y_SIZE/2);
BlkX = LIMIT_COORD(BlkX, XDim, AssumeImageTiles);
BlkY = LIMIT_COORD(BlkY, YDim, AssumeImageTiles);
BlkX /= XBlockSize;
BlkY /= BLK_Y_SIZE;
//BlkX = LIMIT_COORD(BlkX, BlkXDim, AssumeImageTiles);
//BlkY = LIMIT_COORD(BlkY, BlkYDim, AssumeImageTiles);
/*
// compute the positions of the other 3 blocks
*/
BlkXp1 = LIMIT_COORD(BlkX+1, BlkXDim, AssumeImageTiles);
BlkYp1 = LIMIT_COORD(BlkY+1, BlkYDim, AssumeImageTiles);
/*
// Map to block memory locations
*/
pBlocks[0][0] = pCompressedData +TwiddleUV(BlkYDim, BlkXDim, BlkY, BlkX);
pBlocks[0][1] = pCompressedData +TwiddleUV(BlkYDim, BlkXDim, BlkY, BlkXp1);
pBlocks[1][0] = pCompressedData +TwiddleUV(BlkYDim, BlkXDim, BlkYp1, BlkX);
pBlocks[1][1] = pCompressedData +TwiddleUV(BlkYDim, BlkXDim, BlkYp1, BlkXp1);
/*
// extract the colours and the modulation information IF the previous values
// have changed.
*/
if(memcmp(pPrevious, pBlocks, 4*sizeof(void*)) != 0)
{
StartY = 0;
for(i = 0; i < 2; i++)
{
StartX = 0;
for(j = 0; j < 2; j++)
{
Unpack5554Colour(pBlocks[i][j], Colours5554[i][j].Reps);
UnpackModulations(pBlocks[i][j],
Do2bitMode,
ModulationVals,
ModulationModes,
StartX, StartY);
StartX += XBlockSize;
}/*end for j*/
StartY += BLK_Y_SIZE;
}/*end for i*/
/*
// make a copy of the new pointers
*/
memcpy(pPrevious, pBlocks, 4*sizeof(void*));
}/*end if the blocks have changed*/
/*
// decompress the pixel. First compute the interpolated A and B signals
*/
InterpolateColours(Colours5554[0][0].Reps[0],
Colours5554[0][1].Reps[0],
Colours5554[1][0].Reps[0],
Colours5554[1][1].Reps[0],
Do2bitMode, x, y,
ASig);
InterpolateColours(Colours5554[0][0].Reps[1],
Colours5554[0][1].Reps[1],
Colours5554[1][0].Reps[1],
Colours5554[1][1].Reps[1],
Do2bitMode, x, y,
BSig);
GetModulationValue(x,y, Do2bitMode, (const int (*)[16])ModulationVals, (const int (*)[16])ModulationModes,
&Mod, &DoPT);
/*
// compute the modulated colour
*/
for(i = 0; i < 4; i++)
{
Result[i] = ASig[i] * 8 + Mod * (BSig[i] - ASig[i]);
Result[i] >>= 3;
}
if(DoPT)
{
Result[3] = 0;
}
/*
// Store the result in the output image
*/
uPosition = (x+y*XDim)<<2;
pResultImage[uPosition+0] = (U8)Result[0];
pResultImage[uPosition+1] = (U8)Result[1];
pResultImage[uPosition+2] = (U8)Result[2];
pResultImage[uPosition+3] = (U8)Result[3];
}/*end for x*/
}/*end for y*/
}
//--------------------------- ETC Compression ---------------------------------
#define _CLAMP_(X,Xmin,Xmax) ( (X)<(Xmax) ? ( (X)<(Xmin)?(Xmin):(X) ) : (Xmax) )
unsigned int ETC_FLIP = 0x01000000;
unsigned int ETC_DIFF = 0x02000000;
const int mod[8][4]={{2, 8,-2,-8},
{5, 17, -5, -17},
{9, 29, -9, -29},
{13, 42, -13, -42},
{18, 60, -18, -60},
{24, 80, -24, -80},
{33, 106, -33, -106},
{47, 183, -47, -183}};
// lsb: hgfedcba ponmlkji msb: hgfedcba ponmlkji due to endianness
unsigned long modifyPixel(int red, int green, int blue, int x, int y, unsigned long modBlock, int modTable)
{
int index = x*4+y, pixelMod;
unsigned long mostSig = modBlock<<1;
if (index<8) //hgfedcba
pixelMod = mod[modTable][((modBlock>>(index+24))&0x1)+((mostSig>>(index+8))&0x2)];
else // ponmlkj
pixelMod = mod[modTable][((modBlock>>(index+8))&0x1)+((mostSig>>(index-8))&0x2)];
red = _CLAMP_(red+pixelMod,0,255);
green = _CLAMP_(green+pixelMod,0,255);
blue = _CLAMP_(blue+pixelMod,0,255);
return ((red<<16) + (green<<8) + blue)|0xff000000;
}
//
// returns Number of bytes of ETC data decompressed
//
// Description : Convert ETC textures to RGBA8888.
// - pSrcData points to the source DXT data
// - x and y are the dimensions of the source data, in pixels.
// - pDestData is a pointer that will contain the output data.
// - nMode indicates the format of the data (DXT 1 to 5)
//
int ETCTextureDecompress(const void * const pSrcData, const int &x, const int &y, const void *pDestData,const int &nMode)
{
unsigned long blockTop, blockBot, *input = (unsigned long*)pSrcData, *output;
//unsigned long alphaTop, alphaBot, alphaValue[8];
unsigned char red1, green1, blue1, red2, green2, blue2;
bool bFlip, bDiff;
int modtable1,modtable2;
for(int i=0;i<y;i+=4)
{
for(int m=0;m<x;m+=4)
{
blockTop = *(input++);
blockBot = *(input++);
output = (unsigned long*)pDestData + i*x +m;
// check flipbit
bFlip = (blockTop & ETC_FLIP) != 0;
bDiff = (blockTop & ETC_DIFF) != 0;
if(bDiff)
{ // differential mode 5 colour bits + 3 difference bits
// get base colour for subblock 1
blue1 = (unsigned char)((blockTop&0xf80000)>>16);
green1 = (unsigned char)((blockTop&0xf800)>>8);
red1 = (unsigned char)(blockTop&0xf8);
// get differential colour for subblock 2
signed char blues = (signed char)(blue1>>3) + ((signed char) ((blockTop & 0x70000) >> 11)>>5);
signed char greens = (signed char)(green1>>3) + ((signed char)((blockTop & 0x700) >>3)>>5);
signed char reds = (signed char)(red1>>3) + ((signed char)((blockTop & 0x7)<<5)>>5);
blue2 = (unsigned char)blues;
green2 = (unsigned char)greens;
red2 = (unsigned char)reds;
red1 = red1 +(red1>>5); // copy bits to lower sig
green1 = green1 + (green1>>5); // copy bits to lower sig
blue1 = blue1 + (blue1>>5); // copy bits to lower sig
red2 = (red2<<3) +(red2>>2); // copy bits to lower sig
green2 = (green2<<3) + (green2>>2); // copy bits to lower sig
blue2 = (blue2<<3) + (blue2>>2); // copy bits to lower sig
}
else
{ // individual mode 4 + 4 colour bits
// get base colour for subblock 1
blue1 = (unsigned char)((blockTop&0xf00000)>>16);
blue1 = blue1 +(blue1>>4); // copy bits to lower sig
green1 = (unsigned char)((blockTop&0xf000)>>8);
green1 = green1 + (green1>>4); // copy bits to lower sig
red1 = (unsigned char)(blockTop&0xf0);
red1 = red1 + (red1>>4); // copy bits to lower sig
// get base colour for subblock 2
blue2 = (unsigned char)((blockTop&0xf0000)>>12);
blue2 = blue2 +(blue2>>4); // copy bits to lower sig
green2 = (unsigned char)((blockTop&0xf00)>>4);
green2 = green2 + (green2>>4); // copy bits to lower sig
red2 = (unsigned char)((blockTop&0xf)<<4);
red2 = red2 + (red2>>4); // copy bits to lower sig
}
// get the modtables for each subblock
modtable1 = (blockTop>>29)&0x7;
modtable2 = (blockTop>>26)&0x7;
if(!bFlip)
{ // 2 2x4 blocks side by side
for(int j=0;j<4;j++) // vertical
{
for(int k=0;k<2;k++) // horizontal
{
*(output+j*x+k) = modifyPixel(red1,green1,blue1,k,j,blockBot,modtable1);
*(output+j*x+k+2) = modifyPixel(red2,green2,blue2,k+2,j,blockBot,modtable2);
}
}
}
else
{ // 2 4x2 blocks on top of each other
for(int j=0;j<2;j++)
{
for(int k=0;k<4;k++)
{
*(output+j*x+k) = modifyPixel(red1,green1,blue1,k,j,blockBot,modtable1);
*(output+(j+2)*x+k) = modifyPixel(red2,green2,blue2,k,j+2,blockBot,modtable2);
}
}
}
}
}
return x*y/2;
}
int ETCDecompress(const void * const pSrcData,
const unsigned int &x,
const unsigned int &y,
void *pDestData,
const int &nMode)
{
int i32read;
if(x<ETC_MIN_TEXWIDTH || y<ETC_MIN_TEXHEIGHT)
{ // decompress into a buffer big enought to take the minimum size
char* pTempBuffer = (char*)new char[_MAX(x,ETC_MIN_TEXWIDTH)*_MAX(y,ETC_MIN_TEXHEIGHT)*4];
i32read = ETCTextureDecompress(pSrcData,_MAX(x,ETC_MIN_TEXWIDTH),_MAX(y,ETC_MIN_TEXHEIGHT),pTempBuffer,nMode);
for(unsigned int i=0;i<y;i++)
{ // copy from larger temp buffer to output data
memcpy((char*)(pDestData)+i*x*4,pTempBuffer+_MAX(x,ETC_MIN_TEXWIDTH)*4*i,x*4);
}
if(pTempBuffer)
delete(pTempBuffer);
}
else // decompress larger MIP levels straight into the output data
i32read = ETCTextureDecompress(pSrcData,x,y,pDestData,nMode);
// swap r and b channels
unsigned char* pSwap=(unsigned char*)pDestData, swap;
for(unsigned int i=0;i<y;i++)
for(unsigned int j=0;j<x;j++)
{
swap = pSwap[0];
pSwap[0] = pSwap[2];
pSwap[2] = swap;
pSwap+=4;
}
return i32read;
}
#undef PT_INDEX
#undef BLK_Y_SIZE
#undef BLK_X_MAX
#undef BLK_X_2BPP
#undef BLK_X_4BPP
#undef _MIN
#undef _MAX
#undef WRAP_COORD
#undef CLAMP
#undef POWER_OF_2
#undef LIMIT_COORD
//#undef RGBA
#undef _ASSERT
zzajin- Posts : 81
Join date : 2008-10-14
Support for att files
image.h
add to SIO2image struct
add to function def
image.cc
in sio2ImageLoad add
in sio2ImageGenId after
add
add the following function
add to SIO2image struct
- Code:
bool compressed;
bool has_alpha;
add to function def
- Code:
void sio2ImageLoadATT( SIO2image *_SIO2image,
SIO2stream *_SIO2stream);
image.cc
in sio2ImageLoad add
- Code:
//load compressed raw data from Apple's texturetool
else if( strstr( _SIO2stream->fname, ".att" ) )
{
sio2ImageLoadATT( _SIO2image,
_SIO2stream );
}
in sio2ImageGenId after
- Code:
if( sio2->afilter != SIO2_IMAGE_ISOTROPIC )
{
glTexParameterf( GL_TEXTURE_2D,
GL_TEXTURE_MAX_ANISOTROPY_EXT,
sio2->maniso );
}
add
- Code:
if (_SIO2image->compressed)
{
glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_FALSE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
GLenum format;
GLsizei size = _SIO2image->width * _SIO2image->width * _SIO2image->bits / 8;
if(_SIO2image->has_alpha)
{
format = (_SIO2image->bits == 4) ? GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG : GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG;
}
else
{
format = (_SIO2image->bits == 4) ? GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG : GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG;
}
if(size < 32)
{
size = 32;
}
int level = 0;
glCompressedTexImage2D(GL_TEXTURE_2D,
level,
format,
_SIO2image->width,
_SIO2image->width,
0,
size,
&_SIO2image->tex[ 0 ]);
#ifdef SIO2_DEBUG_GL
sio2ErrorGL( __FILE__, __FUNCTION__, __LINE__ );
#endif
return;
}
add the following function
- Code:
void sio2ImageLoadATT( SIO2image *_SIO2image,
SIO2stream *_SIO2stream )
{
unsigned int s = sio2StringLen( _SIO2stream->fname );
int power = atoi(&_SIO2stream->fname[s-5]);
char alpha = _SIO2stream->fname[s-6];
//char level = _SIO2stream->fname[s-7];
//char type = _SIO2stream->fname[s-8];
int bpp = atoi(&_SIO2stream->fname[s-9]);
_SIO2image->width = (int) pow(2,power+2);
_SIO2image->height = (int) pow(2,power+2);
_SIO2image->bits = bpp;
_SIO2image->compressed = 1;
_SIO2image->has_alpha = atoi(&alpha);
sio2DisableState( &_SIO2image->flags, SIO2_IMAGE_MIPMAP );
_SIO2image->tex = ( unsigned char * ) malloc( _SIO2stream->size );
sio2StreamRead( _SIO2stream, &_SIO2image->tex[ 0 ], _SIO2stream->size );
#if (TARGET_IPHONE_SIMULATOR)
// Convert PVRTC to 32-bit
unsigned char *u8TempTexture = (unsigned char*) new unsigned char[_SIO2image->width*_SIO2image->width*4];
unsigned int is2bit = (_SIO2image->bits == 2) ? 1 : 0;
PVRTCDecompress((void*) &_SIO2image->tex[ 0 ], is2bit, _SIO2image->width, _SIO2image->width, u8TempTexture);
free( _SIO2image->tex );
_SIO2image->tex = ( unsigned char * ) malloc( _SIO2image->width*_SIO2image->width*4 );
memcpy((void*) &_SIO2image->tex[ 0 ], u8TempTexture, _SIO2image->width*_SIO2image->width*4);
delete(u8TempTexture);
_SIO2image->bits = 4;
_SIO2image->compressed = 0;
sio2ImageRGBAtoBGRA( _SIO2image );
#endif
}
zzajin- Posts : 81
Join date : 2008-10-14
texturetool for blender
Here is the script for blender to create compressed textures.
- Code:
#!BPY
# Copyright (C) 2008: ZzaJin - zzajin@gmail.com
"""
Name: 'PVRTC TextureTool'
Blender: 248
Group: 'Image'
Tooltip: 'PVRTC TextureTool'
"""
import os
import sys
import struct
import math
import Blender
import platform
import shutil
import bpy
from Blender import Texture
from Blender import Window
from Blender import Object
from Blender import Mesh
from Blender import Mathutils
from Blender.Draw import *
from Blender.BGL import *
from Blender.Window import *
from Blender.Mathutils import *
#Location of the texture tool provided by Apple
TOOL = "/Developer/Platforms/iPhoneOS.platform/usr/bin/texturetool "
INTERNAL_NAME = "PVRTC TextureTool"
VERSION_MAJOR = 1
VERSION_MINOR = 0
VERSION_PATCH = 0
MAX_CHAR = 32
EVENT_EXIT = 0
EVENT_PATH_CHANGE = 1
EVENT_RUN = 2
EVENT_DIR_SELECT = 3
EVENT_LEVEL = 4
EVENT_DONE = 5
EVENT_TOGGLE_2BPP_LINEAR = 12
EVENT_TOGGLE_4BPP_LINEAR = 13
EVENT_TOGGLE_2BPP_PERCEPTION = 14
EVENT_TOGGLE_4BPP_PERCEPTION = 15
EVENT_TOGGLE_LOAD_PNG = 16
EVENT_TOGGLE_LOAD_ATT = 17
#conversion toggles
LIN_2BPP = 1 #Linear channel weighting with 2 bits per pixel
LIN_4BPP = 1 #Linear channel weighting with 4 bits per pixel
PER_2BPP = 1 #Perception channel weighting with 2 bits per pixel
PER_4BPP = 1 #Perception channel weighting with 4 bits per pixel
LEVEL = 0 #MipMap level
LOAD_PNG = 1
LOAD_ATT = 0
lin_2bpp_text = ""
lin_4bpp_text = ""
per_2bpp_text = ""
per_4bpp_text = ""
IDLE = 0
RUNNING = 1
DONE = 3
SCRIPT_RUN_STATE = IDLE
LOG = ""
error_str = ""
#=========================================
# DEFAULT OUTPUT PATH::REMOVE WHEN RELEASE
#=========================================
directory = Create("/Users/zzajin/Desktop/")
#========================================
# REGISTER GUI
#========================================
def register_gui():
#========================================
Register( draw_gui, None, event_gui )
#========================================
# DRAW RECTANGLE
#========================================
def draw_rectangle( x, y, w, h, r, g, b, a ):
#========================================
glEnable( GL_BLEND )
glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA )
glColor4f( r,g,b,a )
glBegin( GL_POLYGON )
glVertex2i( x , y )
glVertex2i( x , y + h )
glVertex2i( x + w, y + h )
glVertex2i( x + w, y )
glEnd()
glDisable( GL_BLEND )
#=========================================
# RENDER OUTLINE OF GUI
#=========================================
def draw_outline():
glClearColor(0.74, 0.74, 0.74, 0.0)
glClear(GL_COLOR_BUFFER_BIT)
draw_rectangle( 5, 213, 400, 30, 0.6, 0.6, 0.6, 1.0 )
draw_rectangle( 5, 6, 400, 207, 0.8, 0.8, 0.8, 1.0 )
#draw version
glColor3f( 1.0, 1.0, 1.0 )
glRasterPos2i( 250, 225 )
tmp = "%s v%d.%d.%d" % ( INTERNAL_NAME,
VERSION_MAJOR,
VERSION_MINOR,
VERSION_PATCH )
Text( tmp )
glPolygonMode( GL_FRONT_AND_BACK, GL_LINE )
draw_rectangle( 5, 7, 399, 236, 0.5, 0.5, 0.5, 1.0 )
draw_rectangle( 5, 7, 399, 207, 0.5, 0.5, 0.5, 1.0 )
glPolygonMode( GL_FRONT_AND_BACK, GL_FILL )
#=========================================
# RENDER THE GUI
#=========================================
def draw_gui():
#=========================================
global directory
global current_image
global status
draw_outline()
#widgets
glColor3f(0.0, 0.0, 0.0)
glRasterPos2i(15, 192)
Text("Output Directory:")
directory = String( "",
EVENT_PATH_CHANGE,
15, 165, 360, 20,
directory.val, 255,
"")
Button( "..." , EVENT_DIR_SELECT , 375, 165, 20, 20 )
EVENT_TOGGLE_2BPP_LINEAR = 12
EVENT_TOGGLE_4BPP_LINEAR = 13
EVENT_TOGGLE_2BPP_PERCEPTION = 14
EVENT_TOGGLE_4BPP_PERCEPTION = 15
#Defaults
BPP2_LINEAR = 1
BPP4_LINEAR = 1
BPP2_PERCEPTION = 1
BPP4_PERCEPTION = 1
Toggle( "Linear 2 bpp", EVENT_TOGGLE_2BPP_LINEAR, 15, 135, 180, 20, LIN_2BPP, "Linear channel weighting with 2 bits per pixel." )
Toggle( "Linear 4 bpp", EVENT_TOGGLE_4BPP_LINEAR, 200, 135, 180, 20, LIN_4BPP, "Linear channel weighting with 4 bits per pixel." )
Toggle( "Perception 2 bpp" , EVENT_TOGGLE_2BPP_PERCEPTION, 15, 90, 180, 20, PER_2BPP, "Perception channel weighting with 2 bits per pixel." )
Toggle( "Perception 4 bpp" , EVENT_TOGGLE_4BPP_PERCEPTION, 200, 90, 180, 20, PER_4BPP, "Perception channel weighting with 4 bits per pixel." )
glRasterPos2i(15, 52)
Text("Post Load:")
Toggle( "PNG", EVENT_TOGGLE_LOAD_PNG, 80 ,45, 30, 20, LOAD_PNG, "Load .pvr.png image after conversion." )
Toggle( "ATT", EVENT_TOGGLE_LOAD_ATT, 110 ,45, 30, 20, LOAD_ATT, "Load .att image after conversion." )
Number("Level",EVENT_LEVEL, 170, 45, 80, 20, LEVEL, 0, 9, "MipMap Level", level_changed)
glColor3f(0.0, 0.0, 0.8)
#status
if (SCRIPT_RUN_STATE == IDLE):
set_current_image()
Button( "Run" , EVENT_RUN , 275, 45, 50, 20 )
Button( "Exit" , EVENT_EXIT , 345, 45, 50, 20 )
glRasterPos2i(15, 20)
Text(status)
#per button status
glRasterPos2i(20, 120)
Text(lin_2bpp_text)
glRasterPos2i(205, 120)
Text(lin_4bpp_text)
glRasterPos2i(20, 75)
Text(per_2bpp_text)
glRasterPos2i(205, 75)
Text(per_4bpp_text)
#====================================
# GUI EVENTS CALLBACKS
#====================================
def event_gui( event ):
#====================================
global UPDATE
global LIN_2BPP
global LIN_4BPP
global PER_2BPP
global PER_4BPP
global SCRIPT_RUN_STATE
global lin_2bpp_text
global lin_4bpp_text
global per_2bpp_text
global per_4bpp_text
if( event == EVENT_EXIT ): Exit()
if( event == EVENT_DIR_SELECT ): Blender.Window.FileSelector( dir_selected, "Select", "" )
if( event == EVENT_RUN ): run()
if( event == EVENT_TOGGLE_2BPP_LINEAR ): LIN_2BPP = not LIN_2BPP
if( event == EVENT_TOGGLE_4BPP_LINEAR ): LIN_4BPP = not LIN_4BPP
if( event == EVENT_TOGGLE_2BPP_PERCEPTION ): PER_2BPP = not PER_2BPP
if( event == EVENT_TOGGLE_4BPP_PERCEPTION ): PER_4BPP = not PER_4BPP
if( event == EVENT_DONE ):
SCRIPT_RUN_STATE = IDLE
lin_2bpp_text = ""
lin_4bpp_text = ""
per_2bpp_text = ""
per_4bpp_text = ""
Draw();
#====================================
# DIRECTORY HAVE CHANGED
#====================================
def dir_selected( _dir ):
#====================================
global directory
directory.val = _dir
#====================================
# LEVEL CHANGED
#====================================
def level_changed( _evt, _val ):
#====================================
global LEVEL
LEVEL = _val
#====================================
# CREATE ARCHITECTURE DIRECTORY
#====================================
def create_directory( d, f = 0 ):
global error_str
global UPDATE
try:
os.mkdir( d )
except:
dummy = 0
#====================================
# BASIC STATUS MESSAGE
#====================================
def set_current_image():
global current_image
global status
current_image = Blender.Image.GetCurrent()
if(not current_image or current_image.source != Blender.Image.Sources["STILL"] ):
status = "No image selected"
else:
(width, height) = current_image.getSize()
status = "Selected image: %s ( %d x %d )" % (os.path.basename( current_image.filename ), width, height)
#====================================
# CHECK IMAGE
#====================================
def check_image( img ):
global error_str
#check exsitence
if(not img or img.source != Blender.Image.Sources["STILL"] ):
error_str = "ERROR: No image selected"
return 0
# Check if the file exists
if( not os.path.exists( img.filename ) ):
error_str = "ERROR: Unable to find file: %s. |" % img.filename
return 0
#image must be square and power of 2 between 1024x1024 and 4x4
(width, height) = img.getSize()
if( width != height ):
error_str = "ERROR: Image must be square"
return 0
if( int(math.log(width,2)) != math.log(width,2)):
error_str = "ERROR: Image must be power of 2"
return 0
if( width > 1024 ):
error_str = "ERROR: Image must be 1024x1024 or smaller"
return 0
if( width < 4 ):
error_str = "ERROR: Image must be 4x4 or larger"
return 0
return 1
#====================================
# convert image to PVRTC
#====================================
def run():
global error_str
global TOOL
global LIN_2BPP
global LIN_4BPP
global PER_2BPP
global PER_4BPP
global LEVEL
global lin_2bpp_text
global lin_4bpp_text
global per_2bpp_text
global per_4bpp_text
global status
global directory
global SCRIPT_RUN_STATE
error_str = ""
current_image = Blender.Image.GetCurrent()
if ( check_image(current_image) ):
#====================================
# Start the process.
#====================================
SCRIPT_RUN_STATE = RUNNING
start_time = Blender.sys.time()
print
print
print "%s v%d.%d.%d\n%s\n" % ( INTERNAL_NAME,VERSION_MAJOR,VERSION_MINOR,VERSION_PATCH,"Copyright (C) 2008" )
print
#====================================
# VALIDATE AND CREATE SCENE DIRECTORY
#====================================
if( directory.val[ len(directory.val) - 1 ] != "/" ):
directory.val = directory.val + "/"
directory.val.replace('\\','/')
source = current_image.filename
name = directory.val + os.path.basename( source.split(".")[0] )
alpha = 0
(width, height) = current_image.getSize()
size = math.log(width,2) - 2
png = ""
pvr = ""
if (LIN_2BPP):
png = "%s_2l%d%d%d.pvr.png" % (name, LEVEL, alpha, size)
pvr = "%s_2l%d%d%d.att" % (name, LEVEL, alpha, size)
status = "%s & %s" % (os.path.basename(pvr), os.path.basename(png))
lin_2bpp_text = "Creating..."
Draw()
command = "%s -e PVRTC --channel-weighting-linear --bits-per-pixel-2 -o %s -p %s %s" % (TOOL, pvr, png, source)
os.system( command )
if( LOAD_ATT ):
Blender.Image.Load(pvr)
if( LOAD_PNG ):
Blender.Image.Load(png)
lin_2bpp_text = "Done"
if (LIN_4BPP):
png = "%s_4l%d%d%d.pvr.png" % (name, LEVEL, alpha, size)
pvr = "%s_4l%d%d%d.att" % (name, LEVEL, alpha, size)
status = "%s & %s" % (os.path.basename(pvr), os.path.basename(png))
lin_4bpp_text = "Creating..."
Draw()
command = "%s -e PVRTC --channel-weighting-linear --bits-per-pixel-4 -o %s -p %s %s" % (TOOL, pvr, png, source)
os.system( command )
if( LOAD_ATT ):
Blender.Image.Load(pvr)
if( LOAD_PNG ):
Blender.Image.Load(png)
lin_4bpp_text = "Done"
if (PER_2BPP):
png = "%s_2p%d%d%d.pvr.png" % (name, LEVEL, alpha, size)
pvr = "%s_2p%d%d%d.att" % (name, LEVEL, alpha, size)
status = "%s & %s" % (os.path.basename(pvr), os.path.basename(png))
per_2bpp_text = "Creating..."
Draw()
command = "%s -e PVRTC --channel-weighting-perceptual --bits-per-pixel-2 -o %s -p %s %s" % (TOOL, pvr, png, source)
os.system( command )
if( LOAD_ATT ):
Blender.Image.Load(pvr)
if( LOAD_PNG ):
Blender.Image.Load(png)
per_2bpp_text = "Done"
if (PER_4BPP):
png = "%s_4p%d%d%d.pvr.png" % (name, LEVEL, alpha, size)
pvr = "%s_4p%d%d%d.att" % (name, LEVEL, alpha, size)
status = "%s & %s" % (os.path.basename(pvr), os.path.basename(png))
per_4bpp_text = "Creating..."
Draw()
command = "%s -e PVRTC --channel-weighting-perceptual --bits-per-pixel-4 -o %s -p %s %s" % (TOOL, pvr, png, source)
os.system( command )
if( LOAD_ATT ):
Blender.Image.Load(pvr)
if( LOAD_PNG ):
Blender.Image.Load(png)
per_4bpp_text = "Done"
set_current_image()
SCRIPT_RUN_STATE = IDLE
lin_2bpp_text = ""
lin_4bpp_text = ""
per_2bpp_text = ""
per_4bpp_text = ""
Draw();
error_str += "Converting Successful: %.3f sec." % ( Blender.sys.time() - start_time )
PupMenu( error_str )
#error
else:
PupMenu( error_str)
#====================================
# MAIN
#====================================
register_gui()
zzajin- Posts : 81
Join date : 2008-10-14
changes to the sio2 exporter
EDITTED: added missing buffer writes
Change the following in sio_exporter_v1.2.1.py
replace line 651
replace line 629
replace lines 257-260
Change the following in sio_exporter_v1.2.1.py
replace line 651
- Code:
t_name = tex_channel[1].tex.image.filename
if( t_name.endswith('.pvr.png') > 0):
t_name = t_name.replace('.pvr.png', '.att')
buffer = "\ttex1( \"%s\" )\n" % ( "image/" + os.path.basename( t_name ) )
f.write( buffer )
replace line 629
- Code:
t_name = tex_channel[0].tex.image.filename
if( t_name.endswith('.pvr.png') > 0):
t_name = t_name.replace('.pvr.png', '.att')
buffer = "\ttex0( \"%s\" )\n" % ( "image/" + os.path.basename( t_name ) )
f.write( buffer )
replace lines 257-260
- Code:
if( UPDATE or not os.path.exists( tmp_name ) ):
#copy the .att image when a placeholder (.pre) image is used
if( t_name.endswith('.pvr.png') > 0):
t_name = Blender.sys.expandpath( t.tex.image.filename )
t_name = t_name.replace('.pvr.png', '.att')
if( os.path.exists( t_name ) ):
pack_name = t_name.split('\\')[-1].split('/')[-1]
pack_name = output + "image/" + pack_name
shutil.copyfile( t_name, pack_name )
else:
error_str = "ERROR: Unable to find compressed file: %s. |" % t_name
return -1
else:
shutil.copyfile( Blender.sys.expandpath( t.tex.image.filename ), tmp_name )
return 1
Last edited by zzajin on Thu Nov 13, 2008 12:10 pm; edited 1 time in total
zzajin- Posts : 81
Join date : 2008-10-14
Re: Compressed Textures from Apple's texturetool
this looks great. thanks for sharing. I will give the workflow a try today and report back.
uprise78- Posts : 228
Join date : 2008-10-31
Re: Compressed Textures from Apple's texturetool
zzajin,
Thanks again for this contribution. I got the siodecompresspvr.h/.cc files loaded and compiling properly and the image exporter works like a charm. I modified the exporter but I think I may be unclear as to what is supposed to be modified. When I run the exporter, I get the .att texture in the images folder as expected, but the material is coming out as an empty file which I believe is the issue. When I render, the material is not getting applied though stepping through the debugger sio2ImageLoadATT() is getting called without error. I am thinking the exporter is the problem with it providing an empty material file. The following is the relevant section of the exporter. Can you take a look at it and let me know if it is correct? Should the buffer that is being created be written at some point?
Thanks again for this contribution. I got the siodecompresspvr.h/.cc files loaded and compiling properly and the image exporter works like a charm. I modified the exporter but I think I may be unclear as to what is supposed to be modified. When I run the exporter, I get the .att texture in the images folder as expected, but the material is coming out as an empty file which I believe is the issue. When I render, the material is not getting applied though stepping through the debugger sio2ImageLoadATT() is getting called without error. I am thinking the exporter is the problem with it providing an empty material file. The following is the relevant section of the exporter. Can you take a look at it and let me know if it is correct? Should the buffer that is being created be written at some point?
- Code:
#================================
# TEX0
#================================
flags = 0
if( st0 == 1 ):
# Use Mipmap
if( tex_channel[0].tex.imageFlags & Texture.ImageFlags["MIPMAP"] ):
flags = flags | 1
# Clamp to Edge
if( tex_channel[0].tex.getExtend() == "Clip" ):
flags = flags | 2
buffer = "\ttflags0( %s )\n" % flags
f.write( buffer )
buffer = "\ttex0( \"%s\" )\n" % ( "image/" + os.path.basename( tex_channel[0].tex.image.filename ) )
f.write( buffer )
t_name = tex_channel[0].tex.image.filename
if( t_name.endswith('.pvr.png') > 0):
t_name = t_name.replace('.pvr.png', '.att')
buffer = "\ttex0( \"%s\" )\n" % ( "image/" + os.path.basename( t_name ) )
#================================
# TEX1
#================================
flags = 0
if( st1 == 1 ):
# Use mipmap
if( tex_channel[1].tex.imageFlags & Texture.ImageFlags["MIPMAP"] ):
flags = flags | 1
# Clamp to edge
if( tex_channel[1].tex.getExtend() == "Clip" ):
flags = flags | 2
buffer = "\ttflags1( %s )\n" % flags
f.write( buffer )
buffer = "\ttex1( \"%s\" )\n" % ( "image/" + os.path.basename( tex_channel[1].tex.image.filename ) )
f.write( buffer )
t_name = tex_channel[1].tex.image.filename
if( t_name.endswith('.pvr.png') > 0):
t_name = t_name.replace('.pvr.png', '.att')
buffer = "\ttex1( \"%s\" )\n" % ( "image/" + os.path.basename( t_name ) )
uprise78- Posts : 228
Join date : 2008-10-31
Re: Compressed Textures from Apple's texturetool
uprise78 wrote:zzajin,
Should the buffer that is being created be written at some point?
Yes you are right I missed the lines after the setting the buffer. Thanks for finding this.
- Code:
f.write( buffer )
I'll fix the above post.
zzajin- Posts : 81
Join date : 2008-10-14
Re: Compressed Textures from Apple's texturetool
zzajin,
I added the f.write( buffer ) lines after each of the 2 texture blocks and when running the exporter it seems to hang when trying to write the Material files. Any ideas? It seems that the .att textures are copied to the images directory as expected.
Would it be possible for you to email or post the updated python exporter script? I can host it if needed so that others can have access to it.
I added the f.write( buffer ) lines after each of the 2 texture blocks and when running the exporter it seems to hang when trying to write the Material files. Any ideas? It seems that the .att textures are copied to the images directory as expected.
Would it be possible for you to email or post the updated python exporter script? I can host it if needed so that others can have access to it.
Last edited by uprise78 on Thu Nov 13, 2008 1:04 pm; edited 1 time in total
uprise78- Posts : 228
Join date : 2008-10-31
Re: Compressed Textures from Apple's texturetool
Here is my exporter:
part 1
part 1
- Code:
#!BPY
"""
Name: 'SIO2 Exporter v1.2.1 (.sio2)...'
Blender: 248
Group: 'Export'
Tooltip: 'Export the current scene to SIO2 (.sio2)'
"""
import os
import sys
import struct
import math
import Blender
import platform
import shutil
import bpy
from Blender import Texture
from Blender import Window
from Blender import Object
from Blender import Mesh
from Blender import Mathutils
from Blender import NMesh
from Blender.Draw import *
from Blender.BGL import *
from Blender.Window import *
from Blender.Mathutils import *
SIO2_INTERNAL_NAME = "SIO2::Exporter"
SIO2_VERSION_MAJOR = 1
SIO2_VERSION_MINOR = 2
SIO2_VERSION_PATCH = 1
EVENT_EXIT = 0
EVENT_PATH_CHANGE = 1
EVENT_EXPORT = 2
EVENT_DIR_SELECT = 3
EVENT_TOGGLE_UPDATE = 4
EVENT_TOGGLE_NORMALS = 5
RAD_TO_DEG = 57.295779
UPDATE = 0
NORMALS = 0
PRECISION = 3
#=========================================
# GLOBAL VARIABLES
#=========================================
error_str = ""
output = ""
#=========================================
# DEFAULT OUTPUT PATH::REMOVE WHEN RELEASE
#=========================================
directory = Create("")
#========================================
# REGISTER GUI
#========================================
def register_gui():
#========================================
Register( draw_gui, None, event_gui )
#========================================
# DRAW RECTANGLE
#========================================
def draw_rectangle( x, y, w, h, r, g, b, a ):
#========================================
glEnable( GL_BLEND )
glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA )
glColor4f( r,g,b,a )
glBegin( GL_POLYGON )
glVertex2i( x , y )
glVertex2i( x , y + h )
glVertex2i( x + w, y + h )
glVertex2i( x + w, y )
glEnd()
glDisable( GL_BLEND )
#=========================================
# RENDER THE EXPORTER GUI
#=========================================
def draw_gui():
#=========================================
global directory
glClearColor(0.74, 0.74, 0.74, 0.0)
glClear(GL_COLOR_BUFFER_BIT)
draw_rectangle( 5, 92, 400, 20, 0.6, 0.6, 0.6, 1.0 )
draw_rectangle( 5, 6, 400, 86, 0.8, 0.8, 0.8, 1.0 )
glColor3f( 1.0, 1.0, 1.0 )
glRasterPos2i( 280, 98 )
tmp = "%s v%d.%d.%d" % ( SIO2_INTERNAL_NAME,
SIO2_VERSION_MAJOR,
SIO2_VERSION_MINOR,
SIO2_VERSION_PATCH )
Text( tmp )
glPolygonMode( GL_FRONT_AND_BACK, GL_LINE )
draw_rectangle( 5, 7, 399, 106, 0.5, 0.5, 0.5, 1.0 )
draw_rectangle( 5, 7, 399, 86, 0.5, 0.5, 0.5, 1.0 )
glPolygonMode( GL_FRONT_AND_BACK, GL_FILL )
glColor3f(0.0, 0.0, 0.0)
glRasterPos2i(15, 72)
Text("Output Directory:")
directory = String( "",
EVENT_PATH_CHANGE,
15, 45, 360, 20,
directory.val, 255,
"")
Button( "..." , EVENT_DIR_SELECT , 375, 45, 20, 20 )
Button( "Export", EVENT_EXPORT , 185, 16, 100, 20 )
Button( "Exit" , EVENT_EXIT , 295, 16, 100, 20 )
Toggle( "UP" , EVENT_TOGGLE_UPDATE , 15, 16, 20, 20, UPDATE , "Update the current SIO2 archive." )
Toggle( "NO" , EVENT_TOGGLE_NORMALS, 40, 16, 20, 20, NORMALS, "Export smooth or solid normals." )
#====================================
# GUI EVENTS CALLBACKS
#====================================
def event_gui( event ):
#====================================
global UPDATE
global NORMALS
if( event == EVENT_EXIT ): Exit()
if( event == EVENT_DIR_SELECT ): Blender.Window.FileSelector( dir_selected, "Select", "" )
if( event == EVENT_EXPORT ): export()
if( event == EVENT_TOGGLE_UPDATE ): UPDATE = not UPDATE
if( event == EVENT_TOGGLE_NORMALS ): NORMALS = not NORMALS
#====================================
# DIRECTORY HAVE CHANGED
#====================================
def dir_selected( _dir ):
#====================================
global directory
directory.val = _dir
#====================================
# OPTIMIZE FLOAT FOR STRING
#====================================
def optimize_float( f ):
#====================================
i = int( f )
r = round( f, PRECISION )
if i == r:
return i
else:
return r
#====================================
# CREATE ARCHITECTURE DIRECTORY
#====================================
def create_directory( d ):
#====================================
try:
os.mkdir( d )
except:
dummy = 0
#====================================
# DISTANCE BETWEEN 2 VERTICES
#====================================
def distance( v1, v2 ):
#====================================
v = []
v.append( v1[0] - v2[0] )
v.append( v1[1] - v2[1] )
v.append( v1[2] - v2[2] )
return math.sqrt( ( v[0] * v[0] ) +
( v[1] * v[1] ) +
( v[2] * v[2] ) )
#====================================
# CHECK TEXTURE
#====================================
def check_texture( t ):
#====================================
global error_str
global UPDATE
if( t != None ):
if( t.tex.type == Texture.Types.IMAGE ):
if( t.tex.image != None ):
#====================================
# Check if the file exists
#====================================
if( not os.path.exists( Blender.sys.expandpath( t.tex.image.filename ) ) ):
error_str = "ERROR: Unable to find file: %s. |" % t.tex.image.filename
return -1
#====================================
# Check if the file is a valid file.
#====================================
t_name = t.tex.image.filename.split('\\')[-1].split('/')[-1]
ogg_uc_pos = t_name.upper().rfind('.OGG' )
if( ogg_uc_pos > -1 ):
#====================================
# Copy the sound file.
#====================================
tmp_name = output + "sound/" + t_name
if( UPDATE or not os.path.exists( tmp_name ) ):
shutil.copyfile( Blender.sys.expandpath( t.tex.image.filename ), tmp_name )
return 1
#====================================
# Copy the texture file.
#====================================
tmp_name = output + "image/" + t_name
if( UPDATE or not os.path.exists( tmp_name ) ):
#copy the .att image when a placeholder (.pre) image is used
if( t_name.endswith('.pvr.png') > 0):
t_name = Blender.sys.expandpath( t.tex.image.filename )
t_name = t_name.replace('.pvr.png', '.att')
if( os.path.exists( t_name ) ):
pack_name = t_name.split('\\')[-1].split('/')[-1]
pack_name = output + "image/" + pack_name
shutil.copyfile( t_name, pack_name )
else:
error_str = "ERROR: Unable to find compressed file: %s. |" % t_name
return -1
else:
shutil.copyfile( Blender.sys.expandpath( t.tex.image.filename ), tmp_name )
return 1
return 0
zzajin- Posts : 81
Join date : 2008-10-14
Re: Compressed Textures from Apple's texturetool
part 2
- Code:
#====================================
# Function that return a vector in
# object space.
#====================================
def get_loc_obj( v, obj_mat ):
#====================================
x = v[0]
y = v[1]
z = v[2]
tx = 0.0
ty = 0.0
tz = 0.0
tx = ( x * obj_mat[0][0] ) + ( y * obj_mat[1][0] ) + ( z * obj_mat[2][0] )
ty = ( x * obj_mat[0][1] ) + ( y * obj_mat[1][1] ) + ( z * obj_mat[2][1] )
tz = ( x * obj_mat[0][2] ) + ( y * obj_mat[1][2] ) + ( z * obj_mat[2][2] )
return tx, ty, tz
#====================================
# Get the DimXYZ of the object
# based on its bounding box
#====================================
def get_dim( obj, mesh ):
#====================================
dim = [ 0.0, 0.0, 0.0 ]
ubound = [-9999.0, -9999.0, -9999.0]
lbound = [ 9999.0, 9999.0, 9999.0]
tmp_mat = obj.getMatrix().copy()
obj_mat = tmp_mat.identity()
for v in mesh.verts:
tmp = [ v.co[0], v.co[1], v.co[2] ]
vloc = get_loc_obj( tmp , obj_mat )
# min
if vloc[0] < lbound[0]:
lbound[0] = vloc[0]
if vloc[1] < lbound[1]:
lbound[1] = vloc[1]
if vloc[2] < lbound[2]:
lbound[2] = vloc[2]
# max
if vloc[0] > ubound[0]:
ubound[0] = vloc[0]
if vloc[1] > ubound[1]:
ubound[1] = vloc[1]
if vloc[2] > ubound[2]:
ubound[2] = vloc[2]
# DimX
a = [ ubound[0], ubound[1], ubound[2] ]
b = [ lbound[0], ubound[1], ubound[2] ]
dim[0] = distance( a, b ) / 2
# DimY
a = [ ubound[0], ubound[1], ubound[2] ]
b = [ ubound[0], lbound[1], ubound[2] ]
dim[1] = distance( a, b ) / 2
# DimZ
a = [ ubound[0], ubound[1], ubound[2] ]
b = [ ubound[0], ubound[1], lbound[2] ]
dim[2] = distance( a, b ) / 2
return dim
#====================================
# Function that return a if an object
# instance have been found.
#====================================
def get_instance( s ):
#====================================
obj = 0
us_pos = s.rfind('.')
if( us_pos == -1 ):
return obj
else:
try:
obj = Blender.Object.Get( s[:us_pos] )
except:
obj = 0
return obj
#====================================
# VERTEX CLASS
#====================================
class vertex( object ):
pass
#====================================
# GET INDEX IN A SPECIFIED VBO ARRAY
#====================================
def get_index( lst, v ):
for i in range( len( lst ) ):
if( lst[ i ].ver == v.ver and
lst[ i ].vco == v.vco and
lst[ i ].vno == v.vno and
lst[ i ].uv0 == v.uv0 and
lst[ i ].uv1 == v.uv1 ):
return i
return -1
#====================================
# EXPORT
#====================================
def export():
#====================================
global output
global directory
global error_str
error_str = ""
#====================================
# Start the export process.
#====================================
start_time = Blender.sys.time()
print
print
print "%s v%d.%d.%d\n%s\n" % ( SIO2_INTERNAL_NAME,
SIO2_VERSION_MAJOR,
SIO2_VERSION_MINOR,
SIO2_VERSION_PATCH,
"Copyright (C) 2008 SIO2 Interactive" )
print
#====================================
# Get the current scene and all the
# objects related to it.
#====================================
scene = Blender.Scene.GetCurrent()
layer = scene.getLayers()
objects = scene.objects
#====================================
# VALIDATE AND CREATE SCENE DIRECTORY
#====================================
if( not len( directory.val ) ):
error_str = "Invalid output directory. |"
PupMenu( error_str )
return
if( directory.val[ len(directory.val) - 1 ] != "/" ):
directory.val = directory.val + "/"
directory.val.replace('\\','/')
#====================================
# Check if the path is suspicious cuz
# instead I can wipe out the whole
# drive...
#====================================
output = directory.val + scene.getName() + "/"
create_directory( output )
create_directory( output + "material/" )
create_directory( output + "image/" )
create_directory( output + "object/" )
create_directory( output + "camera/" )
create_directory( output + "sound/" )
create_directory( output + "lamp/" )
create_directory( output + "script/" )
#====================================
# Make sure that we are not in edit
# mode...
#====================================
Window.EditMode( 0 )
#================================
# CAMERA
#================================
for obj in objects:
if( obj.getType() == "Camera" and obj.layers[0] == layer[0] and obj.isSelected() ):
#================================
# CREATING CAMERA FILE
#================================
if( UPDATE or not os.path.exists( output + "camera/" + obj.getName() ) ):
f = open( output + "camera/" + obj.getName(), "w")
#====================================
# Get access to the camera structure.
#====================================
cam = obj.getData( False, True )
#================================
# NAME
#================================
buffer = "camera( \"%s\")\n{\n" % ( "camera/" + obj.getName() )
f.write( buffer )
#================================
# POSITION
#================================
buffer = "\tpos( %s %s %s )\n" % ( optimize_float( obj.LocX ),
optimize_float( obj.LocY ),
optimize_float( obj.LocZ ) )
f.write( buffer )
#================================
# TARGET POSITION
#================================
tar = ( Blender.Mathutils.Vector( 0.0, 0.0, -1.0 ) * obj.matrixWorld )
buffer = "\ttar( %s %s %s )\n" % ( optimize_float( tar[0] ),
optimize_float( tar[1] ),
optimize_float( tar[2] ) )
f.write( buffer )
#================================
# FOV
#================================
buffer = "\tfov( %s )\n" % optimize_float( cam.getLens() )
f.write( buffer )
#================================
# ZNEAR
#================================
buffer = "\tcstart( %s )\n" % optimize_float( cam.getClipStart() )
f.write( buffer )
#================================
# ZFAR
#================================
buffer = "\tcend( %s )\n" % optimize_float( cam.getClipEnd() )
f.write( buffer )
#===============================
# CLOSE CAMERA
#===============================
f.write( "}\n" )
obj.select(0)
f.close()
#====================================
# MATERIALS
#====================================
for obj in objects:
if( obj.getType() == "Mesh" and obj.layers[0] == layer[0] and obj.isSelected() ):
#====================================
# Get access to the mesh of this
# object...
#====================================
mesh = obj.getData( False, True )
if( len( mesh.materials ) ):
for mat in mesh.materials:
#================================
# CREATING MATERIAL FILES
#================================
if( UPDATE or not os.path.exists( output + "material/" + mat.getName() ) ):
f = open( output + "material/" + mat.getName(), "w")
tex_channel = mat.getTextures()
st0 = check_texture( tex_channel[0] ) # Diffuse
st1 = check_texture( tex_channel[1] ) # Shadowmap
st2 = check_texture( tex_channel[2] ) # Sound (OGG)
if( st0 < 0 or st1 < 0 or st2 < 0 ):
PupMenu( error_str )
return
#================================
# NAME
#================================
buffer = "material( \"%s\" )\n{\n" % ( "material/" + mat.getName() )
f.write( buffer )
#================================
# TEX0
#================================
flags = 0
if( st0 == 1 ):
# Use Mipmap
if( tex_channel[0].tex.imageFlags & Texture.ImageFlags["MIPMAP"] ):
flags = flags | 1
# Clamp to Edge
if( tex_channel[0].tex.getExtend() == "Clip" ):
flags = flags | 2
buffer = "\ttflags0( %s )\n" % flags
f.write( buffer )
t_name = tex_channel[0].tex.image.filename
if( t_name.endswith('.pvr.png') > 0):
t_name = t_name.replace('.pvr.png', '.att')
buffer = "\ttex0( \"%s\" )\n" % ( "image/" + os.path.basename( t_name ) )
f.write( buffer )
#================================
# TEX1
#================================
flags = 0
if( st1 == 1 ):
# Use mipmap
if( tex_channel[1].tex.imageFlags & Texture.ImageFlags["MIPMAP"] ):
flags = flags | 1
# Clamp to edge
if( tex_channel[1].tex.getExtend() == "Clip" ):
flags = flags | 2
buffer = "\ttflags1( %s )\n" % flags
f.write( buffer )
t_name = tex_channel[1].tex.image.filename
if( t_name.endswith('.pvr.png') > 0):
t_name = t_name.replace('.pvr.png', '.att')
buffer = "\ttex1( \"%s\" )\n" % ( "image/" + os.path.basename( t_name ) )
f.write( buffer )
#================================
# SOUND
#================================
if( st2 == 1 ):
# Autoplay
if( tex_channel[2].tex.imageFlags & Texture.ImageFlags["MIPMAP"] ):
flags = flags | 1
# Loop
if( tex_channel[2].tex.getExtend() == "Repeat" ):
flags = flags | 2
# Ambient
if( tex_channel[2].tex.imageFlags & Texture.ImageFlags["INTERPOL"] ):
flags = flags | 4
# FX
else:
flags = flags | 8
buffer = "\tsflags( %s )\n" % flags
f.write( buffer )
buffer = "\tsbname( \"%s\" )\n" % ( "sound/" + os.path.basename( tex_channel[2].tex.image.filename ) )
f.write( buffer )
#================================
# DIFFUSE
#================================
buffer = "\tdiffuse( %s %s %s )\n" % ( optimize_float( mat.getRGBCol()[0] ),
optimize_float( mat.getRGBCol()[1] ),
optimize_float( mat.getRGBCol()[2] ) )
f.write( buffer )
#================================
# SPECULAR
#================================
buffer = "\tspecular( %s %s %s )\n" % ( optimize_float( mat.getSpecCol()[0] ),
optimize_float( mat.getSpecCol()[1] ),
optimize_float( mat.getSpecCol()[2] ) )
f.write( buffer )
#================================
# ALPHA
#================================
if( mat.getAlpha() ):
buffer = "\talpha( %s )\n" % optimize_float( mat.getAlpha() )
f.write( buffer )
#================================
# SHININESS
#================================
if( mat.getHardness() ):
buffer = "\tshininess( %s )\n" % optimize_float( ( mat.getHardness() / 4 ) )
f.write( buffer )
#================================
# FRICTION
#================================
if( mat.rbFriction ):
buffer = "\tfriction( %s )\n" % optimize_float( mat.rbFriction )
f.write( buffer )
#================================
# RESTITUTION
#================================
if( mat.rbRestitution ):
buffer = "\trestitution( %s )\n" % optimize_float( mat.rbRestitution )
f.write( buffer )
#================================
# ALPHA LEVEL
#================================
if( mat.getTranslucency() ):
buffer = "\talvl( %s )\n" % optimize_float( mat.getTranslucency() )
f.write( buffer )
#================================
# BLEND MODE
#================================
blend = 0
if( st0 > 0 ):
blend = tex_channel[0].blendmode
# Use Value for Alpha
if( blend == 12 ):
blend = 1
elif( blend > 0 ):
blend = blend + 1
if( blend ):
buffer = "\tblend( %s )\n" % blend
f.write( buffer )
#================================
# END MATERIAL
#================================
f.write( "}\n" )
f.close()
zzajin- Posts : 81
Join date : 2008-10-14
Re: Compressed Textures from Apple's texturetool
part 3
- Code:
#====================================
# LAMP
#====================================
for obj in objects:
if( obj.getType() == "Lamp" and obj.layers[0] == layer[0] and obj.isSelected() ):
if( UPDATE or not os.path.exists( output + "lamp/" + obj.getName() ) ):
#====================================
# Write the current lamp data.
#====================================
f = open( output + "lamp/" + obj.getName(), "w")
#================================
# NAME
#================================
buffer = "lamp( \"%s\")\n{\n" % ( "lamp/" + obj.getName() )
f.write( buffer )
#===============================
# TYPE
#===============================
# 0 = Lamp
# 1 = Sun
# 2 = Spot
# 3 = Hemi
# 4 = Area
#===============================
lamp = obj.getData()
if( lamp.getType() ):
buffer = "\ttype( %s )\n" % lamp.getType()
f.write( buffer )
#===============================
# FLAGS
#===============================
flags = 0
if( lamp.getMode() & Blender.Lamp.Modes["NoDiffuse"] ):
flags = flags | 1
if( lamp.getMode() & Blender.Lamp.Modes["NoSpecular"] ):
flags = flags | 2
if( flags ):
buffer = "\tflags( %s )\n" % flags
f.write( buffer )
#===============================
# POSITION
#===============================
buffer = "\tpos( %s %s %s )\n" % ( optimize_float( obj.loc[0] ),
optimize_float( obj.loc[1] ),
optimize_float( obj.loc[2] ) )
f.write( buffer )
#===============================
# DIRECTION VECTOR
#===============================
dir = Blender.Mathutils.Vector( 0.0, 0.0, -1.0 )
if( lamp.getType() == 1 ):
dir.x = obj.matrixWorld[2][0]
dir.y = obj.matrixWorld[2][1]
dir.z = obj.matrixWorld[2][2]
elif( lamp.getType() == 2 ):
dir.x = -obj.matrixWorld[2][0]
dir.y = -obj.matrixWorld[2][1]
dir.z = -obj.matrixWorld[2][2]
buffer = "\tdir( %s %s %s )\n" % ( optimize_float( dir.x ),
optimize_float( dir.y ),
optimize_float( dir.z ) )
f.write( buffer )
#===============================
# COLOR
#===============================
buffer = "\tcol( %s %s %s )\n" % ( optimize_float( lamp.col[0] ),
optimize_float( lamp.col[1] ),
optimize_float( lamp.col[2] ) )
f.write( buffer )
#===============================
# ENERGY
#===============================
buffer = "\tnrg( %s )\n" % optimize_float( lamp.getEnergy() )
f.write( buffer )
#===============================
# DISTANCE
#===============================
buffer = "\tdst( %s )\n" % optimize_float( lamp.getDist() )
f.write( buffer )
#===============================
# FOV
#===============================
buffer = "\tfov( %s )\n" % optimize_float( lamp.getSpotSize() )
f.write( buffer )
#===============================
# SPOT BLEND
#===============================
buffer = "\tsblend( %s )\n" % optimize_float( lamp.getSpotBlend() )
f.write( buffer )
#===============================
# LINEAR ATTENUATION
#===============================
buffer = "\tatt1( %s )\n" % optimize_float( lamp.getQuad1() )
f.write( buffer )
#===============================
# QUADRATIC ATTENUATION
#===============================
buffer = "\tatt2( %s )\n" % optimize_float( lamp.getQuad2() )
f.write( buffer )
#===============================
# CLOSE LAMP
#===============================
f.write( "}\n" )
obj.select(0)
f.close()
#====================================
# OBJECT
#====================================
for obj in objects:
if( obj.getType() == "Mesh" and obj.layers[0] == layer[0] and obj.isSelected() ):
if( UPDATE or not os.path.exists( output + "object/" + obj.getName() ) ):
#====================================
# Write the current object data.
#====================================
f = open( output + "object/" + obj.getName(), "w")
#====================================
# Get access to the mesh of this
# object...
#====================================
mesh = obj.getData( False, True )
#====================================
# Convert all quads into triangles,
# and remove every duplicates.
#====================================
for face in mesh.faces:
face.sel = 1
mesh.quadToTriangle( 0 )
mesh.remDoubles( 0 )
mesh.update()
mesh = obj.getData( False, True )
for face in mesh.faces:
face.sel = 0
#================================
# NAME
#================================
buffer = "object( \"%s\" )\n{\n" % ( "object/" + obj.getName() )
f.write( buffer )
#===============================
# POSITION
#===============================
buffer = "\tpos( %s %s %s )\n" % ( optimize_float( obj.loc[0] ),
optimize_float( obj.loc[1] ),
optimize_float( obj.loc[2] ) )
f.write( buffer )
#===============================
# ROTATION (in degree)
#===============================
rot = []
rot.append( obj.rot[0] * RAD_TO_DEG )
rot.append( obj.rot[1] * RAD_TO_DEG )
rot.append( obj.rot[2] * RAD_TO_DEG )
if( rot[0] < 0 ): rot[0] = rot[0] + 360.0
if( rot[1] < 0 ): rot[1] = rot[1] + 360.0
if( rot[2] < 0 ): rot[2] = rot[2] + 360.0
if( optimize_float( rot[0] ) != 0.0 or
optimize_float( rot[1] ) != 0.0 or
optimize_float( rot[2] ) != 0.0 ):
buffer = "\trot( %s %s %s )\n" % ( optimize_float( rot[0] ),
optimize_float( rot[1] ),
optimize_float( rot[2] ) )
f.write( buffer )
#===============================
# SCALE
#===============================
if( optimize_float( obj.size[0] ) != 1.0 or
optimize_float( obj.size[1] ) != 1.0 or
optimize_float( obj.size[2] ) != 1.0 ):
buffer = "\tscl( %s %s %s )\n" % ( optimize_float( obj.size[0] ),
optimize_float( obj.size[1] ),
optimize_float( obj.size[2] ) )
f.write( buffer )
#================================
# BOUNDING SPHERE RADIUS
#================================
i = 0
j = 1
bb = obj.getBoundBox()
bs_radius = 0
for i in range( 7 ):
for j in range( 8 ):
dd = ( distance( bb[i], bb[j] ) * 0.5 )
if( dd > bs_radius ):
bs_radius = dd
buffer = "\trad( %s )\n" % optimize_float( bs_radius )
f.write( buffer )
#===============================
# PHYSIC FLAGS
#===============================
flags = 0
bounds = 4
mass = 0.0
if( obj.rbFlags & Object.RBFlags["ACTOR"] ):
flags = flags | 1
if( obj.rbFlags & Object.RBFlags["GHOST"] ):
flags = flags | 2
if( obj.rbFlags & Object.RBFlags["ACTOR"] and
obj.rbFlags & Object.RBFlags["DYNAMIC"] ):
flags = flags | 4
if( obj.rbFlags & Object.RBFlags["ACTOR"] and
obj.rbFlags & Object.RBFlags["DYNAMIC"] and
obj.rbFlags & Object.RBFlags["RIGIDBODY"] ):
flags = flags | 8
"""
if( obj.rbFlags & Object.RBFlags["ACTOR"] and
obj.rbFlags & Object.RBFlags["DYNAMIC"] and
obj.rbFlags & Object.RBFlags["SOFTBODY"] ):
flags = flags & ~ 8
flags = flags | 16
"""
if( obj.rbFlags & Object.RBFlags["COLLISION_RESPONSE"] ):
flags = flags | 256
#===============================
# MISC. FLAGS
#===============================
uv_channel = mesh.getUVLayerNames()
for face in mesh.faces:
if( not len( uv_channel ) ):
break
# Two Side
if( face.mode & NMesh.FaceModes["TWOSIDE"] ):
flags = flags | 128
# Cylindrical
if( face.mode & NMesh.FaceModes["BILLBOARD"] ):
flags = flags | 32
break
# Spherical
if( face.mode & NMesh.FaceModes["HALO"] ):
flags = flags | 64
break
if( flags ):
buffer = "\tflags( %s )\n" % flags
f.write( buffer )
#====================================
# OBJECT BOUNDS
#
# 0 - Cube
# 1 - Sphere
# 2 - Cylinder
# 3 - Cone
# 4 - Static Triangle Mesh
# 5 - Convex Hull Polytope
#
#====================================
if( obj.rbFlags & Object.RBFlags[ "ACTOR" ] and
obj.rbFlags & Object.RBFlags[ "DYNAMIC" ] ):
"""
or
obj.rbFlags & Object.RBFlags[ "SOFTBODY"]
"""
bounds = obj.rbShapeBoundType
if( bounds != 4 ):
mass = obj.rbMass
if( bounds ):
buffer = "\tbounds( %s )\n" % bounds
f.write( buffer )
#===============================
# MASS
#===============================
if( mass ):
buffer = "\tmass( %s )\n" % optimize_float( mass )
f.write( buffer )
#===============================
# Please take note that the
# following properties cannot
# be accessed with python, you'll
# have to manually enter them
# to the object properties in
# order for them to work.
#===============================
"""
damp <float>
rotdamp <float>
margin <float>
"""
#===============================
# DIM XYZ
#===============================
dim = get_dim( obj, mesh )
buffer = "\tdim( %s %s %s )\n" % ( optimize_float( dim[0] ),
optimize_float( dim[1] ),
optimize_float( dim[2] ) )
f.write( buffer )
#===============================
# USER PROPERTIES
#===============================
properties = obj.getAllProperties()
for p in properties:
if( p.getType() == "STRING" ):
buffer = "\t%s( \"%s\" )\n" % ( p.getName(), p.getData() )
else:
buffer = "\t%s( %s )\n" % ( p.getName(), p.getData() )
f.write( buffer )
#===============================
# SCRIPTS
#===============================
script = obj.getScriptLinks( "FrameChanged" )
for s in script:
tmp_name = output + "script/" + s
t = Blender.Text.Get( s )
if( UPDATE or not os.path.exists( tmp_name ) ):
shutil.copyfile( t.getFilename(), tmp_name )
#===============================
# INSTANCE
#===============================
inst = get_instance( obj.getName() )
if( inst and inst.layers[0] == layer[0] ):
buffer = "\tiname( \"%s\" )\n" % ( "object/" + inst.getName() )
f.write( buffer )
#===============================
# CLOSE OBJECT
#===============================
f.write( "}\n" )
obj.select(0)
f.close()
continue
#================================
# CREATE VERTEX GROUP (IF NOT ANY)
#================================
if( not len( mesh.getVertGroupNames() ) ):
mesh.addVertGroup( "null" )
tmp_vert_lst = []
for v in mesh.verts:
tmp_vert_lst.append( v.index )
mesh.assignVertsToGroup( "null", tmp_vert_lst, 1.0, Blender.Mesh.AssignModes.REPLACE )
mesh.update()
zzajin- Posts : 81
Join date : 2008-10-14
Re: Compressed Textures from Apple's texturetool
part 4
- Code:
#===============================
# CREATE THE NECESSARY VBO ARRAY
#===============================
vert_lst = []
for face in mesh.faces:
for i in range( 3 ):
tmp_vert = vertex()
tmp_vert.ver = Blender.Mathutils.Vector( optimize_float( face.v[i].co[0] ),
optimize_float( face.v[i].co[1] ),
optimize_float( face.v[i].co[2] ) )
tmp_vert.vco = None
if( mesh.vertexColors ):
rgba = [ face.col[i][0],
face.col[i][1],
face.col[i][2],
face.col[i][3] ]
tmp_vert.vco = rgba
tmp_vert.vno = None
if( NORMALS ):
if( face.smooth ):
tmp_vert.vno = Blender.Mathutils.Vector( optimize_float( face.v[i].no[0] ),
optimize_float( face.v[i].no[1] ),
optimize_float( face.v[i].no[2] ) )
else:
tmp_vert.vno = Blender.Mathutils.Vector( optimize_float( face.no[0] ),
optimize_float( face.no[1] ),
optimize_float( face.no[2] ) )
tmp_vert.uv0 = None
if( len( uv_channel ) ):
mesh.activeUVLayer = uv_channel[0]
tmp_vert.uv0 = Blender.Mathutils.Vector( optimize_float( face.uv[i][0] ),
optimize_float( 1.0 - face.uv[i][1] ),
0.0 )
tmp_vert.uv1 = None
if( len( uv_channel ) > 1 ):
mesh.activeUVLayer = uv_channel[1]
tmp_vert.uv1 = Blender.Mathutils.Vector( optimize_float( face.uv[i][0] ),
optimize_float( 1.0 - face.uv[i][1] ),
0.0 )
if( get_index( vert_lst, tmp_vert ) == -1 ):
vert_lst.append( tmp_vert )
#===============================
# BOFFSET
#===============================
vbo_offset = [ 0, 0, 0, 0 ]
vbo_size = len( vert_lst ) * 3 * 4
# Vertex Color
if( mesh.vertexColors ):
vbo_offset[0] = vbo_size
vbo_size = vbo_size + len( vert_lst ) * 4
# Vertex Normals
if( NORMALS ):
vbo_offset[1] = vbo_size
vbo_size = vbo_size + len( vert_lst ) * 3 * 4
# UV0
if( len( uv_channel ) ):
vbo_offset[2] = vbo_size
vbo_size = vbo_size + len( vert_lst ) * 2 * 4
# UV1
if( len( uv_channel ) > 1 ):
vbo_offset[3] = vbo_size
vbo_size = vbo_size + len( vert_lst ) * 2 * 4
#===============================
# WRITE BUFFER SIZE + OFFSET
#===============================
buffer = "\tvbo_offset( %s %s %s %s %s )\n" % ( vbo_size,
vbo_offset[0],
vbo_offset[1],
vbo_offset[2],
vbo_offset[3] )
f.write( buffer )
#===============================
# VBO
#===============================
# Vertex
for v in vert_lst:
buffer = "\tvert( %s %s %s )\n" % ( optimize_float( v.ver.x ),
optimize_float( v.ver.y ),
optimize_float( v.ver.z ) )
f.write( buffer )
# Vertex Color
if( mesh.vertexColors ):
vbo_offset[0] = vbo_size
vbo_size = vbo_size + len( vert_lst ) * 4
for v in vert_lst:
buffer = "\tvcol( %s %s %s %s )\n" % ( v.vco[ 0 ],
v.vco[ 1 ],
v.vco[ 2 ],
v.vco[ 3 ] )
f.write( buffer )
# Vertex Normals
if( NORMALS ):
vbo_offset[1] = vbo_size
vbo_size = vbo_size + len( vert_lst ) * 3 * 4
for v in vert_lst:
buffer = "\tvnor( %s %s %s )\n" % ( optimize_float( v.vno.x ),
optimize_float( v.vno.y ),
optimize_float( v.vno.z ) )
f.write( buffer )
# UV0
if( len( uv_channel ) ):
vbo_offset[2] = vbo_size
vbo_size = vbo_size + len( vert_lst ) * 2 * 4
for v in vert_lst:
buffer = "\tuv0( %s %s )\n" % ( optimize_float( v.uv0.x ),
optimize_float( v.uv0.y ) )
f.write( buffer )
# UV1
if( len( uv_channel ) > 1 ):
vbo_offset[3] = vbo_size
vbo_size = vbo_size + len( vert_lst ) * 2 * 4
for v in vert_lst:
buffer = "\tuv1( %s %s )\n" % ( optimize_float( v.uv1.x ),
optimize_float( v.uv1.y ) )
f.write( buffer )
#================================
# WRITE THE N VERTEX GROUPS
#================================
buffer = "\tn_vgroup( %s )\n" % len( mesh.getVertGroupNames() )
f.write( buffer )
#================================
# WRITE THE VERTEX GROUPS BLOCKS
#================================
for vg in mesh.getVertGroupNames():
# Unselect All
for i in mesh.verts:
i.sel = 0
for face in mesh.faces:
face.sel = 0
# Get vertex list
try:
v_lst = mesh.getVertsFromGroup( vg, 1 )
except:
error_str = "ERROR: This object contain no vertices: %s. |" % obj.getName()
PupMenu( error_str )
return
#===============================
# NAME
#===============================
buffer = "\tvgroup( \"%s\" )\n" % vg
f.write( buffer )
# Select all group vertices
for v in v_lst:
mesh.verts[ v[0] ].sel = 1
n_ind = 0
for face in mesh.faces:
if( face.v[0].sel == 1 and
face.v[1].sel == 1 and
face.v[2].sel == 1 ):
face.sel = 1
if( not n_ind ):
if( len( mesh.materials ) ):
mat = mesh.materials[ face.mat ]
buffer = "\tmname( \"%s\" )\n" % ( "material/" + mat.getName() )
f.write( buffer )
n_ind = n_ind + 3
#===============================
# N_INDICES
#===============================
buffer = "\tn_ind( %s )\n" % n_ind
f.write( buffer )
#===============================
# TRIANGLE INDICES
#===============================
for face in mesh.faces:
if( face.sel == 1 ):
buffer = "\tind( "
f.write( buffer )
#===============================
# INDICES
#===============================
for i in range( 3 ):
tmp_vert = vertex()
tmp_vert.ver = Blender.Mathutils.Vector( optimize_float( face.v[i].co[0] ),
optimize_float( face.v[i].co[1] ),
optimize_float( face.v[i].co[2] ) )
tmp_vert.vco = None
if( mesh.vertexColors ):
rgba = [ face.col[i][0],
face.col[i][1],
face.col[i][2],
face.col[i][3] ]
tmp_vert.vco = rgba
tmp_vert.vno = None
if( NORMALS ):
if( face.smooth ):
tmp_vert.vno = Blender.Mathutils.Vector( optimize_float( face.v[i].no[0] ),
optimize_float( face.v[i].no[1] ),
optimize_float( face.v[i].no[2] ) )
else:
tmp_vert.vno = Blender.Mathutils.Vector( optimize_float( face.no[0] ),
optimize_float( face.no[1] ),
optimize_float( face.no[2] ) )
tmp_vert.uv0 = None
if( len( uv_channel ) ):
mesh.activeUVLayer = uv_channel[0]
tmp_vert.uv0 = Blender.Mathutils.Vector( optimize_float( face.uv[i][0] ),
optimize_float( 1.0 - face.uv[i][1] ),
0.0 )
tmp_vert.uv1 = None
if( len( uv_channel ) > 1 ):
mesh.activeUVLayer = uv_channel[1]
tmp_vert.uv1 = Blender.Mathutils.Vector( optimize_float( face.uv[i][0] ),
optimize_float( 1.0 - face.uv[i][1] ),
0.0 )
index = get_index( vert_lst, tmp_vert )
if( index == -1 ):
error_str = "ERROR: Unable to find vertex indice: %s. |" % obj.getName()
PupMenu( error_str )
return
buffer = "%s " % index
f.write( buffer )
buffer = ")\n"
f.write( buffer )
#===============================
# CLOSE OBJECT
#===============================
f.write( "}\n" )
obj.select(0)
f.close()
#====================================
# CREATE/UPDATE THE ZIP ARCHIVE
#====================================
os.chdir( output )
if( not UPDATE ):
remove = "rm ../" + scene.getName() + ".sio2"
os.system( remove )
compress = "zip -9 -o ../" + scene.getName() + ".sio2 -r *"
os.system( compress )
#===============================
# END
#===============================
error_str += "Export Successful: %.3f sec." % ( Blender.sys.time() - start_time )
PupMenu( error_str )
#Exit()
#====================================
# MAIN
#====================================
register_gui()
zzajin- Posts : 81
Join date : 2008-10-14
Re: Compressed Textures from Apple's texturetool
Thanks for your efforts. Does using this format make any difference in terms of performance?
Best,
Matt
Best,
Matt
Re: Compressed Textures from Apple's texturetool
matt wrote:Thanks for your efforts. Does using this format make any difference in terms of performance?
While I have no numbers to back me up - it should: both in memory usage and speed.
It's part of the recommended method for optimizing textures according to Apple https://developer.apple.com/iphone/library/technotes/tn2008/tn2230.html and Imagination Technologies ( I don't have that link - actually I think it's in a pdf).
Actually seeing the memory usage change should be easy - I just haven't checked it out.
Part of my hope is the workflow is convenient enough to make trying different textures semi-easy.
zzajin- Posts : 81
Join date : 2008-10-14
Re: Compressed Textures from Apple's texturetool
If you ever get some numbers on performance, please let me know. Memory isn't really an issue for me, but performance is. But, honestly, I think the image format is only interesting for disk space and pregenerated mipmaps, internally the device will most likely store the texture in a format most convenient for the texture processing unit. (But I have no idea about the GPU internals so I may very well be wrong. ;-))zzajin wrote:While I have no numbers to back me up - it should: both in memory usage and speed.
Best,
Matt
Re: Compressed Textures from Apple's texturetool
Thanks again zzajin,
For some reason I still can't get the material to export. It either doesn't complete the export or I get a material that is an empty file. Any idea what it can be?
For some reason I still can't get the material to export. It either doesn't complete the export or I get a material that is an empty file. Any idea what it can be?
uprise78- Posts : 228
Join date : 2008-10-31
Re: Compressed Textures from Apple's texturetool
Would you mind sending me you blend file, .pvr.png, and .att file(s)? I sent you my email. Also what version of blender are you using? Are the textures in the same directory and differ only by ".pvr.png" and ".att" extensions?
zzajin- Posts : 81
Join date : 2008-10-14
Re: Compressed Textures from Apple's texturetool
zzajin wrote:Would you mind sending me you blend file, .pvr.png, and .att file(s)? I sent you my email. Also what version of blender are you using? Are the textures in the same directory and differ only by ".pvr.png" and ".att" extensions?
Email sent with the files. I am using Blender 2.48. The .att files seem to get created properly along with the .pvr.png files.
Thanks for looking at this.
uprise78- Posts : 228
Join date : 2008-10-31
Re: Compressed Textures from Apple's texturetool
Just so everyone knows, zzajin is awesome. He found the problem in no time at all (my silly mistake for having an indent off in the Python script). Many thanks to zzajin for this contribution which will allow textures to load faster and take up less memory!
Thanks zzajin.
Thanks zzajin.
uprise78- Posts : 228
Join date : 2008-10-31
Re: Compressed Textures from Apple's texturetool
Is there any chance of getting these changes into SIO2? This is the type of optimization that everyone can benefit from.
uprise78- Posts : 228
Join date : 2008-10-31
Re: Compressed Textures from Apple's texturetool
Unfortunately, this is somewhat outdated now and I'm not sure when I'll be able to update it. The newest version of the SDK includes a new texturetool. This new version includes mipmaps and I think creates pvr files similar to ImgTech's file format. There is sample code for parsing the pvr file available in the dev center. Having the meta data embedded into the file would be a nice improvement. As would mipmaps of course.
zzajin- Posts : 81
Join date : 2008-10-14
Similar topics
» Using Compressed Textures
» Textures Killing My App
» Need help with multiple textures
» A couple questions concerning textures...
» Some Info on PVR Textures for anyone interested
» Textures Killing My App
» Need help with multiple textures
» A couple questions concerning textures...
» Some Info on PVR Textures for anyone interested
Permissions in this forum:
You cannot reply to topics in this forum
|
|