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.
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.
Why are my TIMs Displayed Garbled?
Why are my TIMs Displayed Garbled?
-Sirrico
-
LameGuy64 Verified
- 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:
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.
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.
Here is one set of code I'm using, coming from Orion_'s libraries.LameGuy64 wrote:Could you show some of your code? Mainly the image drawing part.
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);
}
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;
}
-Sirrico
-
Shadow Verified
- Admin / PSXDEV
- Posts: 2670
- Joined: Dec 31, 2012
- PlayStation Model: H2000/5502
- Discord: Shadow^PSXDEV
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.
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.
-
LameGuy64 Verified
- 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:
Here's a good example on how to display TIMs properly:
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).
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]);
}
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.
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.
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.LameGuy64 wrote:Here's a good example on how to display TIMs properly:
I was also able to get Orion_'s code to work as well.
-Sirrico
-
Shadow Verified
- Admin / PSXDEV
- Posts: 2670
- Joined: Dec 31, 2012
- PlayStation Model: H2000/5502
- Discord: Shadow^PSXDEV
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
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
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.
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.
Who is online
Users browsing this forum: No registered users and 2 guests