Why are my TIMs Displayed Garbled?

Graphic based area of development (Graphics Processing Unit), including the Geometry Transform Engine (GTE), TIM, STR (MDEC), etc.
Post Reply
User avatar
Sirrico
Curious PSXDEV User
Curious PSXDEV User
Posts: 27
Joined: Sep 05, 2014
PlayStation Model: SCPH-1001

Why are my TIMs Displayed Garbled?

Post by Sirrico » October 6th, 2014, 10:49 am

I've been following a few tutorials on using TIM files and I can't seem to get it right. Every time I try to display an image on the screen, the colors end up garbled.
ImageImage
The left image is the program as it is run through an emulator, and the right is the TIM when loaded in timtool. Here is my process:
1. Draw a 256x240 BMP image.
2. Convert to TIM format with TimViewer.
3. Place in the correct zone with TimTool.
4. Attempt to create CLUT, but it tells me I don't need one.
5. "Save updated TIM".
6. Write code to display, run program.

Am I doing something wrong? Did I miss a step? I've tried replacing my image into other people's code but it does the same.
-Sirrico

User avatar
LameGuy64
Verified
Psy-Q Enthusiast
Psy-Q Enthusiast
Posts: 388
Joined: Apr 10, 2013
I am a: Hobbyist Game Developer
Motto: Commercial or not, play it!
PlayStation Model: H2000/7000
Location: Philippines
Contact:

Post by LameGuy64 » October 6th, 2014, 11:21 am

Could you show some of your code? Mainly the image drawing part.
Please don't forget to include my name if you share my work around. Credit where it is due.

Dev. Console: SCPH-7000 with SCPH-7501 ROM, MM3, PAL color fix, Direct AV ports, DB-9 port for Serial I/O, and a Xplorer FX with Caetla 0.35.

DTL-H2000 PC: Dell Optiplex GX110, Windows 98SE & Windows XP, Pentium III 933MHz, 384MB SDRAM, ATI Radeon 7000 VE 64MB, Soundblaster Audigy, 40GB Seagate HDD, Hitachi Lite-on CD-RW Drive, ZIP 250 and 3.5" Floppy.

User avatar
Sirrico
Curious PSXDEV User
Curious PSXDEV User
Posts: 27
Joined: Sep 05, 2014
PlayStation Model: SCPH-1001

Post by Sirrico » October 6th, 2014, 11:43 am

LameGuy64 wrote:Could you show some of your code? Mainly the image drawing part.
Here is one set of code I'm using, coming from Orion_'s libraries.

Code: Select all

#include "System.h"
#include "Sound.h"
#include "Sprite.h"
#include "DataManager.h"

//#define	CD_AUDIO_EXAMPLE	// Uncomment this to enable CD Audio Example

/******************************************/

GsIMAGE		TIMimage;
GsSPRITE	Sprite;

// This is a group of files we want to load
// My Data manager is set by default to load files data at address 0x80010000 which is the beginning of the user ram (see DataManager.h)
// But don't forget that we only have 2Mbytes of ram, and that our program is compiled to be loaded at the address 0x80100000 (see comp.bat)
// So this only give us 960Kbytes of free space for our data at one time ! don't forget it !

DataManager_Files	game_datas[] =
{
	{"sprite.tim", 0},
	{"ui4.vag", 0},
	{NULL, 0}
};

// These are corresponding index from the file above, it's easier to access using these name rather than index numbers
enum
{
	SPRITE_TIM,
	SOUND_VAG
};

/******************************************/

int			main(void)
{
	int		i;
	GsOT	*ot;
	int		cputime, gputime;
	int		sx, sy;

	// Init in 16bits 320x240
	System_Init(MODE_PAL, VMODE_16BITS, 320, 240, GsNONINTER, 0);
	System_SetBackgroundColor(255,255,255);	// Set White background color

	// Load our files (from cd or from pc depending on CDROM_RELEASE flag compilation)
	DataManager_Init();
	DataManager_LoadDatas("DATA", game_datas);	// from the "DATA" folder

	// Load TIM Images in VRAM from our loaded data files
	Tim_Load(&TIMimage, game_datas[SPRITE_TIM].address);

	// Init our sprite from the TIM image previously loaded in VRAM
	Sprite_Init(&Sprite, &TIMimage, SPRITE_NORMAL, 0, 0, 256, 240);

	// Load our sound in channel 0 from our previously loaded data
	Sound_Init();
	Sound_Load(0, game_datas[SOUND_VAG].address);

#ifdef	CD_AUDIO_EXAMPLE
	printf("Please Insert an Audio CD");

	CdInit();
	Sound_CD_Init();
	printf("CD ntracks: %d\n", Sound_CD_GetNumTracks());
#endif

	// Init Our Sprite Position in the middle for the screen
	sx = 1;
	sy = 1;

	// Main Display Loop until we press Start button on pad
	while (!IsPadPress(Pad1Start))
	{
		// This init our drawing, it return an OT pointer which is the table in which we will ask to draw
		ot = System_InitFrame();

		// Play our sound in channel 0 if Cross button is pressed on pad
		if (IsPadTrig(Pad1Cross))
			Sound_Play(0);

#ifdef	CD_AUDIO_EXAMPLE
		if (IsPadTrig(Pad1Triangle))
			Sound_CD_Play(2, CD_REPEAT);

		if (IsPadTrig(Pad1Square))
			Sound_CD_Stop();

		if (IsPadTrig(Pad1Circle))
			if (Sound_CD_IsPlaying())
				printf("Yes\n");
#endif

		// Move the sprite if you press on the arrows on the pad
		if (IsPadPress(Pad1Left))
			sx--;
		if (IsPadPress(Pad1Right))
			sx++;
		if (IsPadPress(Pad1Up))
			sy--;
		if (IsPadPress(Pad1Down))
			sy++;

		// Set the sprite position
		Sprite_SetPosition(&Sprite, sx, sy);

		// Ask to Draw the sprite in the OT
		Sprite_DrawFast(&Sprite, ot);

		// Ask to draw the OT on screen !
		System_DrawFrame(ot, &cputime, &gputime);	// You can print cputime and gputime to track how fast your program is, but you can also use NULL pointer if you don't mind.
	}

	System_Exit();
	return (0);
}
My other set of code comes from this example on this site, but it does not produce anything on the screen.

Code: Select all

#include <sys/types.h>
#include <libetc.h>
#include <libgte.h>
#include <inline_c.h>
#include <gtemac.h>
#include <libgpu.h>
#define SCR_Z	507		// distant to screen

extern unsigned long TIMaddr[];	// background TIM
static u_long PadData;

static void initTexture(unsigned long *timAddr)
	{
	unsigned long bnum;		// number of bytes
	RECT rect;				// LoadImage rectangle
	timAddr++;
	if (*timAddr & 8)				// check CLUT flag
		{
		timAddr++;					// load CLUT info
		bnum = *timAddr;
		timAddr++;
		rect.x = *timAddr & 0xffff;
		rect.y = *timAddr >> 0x10;
		timAddr++;
		rect.w = *timAddr & 0xffff;
		rect.h = *timAddr >> 0x10;
		timAddr++;
		LoadImage(&rect,timAddr);
		timAddr += (bnum >> 2) - 2;
		}
	else
		timAddr += 2;
	rect.x = *timAddr & 0xffff;		// load pixel info
	rect.y = *timAddr >> 0x10;
	timAddr++;
	rect.w = *timAddr & 0xffff;
	rect.h = *timAddr >> 0x10;
	timAddr++;
	LoadImage(&rect,timAddr);
	DrawSync(0);
	}


int main(void)
	{


	DRAWENV	drawenv;
	DISPENV	dispenv;
	ResetCallback();
	ResetGraph(0);				// reset graphic subsystem (0:cold,1:warm)
	SetGraphDebug(0);			// set debug mode (0:off, 1:monitor, 2:dump)
	PadInit(0);             	// initialise PAD
	InitGeom();					// initialise geometry subsystem
	SetGeomOffset(320, 240);	// set geometry origin
	SetGeomScreen(SCR_Z);		// distance to viewing-screen
	SetDefDrawEnv(&drawenv, 0, 0, 320, 240);  	// initialise environment
	SetDefDispEnv(&dispenv, 0, 0, 320, 240);
	dispenv.isinter = 1;		// interlaced
	drawenv.isbg = 0;			// don't clear background
	SetDispMask(1);				// enable display (0:inhibit, 1:enable)
	PutDrawEnv(&drawenv); 		// update drawing environment
	PutDispEnv(&dispenv); 		// update display environment
	initTexture(TIMaddr);		// load TIMs
//	VSyncCallback(0);
//	StopCallback();
	ResetGraph(3);
	return 0;
	}
I'm basically just copypasting code and changing the image, and changing the display resolution in the code.
-Sirrico

User avatar
Shadow
Verified
Admin / PSXDEV
Admin / PSXDEV
Posts: 2670
Joined: Dec 31, 2012
PlayStation Model: H2000/5502
Discord: Shadow^PSXDEV

Post by Shadow » October 6th, 2014, 3:47 pm

Try using an 8-bit TIM instead of 4-BIT.
Development Console: SCPH-5502 with 8MB RAM, MM3 Modchip, PAL 60 Colour Modification (for NTSC), PSIO Switch Board, DB-9 breakout headers for both RGB and Serial output and an Xplorer with CAETLA 0.34.

PlayStation Development PC: Windows 98 SE, Pentium 3 at 400MHz, 128MB SDRAM, DTL-H2000, DTL-H2010, DTL-H201A, DTL-S2020 (with 4GB SCSI-2 HDD), 21" Sony G420, CD-R burner, 3.25" and 5.25" Floppy Diskette Drives, ZIP 100 Diskette Drive and an IBM Model M keyboard.

User avatar
LameGuy64
Verified
Psy-Q Enthusiast
Psy-Q Enthusiast
Posts: 388
Joined: Apr 10, 2013
I am a: Hobbyist Game Developer
Motto: Commercial or not, play it!
PlayStation Model: H2000/7000
Location: Philippines
Contact:

Post by LameGuy64 » October 6th, 2014, 3:59 pm

Here's a good example on how to display TIMs properly:

Code: Select all

#include <sys/types.h>
#include <libetc.h>
#include <libgte.h>
#include <libgpu.h>
#include <libgs.h>


// Test TIM
#include "lunarbow.c"


#define OT_LENGTH	10

#define OT_ENTRIES	1<<OT_LENGTH
#define PACKETMAX	2048


GsOT		myOT[2];						// OT handlers
GsOT_TAG	myOT_TAG[2][OT_ENTRIES];		// OT tables
PACKET		myPacketArea[2][PACKETMAX*24];	// Packet buffers
int			myActiveBuff=0;					// Page index counter


// Prototypes
void LoadTexture(GsIMAGE *image, u_long *addr);
void InitSprite(GsSPRITE *sprite, GsIMAGE image);

void InitVideo();
void PrepDisplay();
void Display();


// Main function
int main() {

	GsIMAGE		spriteimage;
	GsSPRITE	sprite;

	// Init video
	InitVideo();
	
	
	// Load the texture and prepare the sprite object
	LoadTexture(&spriteimage, (u_long*)tim_image);
	InitSprite(&sprite, spriteimage);
	
	
	// Main loop
	while(1) {
		
		// Prepare display
		PrepDisplay();
		
		
		// Draw the sprite
		GsSortSprite(&sprite, &myOT[myActiveBuff], 0);
		
		
		// Display the current OT
		Display();
		
	}

}


void InitSprite(GsSPRITE *sprite, GsIMAGE image) {
	
	// Set texture size and coordinates
	switch (image.pmode & 3) {
		case 0: // 4-bit
			sprite->w = image.pw << 2;
			sprite->u = (image.px & 0x3f) * 4;
			break;
		case 1: // 8-bit
			sprite->w = image.pw << 1;
			sprite->u = (image.px & 0x3f) * 2;
			break;
		default: // 16-bit
			sprite->w = image.pw;
			sprite->u = image.px & 0x3f; 
    };
	
	sprite->h = image.ph;
	sprite->v = image.py & 0xff;

	// Set texture page and color depth attribute
	sprite->tpage 		= GetTPage((image.pmode & 3), 0, image.px, image.py);
	sprite->attribute 	= (image.pmode & 3) << 24;
	
	// CLUT coords
	sprite->cx 			= image.cx;
	sprite->cy 			= image.cy;
	
	// Default position, color intensity, and scale/rotation values
	sprite->x		= sprite->y 				= 0;
	sprite->mx		= sprite->my				= 0;
	sprite->r		= sprite->g = sprite->b 	= 128;
	sprite->scalex 	= sprite->scaley 			= ONE;
	sprite->rotate 	= 0;
	
}
void LoadTexture(GsIMAGE *image, u_long *addr) {
	
	// A simple TIM loader... Not much to explain
	
	RECT rect;
	
	// Get TIM information
	GsGetTimInfo((addr+1), image);
	
	// Load the texture image
	rect.x = image->px;	rect.y = image->py;
	rect.w = image->pw;	rect.h = image->ph;
	LoadImage(&rect, image->pixel);
	DrawSync(0);
	
	// Load the CLUT (if there is one)
	if ((image->pmode>>3) & 0x01) {
		rect.x = image->cx;	rect.y = image->cy;
		rect.w = image->cw;	rect.h = image->ch;
		LoadImage(&rect, image->clut);
		DrawSync(0);
	}
	
}

void InitVideo() {
	
	// Init video system
	GsInitGraph(320, 240, GsOFSGPU|GsNONINTER, 0, 0);	
	GsDefDispBuff(0, 0, 0, 240);
	
	// Prepare the ordering tables
	myOT[0].length	=OT_LENGTH;
	myOT[1].length	=OT_LENGTH;
	myOT[0].org		=myOT_TAG[0];
	myOT[1].org		=myOT_TAG[1];
	
	GsClearOt(0, 0, &myOT[0]);
	GsClearOt(0, 0, &myOT[1]);
	
}
void PrepDisplay() {
	
	// Get active buffer ID and clear the OT to be processed for the next frame
	myActiveBuff = GsGetActiveBuff();
	GsSetWorkBase((PACKET*)myPacketArea[myActiveBuff]);
	GsClearOt(0, 0, &myOT[myActiveBuff]);
	
}
void Display() {
	
	// Wait for VSync, switch buffers, and draw the new frame.
	VSync(0);
	GsSwapDispBuff();
	GsSortClear(0, 0, 0, &myOT[myActiveBuff]);
	GsDrawOt(&myOT[myActiveBuff]);
	
}
I couldn't fix the other one as I don't use Orion_'s libraries (and using such libraries can be problematic especially with the memory limitations of the PlayStation).
Please don't forget to include my name if you share my work around. Credit where it is due.

Dev. Console: SCPH-7000 with SCPH-7501 ROM, MM3, PAL color fix, Direct AV ports, DB-9 port for Serial I/O, and a Xplorer FX with Caetla 0.35.

DTL-H2000 PC: Dell Optiplex GX110, Windows 98SE & Windows XP, Pentium III 933MHz, 384MB SDRAM, ATI Radeon 7000 VE 64MB, Soundblaster Audigy, 40GB Seagate HDD, Hitachi Lite-on CD-RW Drive, ZIP 250 and 3.5" Floppy.

User avatar
Sirrico
Curious PSXDEV User
Curious PSXDEV User
Posts: 27
Joined: Sep 05, 2014
PlayStation Model: SCPH-1001

Post by Sirrico » October 7th, 2014, 6:54 am

LameGuy64 wrote:Here's a good example on how to display TIMs properly:
That's some mighty fine code, thanks! What program are you using to save TIMs as C files? I'm using GIMP, myself, and it doesn't seem to put it in the right format, maybe an incorrect header or something.

I was also able to get Orion_'s code to work as well.
-Sirrico

User avatar
Shadow
Verified
Admin / PSXDEV
Admin / PSXDEV
Posts: 2670
Joined: Dec 31, 2012
PlayStation Model: H2000/5502
Discord: Shadow^PSXDEV

Post by Shadow » October 7th, 2014, 3:47 pm

I guess you could use C files, but usually you're supposed to use H files (headers). It depends on the compiler, and as for CCPSX, it optimised for H files.

You don't save the image as a *.C file, you need to save it as a PNG or BMP, place it in the desired location in the Framebuffer (using TIMTOOL) and then convert the TIM file that's outputted to a *.H file using a RAW binary converter program such as BIN2H (convert files to C or ASM hexadecimal code). That binary file then gets parsed in during compilation into the final PS-EXE where in your code, it's pointed to. You can just use a TIM directly, but in order to do so you need to write code to load it from the CD-ROM into a RAM address. The best thing you can do for a PSX game is to compress a TIM (they compress extremely well), decompress it into a malloced chunk of RAM and then free it after. The R3000 should be able to decompress it fairly quick, but I wouldn't do it for 16 or even 32 BIT based images :roll:
Development Console: SCPH-5502 with 8MB RAM, MM3 Modchip, PAL 60 Colour Modification (for NTSC), PSIO Switch Board, DB-9 breakout headers for both RGB and Serial output and an Xplorer with CAETLA 0.34.

PlayStation Development PC: Windows 98 SE, Pentium 3 at 400MHz, 128MB SDRAM, DTL-H2000, DTL-H2010, DTL-H201A, DTL-S2020 (with 4GB SCSI-2 HDD), 21" Sony G420, CD-R burner, 3.25" and 5.25" Floppy Diskette Drives, ZIP 100 Diskette Drive and an IBM Model M keyboard.

Post Reply

Who is online

Users browsing this forum: No registered users and 2 guests