FOnline Development > 3D Development

How to make 3D characters work

(1/3) > >>

Karpov:
How to make 3D characters work

I'm going to explain how to make the characters work properly. Read carefully.

Optional: before we download the files, it's recommended to move the VanBuren3D files that are located in the folders data/art/critters and data/textures into a zip file.
This zip file needs to have the same structure as the data folder. That means you need to have on the file's root a folder named textures, and another one named art, and inside art the folder critters. When we have that file, we can clear those two folders, and place the zip in the client folder. Once the zip is there, we edit the file named DataFiles.cfg, which is located in the same folder, and add a line to it with the name of our zip file.

Getting the files
First we download the files from the repository with a subversion (svn) client, like TortoiseSVN  and we set it to download into client/data folder.
With this URL
http://svn3.xp-dev.com/svn/FOnline3d/trunk/data/

If everything goes right, the files should download into data/textures and data/art/critters

Now we edit some game files.
Note: always backup the files you edit. And it is recommended that you test the game after you edit each code inside the scripts.

Enabling the characters.
Once the downloaded files are in the right folders, we need to set them up in the server.
To enable the 3D characters go to the folder server/data/scripts and edit _defines.fos and uncomment this line by erasing those two slashes (//)


--- Code: ---//#define PLAYERS_3D             // Enable 3d players
--- End code ---
(Change To)

--- Code: ---#define PLAYERS_3D             // Enable 3d players
--- End code ---

Test. Now the character creation should be 3D. Always erase the cache before you test any change in the scripts.

Setting the new bodytypes.
Next, in the file server/data/CritterTypes.cfg we set up the new body types, replacing the default 3D characters.

Find critter ID 292 (@  292 in the file)
We'll modify number 292, 293, 295, and 296
Change their names (default name "VBMaleStrong") to these:
292 FOMaleStrong
293 FOMaleStrong_Death
295 FoFemaleNormal
296 FoFemaleNormal_Death

Optional: you can set a new number if you want to keep the old 3D models. But be careful with some scripts that use those numbers.

Test. In the character creation, one of the bodytypes should be the new 3D character. If you can't pick the bodytype from the character creation, select a default body and play. Then inside a map, run the command ~run debug body 0 292 1
and the character should render.

Setting up the armors
Next is to set up the armors. To do this, open the file scripts/Client_main.fos, search for the line "void animation3d_process", scroll down until you see
"switch(armorPid)"
Note: Be careful with this, because there is another line that says "/*switch(armorPid)". That's not the one
Select from "switch(armorPid) to the last time the string "break;" appears (right before // Backpack) and paste this code


--- Code: ---  switch(armorPid)
    {
case PID_LEATHER_JACKET:
bodyAtr     = 1;
handsAtr    = 1;
break;

case PID_POWERED_ARMOR:
case PID_HARDENED_POWER_ARMOR:
bodyAtr     = 6;

feetAtr     = 6;

break;

case PID_TIBBETS_PRISON:
bodyAtr     = 0;
break;

case PID_JUMPSUIT:
case PID_FAKE_JUMPSUIT:
case PID_VAULT_SUIT:
bodyAtr     = 0;
break;

case PID_LEATHER_ARMOR:
                bodyAtr     = 8;
handsAtr    = 8;
break;
case PID_LEATHER_ARMOR_MK_II:
                bodyAtr     = 20;
handsAtr    = 20;
break;
case PID_CURED_LEATHER_ARMOR:
bodyAtr     = 10;
handsAtr    = 10;
break;

case PID_METAL_ARMOR:
                bodyAtr     = 2;
handsAtr    = 2;
break;
case PID_METAL_ARMOR_MK_II:
                bodyAtr     = 3;
handsAtr    = 2;
break;
case PID_TESLA_ARMOR:
bodyAtr     = 19;
handsAtr    = 16;
break;

       case 600:
bodyAtr     = 7;
handsAtr    = 7;
break;


case PID_COMBAT_ARMOR:
                bodyAtr     = 5;
handsAtr    = 5;
                backpackAtr = 3;
break;
case PID_COMBAT_ARMOR_MK_II:
                bodyAtr     = 14;
handsAtr    = 5;
                backpackAtr = 3;
break;
case PID_BROTHERHOOD_COMBAT_ARMOR:
                bodyAtr     = 15;
handsAtr    = 5;
                backpackAtr = 3;
break;

case PID_BLACK_COMBAT_ARMOR:
bodyAtr     = 16;
handsAtr    = 16;
                backpackAtr = 3;
        break;

case PID_ADVANCED_POWER_ARMOR:
case PID_ADVANCED_POWER_ARMOR_MK2:
bodyAtr     = 9;
feetAtr     = 9;
break;

case PID_PURPLE_ROBE:
bodyAtr     = 11;
break;
case PID_KEEPBRIGE_ROBE:
bodyAtr     = ATTRIBUTE_Body_PrisonSuit;
feetAtr     = ATTRIBUTE_Feet_PrisonSuit;
headAtr     = ATTRIBUTE_Head_StrawHat;
break;
case PID_BLACK_ROBE:
bodyAtr     = ATTRIBUTE_Body_PrisonSuit;
feetAtr     = ATTRIBUTE_Feet_PrisonSuit;
headAtr     = ATTRIBUTE_Head_ArmingCap;
break;

default:
break;
--- End code ---

IMPORTANT be careful not to erase the closing bracket after "break;" "}"

Test. Use different armors.

Additional Info:
Inside that code, the name of the attributes don't match the ones I used. This is what they actually are:
#    rhandleAtr  = Right Hand
#    lhandleAtr  = Unactive Slot/Holstered weapons
#    handsAtr    = Armor Clothing Layer
#    bodyAtr     = Armor Layer
#    feetAtr     = Headwear Layer
#    headAtr     = Unused/Free
#    eyeAtr      = Eyewear (nothing available yet)
#    shoulderAtr = Custom Pants
#    backAtr     = Custom Shoes
#    backpackAtr = Backpack

Setting up the weapons

Still inside client_main scroll down and find "int GetHandleValue(uint16 pid)"
select from ther to the last bracket (}), right before "#endif" and paste this:

--- Code: ---int GetHandleValue(uint16 pid)
{
if(pid == 0 || (pid >= 1000 && pid <=1100)) return 0;
int handle = 0;
return int(pid);
}
--- End code ---

Test. Try different weapons (or items)

Making the Death animations work.

First, find in client_main a line that says "case ACTION_DEAD:"

Select from that line to the last "break;", right before "case ACTION_CONNECT", and paste this.


--- Code: ---case ACTION_DEAD:

        if( not cr.IsDead() && not cr.IsAnim3d())

            {
            cr.ClearAnim();
            cr.Animate( 0, actionExt );
Message("DEBUG NOTE: 2D critter died. No bodytype switched", FOMB_GAME );           
        }

else if( cr.IsAnim3d())
     
{
 switch( actionExt )           
            {
 case ANIM2_DEAD_FRONT:
                  cr.ClearAnim();
            cr.Animate( 0, actionExt );
Message("DEBUG NOTE: 3D critter died on DEAD_FRONT. No special anim, so bodytype has not changed", FOMB_GAME );
                break;

 case ANIM2_DEAD_BACK :
                  cr.ClearAnim();
            cr.Animate( 0, actionExt );
Message("DEBUG NOTE: 3D critter died on DEAD_BACK. No special anim, so bodytype has not changed", FOMB_GAME );
                break;

 case ANIM2_DEAD_BLOODY_SINGLE:
                    cr.ClearAnim();           
if (cr.Stat[ST_GENDER] == 0)
{
RunServerScriptUnsafe("debug@unsafe_specialdeath",(cr.Id),293,110,null,null);
PlaySound("HMXXXXBD.ACM");
}
else if (cr.Stat[ST_GENDER] == 1)
{
RunServerScriptUnsafe("debug@unsafe_specialdeath",(cr.Id),296,110,null,null);
PlaySound("HFXXXXBD.ACM");
}
         
Message("DEBUG NOTE: 3D critter died on BLOODY_SINGLE. Bodytype switched to dead model", FOMB_GAME );
                break;

 case ANIM2_DEAD_BLOODY_BURST:
cr.ClearAnim();           
if (cr.Stat[ST_GENDER] == 0)
{
RunServerScriptUnsafe("debug@unsafe_specialdeath",(cr.Id),293,111,null,null);
PlaySound("HMXXXXBF.ACM");
}
else if (cr.Stat[ST_GENDER] == 1)
{
RunServerScriptUnsafe("debug@unsafe_specialdeath",(cr.Id),296,111,null,null);
PlaySound("HFXXXXBF.ACM");
}
Message("DEBUG NOTE: 3D critter died on BLOODY_BURST. Bodytype switched to dead model", FOMB_GAME );
                break;

 case ANIM2_DEAD_BURST:
cr.ClearAnim();           
if (cr.Stat[ST_GENDER] == 0)
{
RunServerScriptUnsafe("debug@unsafe_specialdeath",(cr.Id),293,112,null,null);
PlaySound("HMXXXXBG.ACM");
}
else if (cr.Stat[ST_GENDER] == 1)
{
RunServerScriptUnsafe("debug@unsafe_specialdeath",(cr.Id),296,112,null,null);
PlaySound("HFXXXXBG.ACM");
}
Message("DEBUG NOTE: 3D critter died on BURST. Bodytype switched to dead model", FOMB_GAME );
                break;

 case ANIM2_DEAD_LASER:
cr.ClearAnim();           
if (cr.Stat[ST_GENDER] == 0)
{
RunServerScriptUnsafe("debug@unsafe_specialdeath",(cr.Id),293,115,null,null);
PlaySound("HMXXXXBI.ACM");
}
else if (cr.Stat[ST_GENDER] == 1)
{
RunServerScriptUnsafe("debug@unsafe_specialdeath",(cr.Id),296,115,null,null);
PlaySound("HFXXXXBI.ACM");
}
Message("DEBUG NOTE: 3D critter died on LASER. Bodytype switched to dead model", FOMB_GAME );
                break;
           
 case ANIM2_DEAD_FUSED:
cr.ClearAnim();           
if (cr.Stat[ST_GENDER] == 0)
{
RunServerScriptUnsafe("debug@unsafe_specialdeath",(cr.Id),293,116,null,null);
PlaySound("HMXXXXBM.ACM");
}
else if (cr.Stat[ST_GENDER] == 1)
{
RunServerScriptUnsafe("debug@unsafe_specialdeath",(cr.Id),296,116,null,null);
PlaySound("HFXXXXBM.ACM");
}
Message("DEBUG NOTE: 3D critter died on FUSED (plasma). Bodytype switched to dead model", FOMB_GAME );
                break;

 case ANIM2_DEAD_PRONE_FRONT :
                  cr.ClearAnim();
            cr.Animate( 0, actionExt );
Message("DEBUG NOTE: 3D critter died on PRONE_FRONT. No bodytype switch needed", FOMB_GAME );
                break;

 case ANIM2_DEAD_PRONE_BACK:
                  cr.ClearAnim();
            cr.Animate( 0, actionExt );
Message("DEBUG NOTE: 3D critter died on PRONE_BACK. No bodytype switch needed", FOMB_GAME );
                break;

 case ANIM2_DEAD_PULSE:
                  cr.ClearAnim();
if (cr.Stat[ST_GENDER] == 0) RunServerScriptUnsafe("debug@unsafe_specialdeath",(cr.Id),11,113,null,null);
if (cr.Stat[ST_GENDER] == 1) RunServerScriptUnsafe("debug@unsafe_specialdeath",(cr.Id),5,113,null,null);
Message("DEBUG NOTE: 3D critter died on PULSE. No 3d anim available. Needs switch to 2d and play anim again", FOMB_GAME );
                break;

 case ANIM2_DEAD_PULSE_DUST :
                  cr.ClearAnim();
if (cr.Stat[ST_GENDER] == 0) RunServerScriptUnsafe("debug@unsafe_specialdeath",(cr.Id),11,114,null,null);
if (cr.Stat[ST_GENDER] == 1) RunServerScriptUnsafe("debug@unsafe_specialdeath",(cr.Id),5,114,null,null);
Message("DEBUG NOTE: 3D critter died on PULSE_DUST. No 3d anim available. Needs switch to 2d and play anim again", FOMB_GAME );
                break;

 case ANIM2_DEAD_EXPLODE :
if (cr.Stat[ST_GENDER] == 0) RunServerScriptUnsafe("debug@unsafe_specialdeath",(cr.Id),12,117,null,null);
if (cr.Stat[ST_GENDER] == 1) RunServerScriptUnsafe("debug@unsafe_specialdeath",(cr.Id),5,117,null,null);                 
 Message("DEBUG NOTE: 3D critter died on EXPLODE. No 3d anim available. Needs switch to 2d and play anim again", FOMB_GAME );
                break;

 case ANIM2_DEAD_BURN :
                  cr.ClearAnim();
if (cr.Stat[ST_GENDER] == 0) RunServerScriptUnsafe("debug@unsafe_specialdeath",(cr.Id),11,118,null,null);
if (cr.Stat[ST_GENDER] == 1) RunServerScriptUnsafe("debug@unsafe_specialdeath",(cr.Id),5,118,null,null);
Message("DEBUG NOTE: 3D critter died on burn. No 3d anim available. Needs switch to 2d and play anim again", FOMB_GAME );
                break;

 case ANIM2_DEAD_BURN_RUN:
                  cr.ClearAnim();
if (cr.Stat[ST_GENDER] == 0) RunServerScriptUnsafe("debug@unsafe_specialdeath",(cr.Id),11,119,null,null);
if (cr.Stat[ST_GENDER] == 1) RunServerScriptUnsafe("debug@unsafe_specialdeath",(cr.Id),5,119,null,null);
Message("DEBUG NOTE: 3D critter died on BURN_RUN. No 3d anim available. Needs switch to 2d and play anim again", FOMB_GAME );
                break;

            default:
                break;
}
--- End code ---

You need two closing brackets at the end.

Second, we open the file debug.fos and add this code at the bottom:


--- Code: ---void unsafe_specialdeath(Critter& player, int param0, int param1, int param2, string@ param3, int[]@ param4)
    {
         Critter@ target=GetCritter(def(param0,player.Id));
if (not valid(target)) return;
target.ChangeCrType(param1);
target.Animate( player.Id, param2, null, true, true );
           
    }
--- End code ---

Info: This method is not very effective, but it's all I can do. This has to be coded inside the engine and not executed from an external script. I'm sure someone will find a way to make it work properly.

Test. Try to die. Or use these commands:
~run debug body 0 293 1
~run debug anim 0 1 115

Shaders
Place these files inside client/effects.
Edit IOstructures.inc and add this at the bottom


--- Code: ---struct VsToPs_3DNormal
{
float4 Position     : POSITION;       
float2 TexCoord     : TEXCOORD0;
float3 Normal       : TEXCOORD1;
};

--- End code ---

Erase the cache. Test, and you should see some armors shine, and a different shading on the character.
That's all for now, I hope it works for you.

Please send me a message if you have any question. Sometimes I miss something or make a mistake that I don't notice, so don't hesitate and tell me about it.

Also, to enable the muzzleflashes, and death gore use this command
~param 0 161 101
or
~run debug SetCritParam (#crID) 161 101
last one works on other critters using their id number

Graf:
It's alive!  :) I'm still testing certain things, cause there are a lot of differences between our project (based on revision 360) and the clean SDK, but I can say for sure, that it is working very well. I haven't yet tested the death animations and shaders though.

One thing I would like to ask, is that where I can add item PID's to a different values? In my case, all items are described in _itempid.fos, see example:


--- Code: ---# define PID_9MM_MAUSER ( 11000 ) / / 9mm Mauser
# define PID_10MM_PISTOL ( 11001 ) / / 10mm gun
# define PID_14MM_PISTOL ( 11002 ) / / 14mm gun


--- End code ---

This is clear enough. But in game they don't show up. I supposed it's because they weren't added in client_main.fos and added extra values, like here:


--- Code: ---#ifdef PLAYERS_3D
int GetHandleValue(uint16 pid)
{
if(pid == 0 || (pid >= 1000 && pid <=1100)) return 0;

int handle = 0;
switch(pid)
{
case 11000 : handle = 11000 ;                break;
case 11001 : handle = 11001 ;                break;
case 11002 : handle = 11002 ;                break;

// Generic item
default:                                 handle = 0;                                                  break;
}

return handle;
}
#endif
--- End code ---

It doesn't work like that. Using an item 11000 or 11001 on a right hand slot (as well as left hand) gives nothing.

Have you got any idea?

Karpov:
Yes, there are some differences, some of them are intended and some of them are errors.
For example, switching the weapon to second slot (holster) will now render the weapon before the animation ends, but it's weird that it happens with some of them, so I would say it's intended for some reason. Also there's an error with the character name and floating text when you select 3D , even default VB3d. Another thing is the death anims show a little frame in the transition between bodies, and sometimes the character loses his skin texture (?).

About the weapons. I replaced the original code, with one that matches all of the PIDs to the handle Layer values. I suggest you use this code and try a lower number. I'm not sure, but this might be your problem :

if(pid == 0 || (pid >= 1000 && pid <=1100)) return 0;

Perhaps items in hand can't exceed the ID#1000. Just guessing, I have no idea what the code means...

I just hope you are not planning to add 11000 items  ;D

Graf:

--- Quote from: Karpov on September 11, 2013, 06:07:51 pm ---Also there's an error with the character name and floating text when you select 3D , even default VB3d.

--- End quote ---

Yeah, there's such an issue when the game is launched via Fonline D3D, but it can be avoided by using a Fonline OpenGL (or visa versa, I don't remember). In Fonline D3D there's no obvious way to fix the floating text/name thingie. Only cvet knows how to fix it, but for now he's busy with other things.


--- Quote from: Karpov on September 11, 2013, 06:07:51 pm ---I just hope you are not planning to add 11000 items  ;D

--- End quote ---

Haha :D Not really, it's just for our convenience. For example, weapon PID's start at 11000, armors at 7000 etc etc.

By the way, I didn't get the issue with items not showing yet, cause our scripter is preparing some nice tool for semi-automated items integration. We will see what comes of it.

Karpov:
Graf: Yes, it's obviously a bug, because it was working fine in the older revision.
It would be useful if you could find a way to add more layers, maybe it will help. Also, in case you didn't know, you can attach more than one file/model to a single layer value. Just a tip; I don't know how that system you are working on is going to work.
_________________________________

Someone asked if the models work in 2238, so I tested them. The structure is quite different from the standard FOnlineSDK.

First issue is that the character registration won't work. Enabling the #DEFINE PLAYERS_3D line will make every script that was previously ignoring it, start to use it. But there are 3 functions that are imported from the character registration script into client_main , that will cause a problem, and the script won't compile at all, making the game crash. If you are smart and find a way to fix it, that's very nice. I just cancelled those three functions inside the client_main script, and the problem was solved for me  ;)

Second, the code for armors and weapons are inside client_mapper_animation instead of client_main. Don't know why, but if something works, you don't ask...

You have to enable the characters from CritterTypes by adding the @ symbol at the beggining of the line. The critter IDs are different and they are named like "vbmstr" or something like that.

Last thing. The effects won't work; they wont render and the log states an error with DirectX. That's an SDK related issue that was fixed not long ago. If you could re-compile the game with a newer SDK, or find another way to fix the error, it should work fine.
Note: I think 3D_default.fx should work without error, but the specular effect will make it crash.

In the game I just had to change my body using the 'disguise command, and there it was.
  This is me, in Junktown, with a mercenary named Carrie Basset. Without shader  :(


Navigation

[0] Message Index

[#] Next page

Go to full version