Virtua Skimmer WIP

Hey thanks for sharing my project. I'm learning C as I go but familiar with messing around in Unity. If anyone's trying early build its actually X to start/reset the game. Id say the gameplay is only %30 done, haven't started learning collision yet and its very hacky how I make him Surf + Gravity/Jumping without it. I was thinking a balance bar too but think that just confusing when moving around.

Should get the player textured map soon just having trouble figuring it out with the current tools for Jo engine. Also I added a NiGHTS style grading dice when your given your score and have a sprite animation of it spinning I'm going to add in, but right now its replacing every single TGA and not sure why.

I made a github and will update the iso there with my latest version. Im still new to coding so its extremely confusing because I get things to work after messing around a lot and don't want to touch them. But any help is welcome
https://github.com/VirtuaSkimmer/Virtua-Skimmer-Sega-Saturn-

This is roughly how the player will look. I think this is 64x64 but maybe 128x128 and need to be reduced. And need to get rid of more triangles. Its head is actually Sonic from Sonic R turned facing down and bending his hair
aQIHQ7t.png
 
Hey thanks for sharing my project. I'm learning C as I go but familiar with messing around in Unity. If anyone's trying early build its actually X to start/reset the game. Id say the gameplay is only %30 done, haven't started learning collision yet and its very hacky how I make him Surf + Gravity/Jumping without it. I was thinking a balance bar too but think that just confusing when moving around.

Should get the player textured map soon just having trouble figuring it out with the current tools for Jo engine. Also I added a NiGHTS style grading dice when your given your score and have a sprite animation of it spinning I'm going to add in, but right now its replacing every single TGA and not sure why.

I made a github and will update the iso there with my latest version. Im still new to coding so its extremely confusing because I get things to work after messing around a lot and don't want to touch them. But any help is welcome
https://github.com/VirtuaSkimmer/Virtua-Skimmer-Sega-Saturn-

This is roughly how the player will look. I think this is 64x64 but maybe 128x128 and need to be reduced. And need to get rid of more triangles. Its head is actually Sonic from Sonic R turned facing down and bending his hair
aQIHQ7t.png

I got the model converter I promised you working at pretty much 70-80%, but I didn't have time to test it nor write a demo.
It seems to be ok, but since I didn't test it, I can't guarantee it.
IT'S ALSO REALLY QUICKLY CODED! I made it in like 2 hours and only did small tests, so it might not work properly.

I'm sharing it with you now, but I guess without a demo it might be a bit hard to use.
The file starts with a header portion of 36 bytes, then writes the texture data, then the polygon data.
I added the header and the code to know how I write to the binary file so that you know what to load.
You can use the image converter I posted elsewhere to know how to load the paletted textures.
The program also outputs a text file including all the PDATA, but no textures, so you can also play with that.
Please note that it only supports textures, so if you want to load a flat shaded polygon, you can use something such as a 8x1 texture with 1 color. I guess it would be a bit slower than using a real flat shaded quad, but I don't think I will have time to modify my code to allow it for now.

Code:
//HEADER
typedef struct
{
    unsigned short  TOTAL_MESH; //total amount of PDATA
    short            TOT_TEXT;  //total amount of textures
    unsigned int    TEXT_SIZE;  //to quickly load from disk, that's the size of the textures in bytes
    unsigned int    PDATA_SIZE; //to quickly load from disk, total size of pdata in bytes
    vector3 Origin; //Origin point used to "center" the culling/collision data and your model's position. Should be 0,0,0 unless you have an offset
    vector3 Length;  //Lenght along the x,y z axis. Together with the origin, that gives you the bounding box for quick broad collision testing
} _MOD_DATA;

//TEXTURE DATA
unsigned short convert_to_4bpp(unsigned short a, unsigned short b){    return (((a&0xffff)<<4) | (b));}

void WRITES_TEXTURES(ofstream * binFile)
{
    cout << "Writing the textures to binary file...\n\n";
    unsigned short buf16;

    for (short i=0; i<MODEL_DATA.TOT_TEXT; i++)
    {
        unsigned short w = swap_endian_ushort(pimg[i].width);  //pimg == paletted image
        unsigned short h = swap_endian_ushort(pimg[i].height);
        binFile->write((char*)&w, sizeof(unsigned short));
        binFile->write((char*)&h, sizeof(unsigned short));
        binFile->write((char*)&MODEL_DATA.TEXT_SIZE, sizeof(int));

        for (short ii=0; ii< (pimg[i].width * pimg[i].height);)
        {
            unsigned char buf = 0;
            buf =  (unsigned char) convert_to_4bpp(pimg[i].palette_id[ii], pimg[i].palette_id[ii+1]);
            binFile->write((char*)&buf, sizeof(unsigned char));
            ii+=2;
        }

    }
    for (short i=0; i<MODEL_DATA.TOT_TEXT; i++)
    {
        for (unsigned int ii=0; ii<16; ii++)
        {
           buf16 = swap_endian_ushort(pimg[i].palette[ii]);
           binFile->write((char*)(&buf16), sizeof(unsigned short));
        }
    }
}


//PDATA
/*****
This writes all the PDATA in a sequential order
*****/
void WRITE_PDATA(ofstream * file)
{
    unsigned short bUint16;
    unsigned int bUint32;
    int bSint32;

    for (unsigned int i=0; i<MODEL_DATA.TOTAL_MESH; i++)
    {
        //PDATA, including buffers for the pointers
        bUint32 = 0;
            file->write((char*)&bUint32, sizeof(unsigned int));
        bUint32 = swap_endian_uint(mesh_array[i].nbPoint);
            file->write((char*)&bUint32, sizeof(unsigned int));
        bUint32 = 0;
            file->write((char*)&bUint32, sizeof(unsigned int));
        bUint32 = swap_endian_uint(mesh_array[i].nbPolygon);
            file->write((char*)&bUint32, sizeof(unsigned int));
        bUint32 = 0;
            file->write((char*)&bUint32, sizeof(unsigned int));

        //POINT (vertices), 12 bytes each
        for (unsigned int ii=0; ii<mesh_array[i].nbPoint; ii++)
        {
            for (unsigned int j=0; j<3; j++)
            {
                bSint32 = swap_endian_sint(mesh_array[i].pntbl[ii][j]);
                file->write((char*)&bSint32, sizeof(int));
            }
        }
        //POLYGON, 12 bytes for normals and 8 bytes for vertices
        for (unsigned int ii=0; ii<mesh_array[i].nbPolygon; ii++)
        {
            //Normals
            for (unsigned int j=0; j<3; j++)
            {
                bSint32 = swap_endian_sint(mesh_array[i].pltbl[ii].norm[j]);
                file->write((char*)&bSint32, sizeof(int));
            }
            //Vertices
            for (unsigned int j=0; j<4; j++)
            {
                bUint16 = swap_endian_ushort(mesh_array[i].pltbl[ii].Vertices[j]);
                file->write((char*)&bUint16, sizeof(unsigned short));
            }
        }
        //ATTRIBUTES, 12 bytes each
        for (unsigned int ii=0; ii<mesh_array[i].nbPolygon; ii++)
        {
            file->write((char*)&TextureAttributeArray[mesh_array[i].attbl[ii].texno].flag, sizeof(unsigned char));
            file->write((char*)&TextureAttributeArray[mesh_array[i].attbl[ii].texno].sorting, sizeof(unsigned char));
            bUint16=swap_endian_ushort(TextureAttributeArray[mesh_array[i].attbl[ii].texno].texno);
                file->write((char*)&bUint16, sizeof(unsigned short));
            bUint16=swap_endian_ushort(TextureAttributeArray[mesh_array[i].attbl[ii].texno].atrb);
                file->write((char*)&bUint16, sizeof(unsigned short));
            bUint16=swap_endian_ushort(TextureAttributeArray[mesh_array[i].attbl[ii].texno].colno);
                file->write((char*)&bUint16, sizeof(unsigned short));
            bUint16=swap_endian_ushort(TextureAttributeArray[mesh_array[i].attbl[ii].texno].gstb);
                file->write((char*)&bUint16, sizeof(unsigned short));
            bUint16=swap_endian_ushort(TextureAttributeArray[mesh_array[i].attbl[ii].texno].dir);
                file->write((char*)&bUint16, sizeof(unsigned short));
        }
    }
}

So, you can use something such as GFS_LOAD starting with 36 bytes to know what the file contains, then read the textures (just put it in low-work RAM), DMA everything to VRAM where it belongs and then overwrite the data in low-work RAM with the PDATA (since the textures are transfered, you don't need them in work-RAM, so just overwrite them).
For the textures, make sure you do it per face instead of using texture coordinates.
The converter I wrote only supports 16 colors per texture, but save your TGA files as 32 bits files. The alpha channel is treated as transparent.
I'll see what I can do for the demo, but I'm a bit busy with work so I might not have time. Let me know if you encounter bugs!
I added the title map from Quake as a demo, but this tool isn't meant for maps since it doesn't do map partition nor does it hold data for culling!

Edit : Removed the file, use the one on the newer post instead.
 
Last edited:
Thanks for the info, I'm still a bit lost how to implement it but am not giving up. I managed to update my game with flat shaded polys instead. Actually doesn't look to bad but I still want to texture map the face and eyes properly similar to Virtua Fighter.
Iso available here
https://github.com/VirtuaSkimmer/Virtua-Skimmer-Sega-Saturn-/releases
34zv.png


Seems I need to start using your way and optimizing my sloppy code because seems I'm out of transformation power if I try to scale or add a new mesh it says out of dynamic memory until I reduce something.
 
Last edited:
Thanks for the info, I'm still a bit lost how to implement it but am not giving up. I managed to update my game with flat shaded polys instead. Actually doesn't look to bad but I still want to texture map the face and eyes properly similar to Virtua Fighter.
Iso available here
https://github.com/VirtuaSkimmer/Virtua-Skimmer-Sega-Saturn-/releases
34zv.png


Seems I need to start using your way and optimizing my sloppy code because seems I'm out of transformation power if I try to scale or add a new mesh it says out of dynamic memory until I reduce something.
Looks great with the flat shaded quads!
For memory issues, you really need to put some meshes in low work RAM and avoid keeping textures in main RAM after transfering them to VRAM.
 
Looks great, but I'm not making much sense of the game play, or what those objects in the water are supposed to be.

Do you have an example of what you're looking to do?
 
Its a tony hawk style score attack you get one wave for a score and grade. Im gonna make a link system and better score/grading. Professional Skimboarding pretty niche so it probably seems abstract. You do whats called a wrap on a wave like this.


Many times you end the wave ducking under the barrel when it hits the beach in real life.


The object in the water is just a breaking wave. The white stuff is supposed the by water splashing and make the barrel side clearer as not open. Still hard to make out though.

Here's me doing a wrap on hardware showing my ducking mechanic
. I do 3 tricks out to the wave - 1 backflip on the wave - then duck into the barrel at the end. Usually in skimboarding you do a wrap motion like this known as Frontside or Backside.
3569.png
 
Its a tony hawk style score attack you get one wave for a score and grade. Im gonna make a link system and better score/grading. Professional Skimboarding pretty niche so it probably seems abstract. You do whats called a wrap on a wave like this.


Many times you end the wave ducking under the barrel when it hits the beach in real life.


The object in the water is just a breaking wave. The white stuff is supposed the by water splashing and make the barrel side clearer as not open. Still hard to make out though.

Here's me doing a wrap on hardware showing my ducking mechanic
. I do 3 tricks out to the wave - 1 backflip on the wave - then duck into the barrel at the end. Usually in skimboarding you do a wrap motion like this known as Frontside or Backside.


I hope it helps a little. I didn't have time to test the model loading function, but the code should be close to work (probably with a couple of bugs). I also helped you a bit with memory management by changing the background loading (it also makes an extra layer available as I put text on NBG3, so you can easily put a bitmap on NBG0). I also put for you a smooth fade in/fade out transition for the background layer.
 

Attachments

  • Virtua Skimmer.zip
    1.9 MB · Views: 135
Thanks for the help. I was just going to ask how you did a loading screen I saw on one of your youtube videos. This was a pretty old build before I finished the player model and really had problems running out of memory. I realize now its because I have dual plane on all the now separated body parts(hair,face,body,legs) but on single plane they are missing quads for some reason. Maybe will be fixed when I use your ZTE convertor.

Ill throw this code into my latest build and update my github. I'm so low on memory now Ill have to single plane with glitches for now to implement but hopefully saves enough memory to switch after for now.

One thing right now though is the wave 3d model is missing and the 3dfoam around it is missing its texture and red. But those seem like the only 2 glitches. So your code so far is loading NGB0 and Beach.ZTI from CD right? Its not doing anything with the models yet? The wave mesh isn't in its own function maybe thats messing it up with the new loading.

Edit: the display_workingwave_mesh(); at line 205 is commented out is why the wave doesnt display. I noticed the model.h wasnt included as well that has that. When I uncomment them it just gives me a blue screen. The wave probably is just to sloppy a model?
 
Last edited:
Thanks for the help. I was just going to ask how you did a loading screen I saw on one of your youtube videos. This was a pretty old build before I finished the player model and really had problems running out of memory. I realize now its because I have dual plane on all the now separated body parts(hair,face,body,legs) but on single plane they are missing quads for some reason. Maybe will be fixed when I use your ZTE convertor.

Ill throw this code into my latest build and update my github. I'm so low on memory now Ill have to single plane with glitches for now to implement but hopefully saves enough memory to switch after for now.

One thing right now though is the wave 3d model is missing and the 3dfoam around it is missing its texture and red. But those seem like the only 2 glitches. So your code so far is loading NGB0 and Beach.ZTI from CD right? Its not doing anything with the models yet? The wave mesh isn't in its own function maybe thats messing it up with the new loading.

Edit: the display_workingwave_mesh(); at line 205 is commented out is why the wave doesnt display. I noticed the model.h wasnt included as well that has that. When I uncomment them it just gives me a blue screen. The wave probably is just to sloppy a model?
I removed those because they take too much memory and it was causing issues.
You could also decrease the memory for malloc (256 K should be more than enough) as you won't need as much since you won't be loading the background in highwork RAM anymore.
Just changing that in your makefile should fix your issues.
Your issue with single plane is that your normals are inverted.
Make sure your model is OK in Blender (press n while in Edit mode and check the backface culling option).
Your wave has way too many quads, you really don't need that many (900!).
By using the model loader, you won't have issues and it will make your life easier.
But I haven't finished it, so it probably wouldn't work right now without some extra work.

For the ZTI (z-treme image) files, use the version of the image converter I put in the ZT folder.
You can put anything and even add effects like clouds moving in the sky.

Just use an alpha channel and save the images as 32 bits TGA (no compression).
Choose 4 bpp or 8 bpp setting in the image converter and just convert.
You can also play with the layer's priority to decide what's in front of what.
I made it so that by default the order is NBG3, NBG0, sprites, NBG1.
 
Really like the ZTI background stuff ill be sure to take advantage. Looking forward to your model loader too. Im having trouble integrating your stuff in my build on github with the player models. Seems to refuse to build even with the wave/original BG taken out and only the hair of the player used and is single plane leaving plenty of memory. Ill keep messing around though, I put it up on github (No wave/No player only single plane hair used) and worked without your stuff and now doesn't build after (not missing any files in cd and etc). Seems odd since yours has a entire body and works.

Thanks for the backface culling tip seems clipping is the problem mostly now even using display_level(5)/usenearclip leaves it at its best but still majorly culled off when the normals are correct.

Edit: Got it working. Found your makefile change. Thanks for the help! The ZTI show up currupted though, probably something small im missing
 
Last edited:
Really like the ZTI background stuff ill be sure to take advantage. Looking forward to your model loader too. Im having trouble integrating your stuff in my build on github with the player models. Seems to refuse to build even with the wave/original BG taken out and only the hair of the player used and is single plane leaving plenty of memory. Ill keep messing around though, I put it up on github (No wave/No player only single plane hair used) and worked without your stuff and now doesn't build after (not missing any files in cd and etc). Seems odd since yours has a entire body and works.

Thanks for the backface culling tip seems clipping is the problem mostly now even using display_level(5)/usenearclip leaves it at its best but still majorly culled off when the normals are correct.

Edit: Got it working. Found your makefile change. Thanks for the help! The ZTI show up currupted though, probably something small im missing
I think the corruption issue is because you have too many files on your cd in the same folder, so it just doesn't read it.
Try using subfolders.
The ztLoadVDP2bmp supports subfolders in my own engine, but I removed it here for compatibility with Jo Engine.
Try putting your textures elsewhere, it should fix your background problems.

If not, make sure to turn off any layers you are not using (you can put the priority of the layer to 0 to turn it off - look for the code I included for examples) or that they are not overlapping in memory (like if you keep NBG0 at A0, you should keep NBG1 elsewhere, maybe just put it at A1).
 
You were right about the subfolders. Really like your system I can replace the memory/BG for another easily.
I cant seem to change the priority of a layer be over everything to get a title screen with the controls gui going. This would be better than running 4 seperate tgas as 3d matrix's(200x83px & 180x50px)? In the ztLoadVDP2bmp code the priority it's the int before " bmNBG0"? Doesn't seem to have an effect in Jo and its always behind my planes same with it as NBG0/NBG1.
 
You were right about the subfolders. Really like your system I can replace the memory/BG for another easily.
I cant seem to change the priority of a layer be over everything to get a title screen with the controls gui going. This would be better than running 4 seperate tgas as 3d matrix's(200x83px & 180x50px)? In the ztLoadVDP2bmp code the priority it's the int before " bmNBG0"? Doesn't seem to have an effect in Jo and its always behind my planes same with it as NBG0/NBG1.
I'm not 100% sure what you mean about the int, but that would probably be the palette id. Palette 0 is for the text, but only the first 2 colors are really used. I will add an offset later in the image converter to allow using the palette 0. The priority is something like slPriorityNbg0 (0 to 7), with 7 being the closest to the screen and 1 being the last one. 0 is just to turn it off. Make sure you change it for all the layers else they will just use their default behavior if 2 layers have the same priority.
 
Awesome slPriorityNbg0 was just what I needed. Defiantly better then the separate 4 TGAS I was loading before. Only thing is the ZTI coming out a bit dark but Ill probably just need make sure i converting right. Still wont let me load anymore of my player like the body without blue/black screening but im defiantly getting there.
If it's getting dark it's because you have more than 15 colors (for the 4 bpp mode). Color 0 is reserved for the transparent color. In Gimp you can do something like Mode->indexed, 15 colors. Make sure you then put it back to RGB (Mode->RGB) and save it without RLE compression.
 
Awesome slPriorityNbg0 was just what I needed. Defiantly better then the separate 4 TGAS I was loading before. Only thing is the ZTI coming out a bit dark but Ill probably just need make sure i converting right. Still wont let me load anymore of my player like the body without blue/black screening but im defiantly getting there.
There you go.
The demo will load a binary file, it can also work with header files (just use binary as it's way more flexible).

Don't mind the Sonic model, you can fix the weird eyes placement by rotating your textures or invert them. Sadly it doesn't read texture coordinates, so you need to do it manualy.
But it does read your model, convert it to the ZTP (Z-Treme polygon) format and you can easily load multiple models (as shown in the demo).

Note that Jo Engine default workarea can only process 2 000 quads / 2500 vertices, so you won't see the whole Quake map (it's normal and this tool isn't meant for maps anyway, I just threw it there since I had it).

I will try to add realtime gouraud support at a later time.

You will find the demo attached and in the TOOL subfolder you have the model converter (0.02) and the ZTI image converter (0.25 or whatever number I gave it).

EDIT : See post right under
 
Last edited:
Ok, I made an update to allow realtime gouraud shading.
The way I calculate the vertex normals is probably not 100% accurate, so the gouraud shading might not be as smooth as what you get using the official Saturn SDK 3d tool, but hey, you can output a textured 3D model with almost no work, so I'm sure you won't mind. ;)

I also tried a way to read the texture coordinates, but the results were too random, I just removed it in the end.
It could very well be my fault as I'm not very good in Blender.
Just flip your quads/textures until you get the result you want.

Note that in the demo Sonic is using realtime gouraud, while the Quake level is using per quad light (not as good looking).

EDIT : Ok, I think I got the per-vertex normals right, see attached image following the first one.

Gouraud.png
 

Attachments

  • ZT_Model_loading_0_04.zip
    2.7 MB · Views: 118
  • Gouraud2.png
    Gouraud2.png
    344.5 KB · Views: 106
  • Gouraud3.png
    Gouraud3.png
    396.1 KB · Views: 102
  • Gouraud4.png
    Gouraud4.png
    334.5 KB · Views: 109
Last edited:
I did a small test on real hardware, the VDP1 could keep up with drawing 7 models with realtime gouraud at 60 fps, while the CPU started to struggle at 4 models (maybe 5 without realtime gouraud).
Sadly I didn't test with more models as I thought it would be too slow (I guess I was wrong).
Sega didn't use the SCU DSP for lightning, so I'm really trying to figure out what it could be used for!

 

Attachments

  • 20180515_163448.jpg
    20180515_163448.jpg
    7 MB · Views: 143
Last edited:
Whoa what a update! I wonder how many you can get in there at 60. I was thinking about porting my Katamari clone in JS/C# but cant grasp how to convert it ATM but looks like it could be really cool demo with your engine. Especially now the texture mapping is so easy.

Pretty sure I'm doing something simple wrong though with Blender for your convertor. I can reconvert and use your Sonic .obj with the ZTE convertor just fine. But I tried replacing the sonic (.h and .ZTP) with the Blender default sample Box with 1 TGA I converted and neither it or the title show up anymore in a emulator and I get this error message in SSF.
http://i.magaimg.net/img/3b5r.png
I took your sonic .obj and added 1 TGA(no UV mapping) to the stomach and it doesnt show a error but doesnt show up either.
 
Back
Top