Recent Posts

Pages: [1] 2 3 ... 10
1
General Jo Engine Help / Broken Demo
« Last post by SaturnTeam on June 18, 2018, 08:23:52 pm »
I updated the engine and noticed that the Shooter Demo is broken. It has to do with the introduction of Storyboard code into it. I was able to transplant the old demo into the new engine, but there are errors when compiling. Specifically, the errors are related to lines 9 and 10 in ship.h and lines 58 and 139 in main.c. Apparently, it has to do with definitions in the engine's type.h file. By commenting out lines 9 and 10, I can get the demo to run.
2
General Jo Engine Help / Re: Usage of GFS_NwFread (you probably expected this...)
« Last post by ponut64 on June 17, 2018, 05:09:22 pm »
Here, this is the loading structure.
*30 FPS, by the way. My calculation of roughly 240 bytes per frame must have been at 60 FPS. It is important to know this is an extrapolation based on CDDA quality. ... it's probably wrong.

Code: [Select]
void	load_test()
{

//Hesitant to add code here...
if(accelState > 0){
load_this++;
}
 jo_printf(20, 0, "(%i)", load_this);
if(load_this < 1){
//This mesh is 216 quads
currentAddress = ztFileRequest("BIN",(Sint8*)"BPONY.ZTP", currentAddress, &entities[0], 0);
}
//Left turn steps
if(load_this > 60 && load_this < 119){
currentAddress = ztFileRequest("LTURN", (Sint8*)"LTURN01.ZTP", currentAddress, &entities[11], 1);
}
if(load_this > 120 && load_this < 179){
currentAddress = ztFileRequest("LTURN", (Sint8*)"LTURN02.ZTP", currentAddress, &entities[12], 2);
}
if(load_this > 180 && load_this < 239){
currentAddress = ztFileRequest("LTURN", (Sint8*)"LTURN03.ZTP", currentAddress, &entities[13], 3);
}
if(load_this > 240 && load_this < 379){
currentAddress = ztFileRequest("LTURN", (Sint8*)"LTURN04.ZTP", currentAddress, &entities[14], 4);
}
if(load_this > 380 && load_this < 479){
currentAddress = ztFileRequest("LTURN", (Sint8*)"LTURN05.ZTP", currentAddress, &entities[15], 5);
}
if(load_this > 540 && load_this < 599){
currentAddress = ztFileRequest("LTURN", (Sint8*)"LTURN06.ZTP", currentAddress, &entities[16], 6);
}
if(load_this > 660 && load_this < 729){
currentAddress = ztFileRequest("LTURN", (Sint8*)"LTURN07.ZTP", currentAddress, &entities[17], 7);
}
if(load_this > 780 && load_this < 839){
currentAddress = ztFileRequest("LTURN", (Sint8*)"LTURN08.ZTP", currentAddress, &entities[18], 8);
}
if(load_this > 900 && load_this < 959){
currentAddress = ztFileRequest("LTURN", (Sint8*)"LTURN09.ZTP", currentAddress, &entities[19], 9);
}
if(load_this > 999 && load_this < 1100){
currentAddress = ztFileRequest("LTURN", (Sint8*)"LTURN10.ZTP", currentAddress, &entities[20], 10);
}

}

Very simple. In a given range of numbers, the model loading code is active. I don't really need to do it that way, but I do.

This is the file request function.

Code: [Select]
void * ztFileRequest(char * dirname, Sint8 * name, void * startAddress, entity_t * model, Uint16 rqNum)
{
FilRqNm = rqNum;
if(requests[FilRqNm].file_done == true){
process_requested = false;
jo_printf(0, 0, "(-ended-)");
goto SKIP;
}
if(process_requested == false){
rd_frames = 0;
curRdFrame = 0;
ztCDsetRoot();
}
ztCDsetDir(dirname);
requests[FilRqNm].filename = GFS_NameToId(name);
requests[FilRqNm].dstAddress = startAddress;
requests[FilRqNm].tmodel = model;
process_requested = true;
jo_printf(0, 0, "(started)");
SKIP:
return requests[FilRqNm].dstAddress;
}

And this explains why the model is sometimes not visible. This was my animation system for the old ZTPs using a unique mesh for each frame.

Code: [Select]
void	anim_entity_state(Uint16 start, Uint8 length, Sint8 sync, Bool loop, Bool reverse)
{
if(loop == true && reverse == false){
if(anim_target < length && sync > 0){
anim_target++;
}
if(anim_target == length){
anim_target -= length;
}
display_ztp(&entities[start+anim_target]);
}

if(loop == true && reverse == true){
if(anim_target < length && sync > 0){
anim_target++;
}
if(anim_target == length){
anim_target -= length;
}
display_ztp(&entities[start+length-anim_target]);
}

if(loop == false && reverse == false){
if(anim_target < length && sync > 0){
anim_target++;
}
if(anim_target == length){
display_ztp(&entities[start]);
}
display_ztp(&entities[start+anim_target]);
}

if(loop == false && reverse == true){
if(anim_target < length && sync > 0){
anim_target++;
}
if(anim_target == length){
display_ztp(&entities[start]);
}
display_ztp(&entities[start+length-anim_target]);
}

}

Code: [Select]
		if(accelState == 0 && rotState[X] == 0){
anim_target = 0;
display_ztp(&entities[0]);
}
if(accelState > 0){
anim_entity_state(21, 19, evenFrame, true, false);
} else if(accelState < 0){
anim_entity_state(21, 19, evenFrame, true, true);
} else if(rotState[X] < 0){
anim_entity_state(1, 9, evenFrame, true, false);
} else if(rotState[X] > 0){
anim_entity_state(11, 9, evenFrame, true, false);
}

I'm wordy but I like to leave little up to question.
Once the mesh is finished loading, no more loading is done with it.
It is important to note that if you wanted to use that entity slot for a different mesh, you can just load to that mesh address (&entitites[number]) again and it will be replaced.

And finally, the core loading loop. Note that there is a void function parameter in the function This is where you put the game loop.

Code: [Select]
 //Experimental
void ztFileServer(void(*sysframe)(void))
{
if(process_requested != true){
goto SKIP;
}
    void * workAddress;
workAddress = requests[FilRqNm].dstAddress;

//Data of read structure
    Sint32 fid = (Sint32)requests[FilRqNm].filename;
GfsHn gfsx;
Sint32 nzect;
Sint32 fsize;
//Accepted rate of reading per frame (33 ms)
//Note to self: If your data is already byte size information, do not annotate with sizeOf. Thanks.
Sint32 rt_step = 512;
Uint8 rt_sector = 1;
//Data of read-in-process
Sint32 stat;
Sint32 rdsize;
//Model Data is the destination of data in the work area.
modelData_t bufModelX;
//Destination address?
void * ptr2 = &bufModelX;
//Loading follows
gfsx = GFS_Open(fid);
//Get sectors
//HEY! SBL DOCUMENTATION IS WRONG! THIRD ITEM nzect IS GFS SECTOR COUNT. SECOND ITEM IS CD SECTOR SIZE.
GFS_GetFileSize(gfsx, NULL, &nzect, NULL);
GFS_GetFileInfo(gfsx, NULL, NULL, &fsize, NULL);
//How many frames are we reading?
if(rt_step < fsize){
rd_frames = (fsize + (rt_step - 1))/(rt_step);
}
//Skip the loop if it is finished
if(curRdFrame >= rd_frames){
if(requests[FilRqNm].file_done != true){
  // Copy gfsx payload to modelData_t bufmodel
slDMACopy(workAddress, ptr2, sizeof(modelData_t));
slDMAWait();
//ADDED
    requests[FilRqNm].tmodel->pos[X]=bufModelX.Origin[X];
requests[FilRqNm].tmodel->pos[Y]=bufModelX.Origin[Y];
requests[FilRqNm].tmodel->pos[Z]=bufModelX.Origin[Z];
    requests[FilRqNm].tmodel->length[X]=bufModelX.Length[X];
requests[FilRqNm].tmodel->length[Y]=bufModelX.Length[Y];
requests[FilRqNm].tmodel->length[Z]=bufModelX.Length[Z];
    requests[FilRqNm].tmodel->nbMeshes=bufModelX.TOTAL_MESH;

Uint16 first_texture = loadTextures(workAddress, &bufModelX);
Sint32 bytesOff = bufModelX.TEXT_SIZE+(sizeof(modelData_t));
workAddress = (workAddress + bytesOff);
workAddress = loadPDATA((workAddress), requests[FilRqNm].tmodel, &bufModelX);
workAddress = (workAddress + (bufModelX.PDATA_SIZE + sizeof(modelData_t)));
setTextures(first_texture, requests[FilRqNm].tmodel, bufModelX.TOTAL_MESH, true);
requests[FilRqNm].dstAddress = workAddress;
}
requests[FilRqNm].file_done = true;
jo_printf(0, 9, "(skipped loop)");
goto SKIP;
}

jo_printf(0, 8, "(%i)", nzect);
jo_printf(10, 8, "(sectors)");
jo_printf(0, 9, "(%i)", fsize);
jo_printf(10, 9, "(file size)");

//Read from CD to CD buffer
//CONCEPT: NwFread specifies only a fetch from CD buffer. If no such read has been specified, it will carry it out itself.
//However, if you specify a read from CD, repeats of the fetch command will only fetch from the CD buffer and will not give errors for data remainders.
GFS_SetReadPara(gfsx, rt_step);
GFS_SetTransPara(gfsx, rt_sector);
//TIP: GFS_Nw_CdRead cannot be checked for completion. Sorry!
GFS_NwCdRead(gfsx, fsize);
for( ; curRdFrame < rd_frames ; curRdFrame++){
GFS_NwFread(gfsx, rt_sector, (Uint32*)(workAddress +(curRdFrame * rt_step)), rt_step);
do{
GFS_NwExecOne(gfsx);
GFS_NwGetStat(gfsx, &stat, &rdsize);
jo_printf(0, 11, "(%i)", rdsize);
jo_printf(10, 11, "(fetched filesize)");
}while(stat != GFS_SVR_COMPLETED && rdsize < rt_step);
//Remainder of file in comparison to rt_step [%] is handled, because we do not want to repeat the loop for the end of the data anyway. Do you understand?
//Technically, a file exactly divisible by rt_step will read-loop indefinitely. Watch out for that.
sysframe();
slSynch();
jo_printf(0, 15, "(%i)", curRdFrame);
jo_printf(4, 15, "(%i)", rd_frames);
jo_printf(0, 13, "(%i)", bufModelX.TOTAL_MESH);
jo_printf(3, 13, "(%i)", bufModelX.PDATA_SIZE);
jo_printf(10, 13, "(%i)", requests[FilRqNm].tmodel->nbMeshes);
}

SKIP:
//It is important to ensure GFS is closed at all times outside the read loop.
GFS_Close(gfsx);

sysframe();
}
3
General Jo Engine Help / Re: Usage of GFS_NwFread (you probably expected this...)
« Last post by XL2 on June 17, 2018, 05:03:33 pm »
Ok, so you reload the model each frame?
Yeah, for animation you can just use the newer version.
It can be improved and in fact I already made improvements for Sonic Z-Treme.
I might revisit the tool and demo in the near future.
4
General Jo Engine Help / Re: Usage of GFS_NwFread (you probably expected this...)
« Last post by ponut64 on June 17, 2018, 05:00:30 pm »
It's just streaming / "loading" an entire .ZTP / binary model file. An outdated .ZTP file from the 0.05 model loading demo you put up.

Yeah I know I am not very clear on it. It disappears because my code moves to displaying a different model (that is not loaded). If I loaded everything, nothing would disappear.
These models are about 12KB. All it is streaming is that model. There's no way that can be done for animations using entirely new models, it would just take too long.

Perhaps when I get to your new model loading demo, I could figure something else out but you have the memory usage trimmed down so well. I imagine using this for streaming meshes between levels that are built from smaller meshes. Let's say if you had two very similar levels, but with a few unique assets that can't fit into memory for one or the other, you could load that through a "transitional area" wherein neither of those meshes need to be displayed. An important thing about this is it does not error if the mesh is loaded whilst the game is "trying" to display it.
5
General Jo Engine Help / Re: Usage of GFS_NwFread (you probably expected this...)
« Last post by XL2 on June 17, 2018, 03:55:20 pm »
How big is your model?
Also, I'm not 100% sure what it does.
Is it streaming animation data?
If so, why does it disappear?
I think it would be more interesting to test it with backgrounds (or palettes), you could have animated backgrounds.
Is it running at 60 fps?
So you can fetch 512 bytes per frame at 60 fps, I guess streaming visibility data would work fine.
But, first things first : getting midi music to work!
6
General Jo Engine Help / Re: Usage of GFS_NwFread (you probably expected this...)
« Last post by ponut64 on June 17, 2018, 02:51:08 pm »
This thread has reached the conclusion -

https://youtu.be/bkaD8isNVz0

As noted in my comment, any remaining slowdown is due to directory change functions. I did enough debugging to know it isn't because of NwCdRead.
NwCdRead really just makes things faster, no reason not to use it (so far, anyway).
7
Project announcement / Re: Sonic Z-Treme
« Last post by XL2 on June 16, 2018, 05:47:57 pm »
Here is a new update on my new engine (WIP!) on real hardware.
I didn't implement back the physics/controls, so don't pay attention to that.
What's new : compressed per-vertex animation with interpolation (a bit like Crash Bandicoot and Quake 2), VDP2 transparency fade in, per-vertex realtime lightning (on Sonic only), audio using tones, splitscreen (not seen here), octree instead of an uniform grid for culling/collision, paletted/CLUT sprites, LOD model autogenerated for distant quads and probably many other things I forgot to mention.
Splitscreen works at a stable 30 fps from what I tested.

It's not optimized yet, so there is room for lot of improvement.

https://www.youtube.com/watch?v=J51wKAvJL7g
8
General Jo Engine Help / Re: Usage of GFS_NwFread (you probably expected this...)
« Last post by ponut64 on June 16, 2018, 05:34:17 pm »
Hold up.

With a loop like this...

Quote
   PUSHF:
   GFS_NwFread(gfsx, rt_sector, (Uint32*)(currentAddress +(curRdFrame * rt_step)), rt_step);
do{
      GFS_NwExecOne(gfsx);
      GFS_NwGetStat(gfsx, &stat, &rdsize);
      jo_printf(0, 11, "(%i)", rdsize);
      jo_printf(10, 11, "(fetched filesize)");
}while(stat != GFS_SVR_COMPLETED && rdsize < rt_step);
   //Remainder of file in comparison to rt_step [%] is handled, because we do not want to repeat the loop for the end of the data anyway. Do you understand?
   //Technically, a file exactly divisible by rt_step will read-loop indefinitely. Watch out for that.
   if(rdsize == rt_step){
//   GFS_NwStop(gfsx);
   curRdFrame = curRdFrame + 1;
//      goto PUSHF;
   }

When the program gets to NwFread the second time, it does not seem to register it differently than the first time.
I make this conclusion separate from NwExecOne (or Server) since the loop functions as intended if the goto PUSHF line is enabled, which involves majorly GFS_NwFread.
I also am able to make this conclusion because the program's rdsize doesn't properly get to the last sector size of the file.

I guess I am back to square one. GFS_NwFread is not behaving as expected. If I put it in a loop, the entire program halts whilst inside that loop, even if the function itself is a system callback.
If it is a part of the main program loop, it doesn't behave as expected; it doesn't seem to be picked up more than once.

I always thought at first this is related to GFS_Open and GFS_Close. There's a few problems with that...
If one or the other is missing from each frame, the program will freeze.

So let's look at the NwExecOne loop again. GFS_SVR_COMPLETED can't be causing a problem since this being a do...while loop, it will always hit the GFS_NwGetStat function first, which will set GFS_SVR_BUSY. rdsize being less than RT step.. again, one would think this couldn't cause any problems since rdsize is referenced and should be filled at zero in GFS_NwGetStat. And upon closer examination, rdsize becomes NULL after an incantation of the loop anyway.

So I would say I have identified the problem. It is between GFS_NwFread and GFS_NwExecOne/Server. My guess is NwFread being hit on the second frame is not being registered as a unique command so NwExecOne does not change its status. I can't skip to it on the next frame since GFS_Open needs to happen.

Now, of course, if I put the rest of the game code inside this loop (before goto PUSHF), it would work. I understand what SBL documentation means now. If I go by the documentation, this actually shouldn't work; they never set up an example of the loop being allowed to exit. My assumption is there is no other way for GFS_NwFread to work properly except between a single GFS Open and GFS Close. GFS cannot be closed or opened between incantations of it on the same file, which makes no logical sense but that really appears to be how it works.

My question becomes, is there any workaround...? Well I have been exploring a lot of potential ones, no dice so far...
Not even a unique GfsHn for each frame read works.
Jo engine "async" reading really does not appear to be async reading either. The program is still going to get "stuck" in the file access loop. In an emulator, it's very zippy. On real hardware however, I can't see it behaving as hoped.

I will get back at this tomorrow. Or maybe.. you know some time soon. Restructuring jo_main should be entirely unncessary for this. The loading function just needs to be the preeminent code of a game_frame or game_loop function, like I have in my code. Not that big of a deal now that I think about it.

/e: Just tested it real quick. It DOES work as intended if I put game_frame inside the reading loop. It goes mental (as in frame-rate goes REALLY high because I am sloppily calling the frame code twice), but it works :)
9
Share your code / Re: Model converter (.ZTP) -0.1 - WIP
« Last post by XL2 on June 15, 2018, 04:26:14 am »
Here is how it looks like in action :

https://youtu.be/9q5zSLxVBAI
10
General Jo Engine Help / Re: Usage of GFS_NwFread (you probably expected this...)
« Last post by ponut64 on June 14, 2018, 12:50:21 pm »
Okay, that's what I thought I should do at first since that's how Jo engine does it.

Now I am at a similar problem to before: the read data is getting corrupted if I interrupt the NwFread loop (and thus run the program without interruption).
I do happen to know that the Saturn should be able to manage about 240 bytes fetched each frame, and if I use that for rt_step, it will just crash (im assuming because the data is corrupted).
I don't know why the data is getting corrupted...

Code: [Select]

typedef struct
{
bool file_done;
void * dstAddress;
entity_t * tmodel;
Sint8 * filename;
} request;

Uint32 rd_frames = 0;
Uint32 curRdFrame = 0;

Uint16 FilRqNm;

bool TimeToSet = false;
bool process_requested;

request requests[4];

void ztFileRequest(Sint8 * name, void * startAddress, entity_t * model, Uint16 rqNum)
{
if(requests[rqNum].file_done == true){
process_requested = false;
goto SKIP;
}
requests[rqNum].filename = name;
requests[rqNum].dstAddress = startAddress;
requests[rqNum].tmodel = model;
FilRqNm = rqNum;
process_requested = true;
jo_printf(0, 0, "(started)");
SKIP:
if(process_requested == false){
jo_printf(0, 0, "(-ended-)");
}
}

 //Experimental
void ztFileServer(void)
{
if(process_requested != true){
goto SKIP;
}
    void * currentAddress;
currentAddress = requests[FilRqNm].dstAddress;

//Data of read structure
    Sint32 fid = GFS_NameToId((Sint8*)requests[FilRqNm].filename);
GfsHn gfsx;
Sint32 nsct;
Sint32 fsize;
//Accepted rate of reading per frame (33 ms)
//Note to self: If your data is already byte size information, do not annotate with sizeOf. Thanks.
Sint32 rt_step = 2048;
Uint8 rt_sector = 1;
//Data of read-in-process
Sint32 stat;
Sint32 rdsize;
bool gfs_Active = false;
//Model Data is the destination of data in the work area.
modelData_t bufModelX;
//Destination address?
    void * ptr2 = &bufModelX;

if(gfsx == NULL && requests[FilRqNm].file_done != true){
//Loading follows
gfsx = GFS_Open(fid);
gfs_Active = true;
//Get sectors
//HEY! SBL DOCUMENTATION IS WRONG! THIRD ITEM NSCT IS GFS SECTOR COUNT. SECOND ITEM IS CD SECTOR SIZE.
GFS_GetFileSize(gfsx, NULL, &nsct, NULL);
GFS_GetFileInfo(gfsx, NULL, NULL, &fsize, NULL);
//How many frames are we reading?
if(rt_step < fsize){
rd_frames = (fsize + (rt_step - 1))/(rt_step);
}
}
//Skip the loop if it is finished
if(curRdFrame >= rd_frames){
if(requests[FilRqNm].file_done != true){
  // Copy gfsx payload to modelData_t bufmodel
memcpy_l((Sint32*)ptr2, (Sint32*)(currentAddress), sizeof(modelData_t));
//ADDED
    requests[FilRqNm].tmodel->pos[X]=bufModelX.Origin[X]; requests[FilRqNm].tmodel->pos[Y]=bufModelX.Origin[Y]; requests[FilRqNm].tmodel->pos[Z]=bufModelX.Origin[Z];
    requests[FilRqNm].tmodel->length[X]=bufModelX.Length[X]; requests[FilRqNm].tmodel->length[Y]=bufModelX.Length[Y]; requests[FilRqNm].tmodel->length[Z]=bufModelX.Length[Z];
    requests[FilRqNm].tmodel->nbMeshes=bufModelX.TOTAL_MESH;

Uint16 first_texture = loadTextures(currentAddress, &bufModelX);
Sint32 bytesOff = bufModelX.TEXT_SIZE+(sizeof(modelData_t));
currentAddress = (void*)(currentAddress + bytesOff);
currentAddress = loadPDATA((void*)(currentAddress), requests[FilRqNm].tmodel, &bufModelX);
currentAddress = (void*)(currentAddress + (bufModelX.PDATA_SIZE + sizeof(modelData_t)));
setTextures(first_texture, requests[FilRqNm].tmodel, bufModelX.TOTAL_MESH, false);
}
requests[FilRqNm].file_done = true;
jo_printf(0, 9, "(skipped loop)");
goto SKIP;
}

jo_printf(0, 8, "(%i)", nsct);
jo_printf(10, 8, "(sectors)");
jo_printf(0, 9, "(%i)", fsize);
jo_printf(10, 9, "(file size)");

//Read from CD to CD buffer
//CONCEPT: NwFread specifies only a fetch from CD buffer. If no such read has been specified, it will carry it out itself.
//However, if you specify a read from CD, repeats of the fetch command will only fetch from the CD buffer and will not give errors for data remainders.
//TIP: Since TMODE_CPU is the SH1 of the CD system, there is no reason to use anything else, as that CPU is otherwise entirely idle.
GFS_SetTmode(gfsx, GFS_TMODE_CPU);
//TIP: Setting GFS_GMODE_RESIDENT is a guaranteed crash.
GFS_SetGmode(gfsx, GFS_GMODE_ERASE);
GFS_SetReadPara(gfsx, rt_step);
GFS_SetTransPara(gfsx, rt_sector);
//TIP: GFS_Nw_CdRead cannot be checked for completion. Sorry!
GFS_NwCdRead(gfsx, fsize);
PUSHF:
GFS_NwFread(gfsx, rt_sector, (Uint32*)(currentAddress +(curRdFrame * rt_step)), rt_step);
do{
GFS_NwExecOne(gfsx);
GFS_NwGetStat(gfsx, &stat, &rdsize);
jo_printf(0, 11, "(%i)", rdsize);
jo_printf(10, 11, "(fetched filesize)");
}while(stat != GFS_SVR_COMPLETED && rdsize < rt_step);
//Remainder of file in comparison to rt_step [%] is handled, because we do not want to repeat the loop for the end of the data anyway. Do you understand?
//Technically, a file exactly divisible by rt_step will read-loop indefinitely. Watch out for that.
if(rdsize == rt_step){
// GFS_NwStop(gfsx);
curRdFrame = curRdFrame + 1;
// goto PUSHF;
}

if(gfs_Active == true){
GFS_Close(gfsx);
gfs_Active = false;
}

SKIP:

jo_printf(0, 15, "(%i)", curRdFrame);
jo_printf(4, 15, "(%i)", rd_frames);
jo_printf(0, 13, "(%i)", bufModelX.TOTAL_MESH);
jo_printf(3, 13, "(%i)", bufModelX.PDATA_SIZE);
jo_printf(10, 13, "(%i)", requests[FilRqNm].tmodel->nbMeshes);
}
Pages: [1] 2 3 ... 10
SMF spam blocked by CleanTalk