Saints Row: The Third NPC morphs for the player

[V] IdolNinja

Volition Staff
Back in SR2, there were template cheats where you could morph into any npc that we modded back in. In SRTT though, it seems the system has changed where npcs are actually clothing that you wear. We've had some small success with changing a current items mesh/texture to an npc and wearing it, but it's extremely buggy.
http://www.saintsrowmods.com/forum/threads/some-npc-morph-thing-by-datasnake.2040/

Is there a better way to approach this so that everything works correctly for both sexes?
 
So you're basically asking how to change the player to look like an NPC like during the mission when you become Cyrus? I think we need Meatplowz here, because I'm not sure I understand it all. :) I'll see if I can't poke him so he stops by.
 
The process is of course quite different SR3 and on. Luckily, much of this is still possible, while not as flexible. All the characters on SR2 were essentially the same player character mesh and rig, which made every character compatible, not very flexible for a lot of visual differences though.

In Sr3 there are 5 base character skeletons and of course a few special one-offs including the Matt Miller Avatar.
All the skeletons have the same base hierarchy, except for Brutes, for animation sharing purposes. The bones that show up in the rig_pc ( skeleton file ) , after index 64 are mostly custom to that character. These bones include eyes, breasts and any bones that were used for cloth simulation specifically for that characters. The base 64 bones should map to and inherit from any other character animation in game. The main differences are proportional between the 5 base skeletons. These differences are important and incompatible skeletons will have rotations that can result in bones appearing sheared and stretched. All characters have specific animation sets associated with them which you can see noted in the character_definitions.xtbl and/or overrides in the character.xtbl.

Below are all the "compatible" animation sets. If you looks in the definitions.xtbl you can see which ones should be compatible with the Player Male or Player Female. The character that have the animation sets compatible with the PlayerMale or PlayerFemale should be able to be setup as the character mesh, or clothing item, much like the Cyrus Suit in the customization_items.xtbl. Most of the NPC's are made up of Heads and Bodies, which is why there is a Cyrus Suit and Cyrus Head. It's possible the replacing the cmeshx filename reference in that entry with another compatible npc mesh will work just fine. That and creating similar entries for each character. Doing the same for the female npc's in the female mesh filename entry should work as well.

Other things to note about the player male and female. Gender is important here, as there are two main rigs used by the player, "cm_body.rig" and "cf_body.rig". All the player animations are done with these rigs/skeletons. When you change gender in game from male to female, the rig being used switches from cm_body.rig to cm_body.rig and the animation set switches from PLYM to PLYF. In regards to the clothing that is worn, you can see in the customization_items.xtbl there are entries for male_mesh and female_mesh for each item. The relative mesh will be switched out on gender change as well. So, if the hacks mesh references that were used for female characters weren't referenced in the female_mesh field then the npc's and player items would not have worked as you might think, they would still be using the cm_body.rig and male animation set.

Player_Male -> Heroic_Male
Compatible Animation Sets
PLYM, STAG, COP,GANG,KILLBANE

Player_Female -> Gang_Female ( Small proportional differences but should be mostly compatible )
Compatible Animation Sets
PLYF, GANGF

Pedestrians -> GenericMale/GenericFemale ( Only compatible with themselves )
Compatible Animation Sets
PEDM, PEDF

Brutes ( Only compatible with themselves )
Compatible Animation Sets
BRUTE* ( Flamethrower, and HeavyGun )
 
I experimented with this stuff months ago without having any idea of how it worked and got some... wacky results.

I randomly chose an NPC for this test. Female, to match my character. The file i looked inside is gang_female_mary_high.str2_pc. So, now i've changed customization_items.xtbl to say
Code:
                        <Male_Mesh_Filename>
                            <Filename>mary_body_high.cmeshx</Filename>
                        </Male_Mesh_Filename>
                        <Female_Mesh_Filename>
                            <Filename>mary_body_high.cmeshx</Filename>
                        </Female_Mesh_Filename>
for the Cyrus Suit and
Code:
                        <Male_Mesh_Filename>
                            <Filename>mary_head_high.cmeshx</Filename>
                        </Male_Mesh_Filename>
                        <Female_Mesh_Filename>
                            <Filename>mary_head_high.cmeshx</Filename>
                        </Female_Mesh_Filename>
for the Cyrus Head. Then i added this to my sr3_city_main():
Code:
    customization_item_wear( "Cyrus Suit", "mary_body_high.cmeshx", "Cyrus", false )
    customization_item_wear( "Cyrus Head", "mary_head_high.cmeshx", "Cyrus", false )
Aaaand... it crashes. Any advice? Do i need the meshes to be in a preload VPP?
 
I experimented with this stuff months ago without having any idea of how it worked and got some... wacky results.
...
Aaaand... it crashes. Any advice? Do i need the meshes to be in a preload VPP?

I would expect that you would need alter the cyrus head and body str2 files to swap the actual mesh files you wanted to load into them in addition to what you have done. I am surprised it crashed though, I would expect it to just not work and load the cyrus stuff, but maybe you confused it sufficiently by changing half the things.
 
Yeah, if i leave the original mesh names in the call to customization_item_wear, it doesn't crash; it does nothing. In fact, the Lua call halts, which means there's an error, but i don't get the text of it.

Okay, round two.

Cyrus item str2 files are in customize_item.vpp_pc
head: custmesh_-629684167.str2_pc & custmesh_-629684167f.str2_pc
body: custmesh_871704894.str2_pc & custmesh_871704894f.str2_pc
These contain ccmesh, cpeg, gcmesh, and gpeg files.

Mary's character str2 file is in characters.vpp_pc
both: gang_female_mary_high.str2_pc
This contains cpeg, gpeg, and matlib files. No mesh files; that's worrying.

I delete the files inside -629684167. I replace them with the @streams.xml and the 3 mary_head_high files. I edit @streams.xml to remove references to the body files.
Then i repeat the process for the body in 871704894 , keeping the body files and removing references to the head files.
I repack both files and put them in the root, along with everything else remaining as in my last post.

Ah, but i need to update an asm file. Gibbed's tool doesn't support adding or removing files from the list, only updating their packed size. But i can export the ASM as XML.

This is what it contains for the body file:
Code:
    <ContainerEntry>
      <Name>custmesh_871704894f</Name>
      <Type>10</Type>
      <Flags>Unknown7</Flags>
      <DataOffset>6144</DataOffset>
      <Parent />
      <Extra />
      <CompressedSize>707782</CompressedSize>
      <Sizes>
        <PrimitiveSize>
          <CPUSize>4456</CPUSize>
          <GPUSize>244420</GPUSize>
        </PrimitiveSize>
        <PrimitiveSize>
          <CPUSize>395</CPUSize>
          <GPUSize>786432</GPUSize>
        </PrimitiveSize>
      </Sizes>
      <Primitives>
        <PrimitiveEntry>
          <Name>cyrus_hires.ccmesh_pc</Name>
          <Type>9</Type>
          <Allocator>0</Allocator>
          <Flags>IsSplit</Flags>
          <SplitIndex>0</SplitIndex>
          <CPUSize>4456</CPUSize>
          <GPUSize>244420</GPUSize>
          <Unknown7>0</Unknown7>
        </PrimitiveEntry>
        <PrimitiveEntry>
          <Name>cyrus_hires.cpeg_pc</Name>
          <Type>10</Type>
          <Allocator>0</Allocator>
          <Flags>IsSplit</Flags>
          <SplitIndex>0</SplitIndex>
          <CPUSize>395</CPUSize>
          <GPUSize>786432</GPUSize>
          <Unknown7>0</Unknown7>
        </PrimitiveEntry>
      </Primitives>
    </ContainerEntry>

This is what's in character_containers.asm_pc for mary:
Code:
  <ContainerEntry>
      <Name>gang_female_mary_high</Name>
      <Type>8</Type>
      <Flags>Unknown7</Flags>
      <DataOffset>6144</DataOffset>
      <Parent />
      <Extra />
      <CompressedSize>1288954</CompressedSize>
      <Sizes>
        <PrimitiveSize>
          <CPUSize>294</CPUSize>
          <GPUSize>1059504</GPUSize>
        </PrimitiveSize>
        <PrimitiveSize>
          <CPUSize>1568</CPUSize>
          <GPUSize>0</GPUSize>
        </PrimitiveSize>
        <PrimitiveSize>
          <CPUSize>394</CPUSize>
          <GPUSize>1092288</GPUSize>
        </PrimitiveSize>
        <PrimitiveSize>
          <CPUSize>2912</CPUSize>
          <GPUSize>0</GPUSize>
        </PrimitiveSize>
      </Sizes>
      <Primitives>
        <PrimitiveEntry>
          <Name>mary_head_high.cpeg_pc</Name>
          <Type>16</Type>
          <Allocator>0</Allocator>
          <Flags>IsSplit</Flags>
          <SplitIndex>0</SplitIndex>
          <CPUSize>294</CPUSize>
          <GPUSize>1059504</GPUSize>
          <Unknown7>0</Unknown7>
        </PrimitiveEntry>
        <PrimitiveEntry>
          <Name>mary_head_high.matlib_pc</Name>
          <Type>22</Type>
          <Allocator>0</Allocator>
          <Flags />
          <SplitIndex>0</SplitIndex>
          <CPUSize>1568</CPUSize>
          <GPUSize>0</GPUSize>
          <Unknown7>0</Unknown7>
        </PrimitiveEntry>
        <PrimitiveEntry>
          <Name>mary_body_high.cpeg_pc</Name>
          <Type>16</Type>
          <Allocator>0</Allocator>
          <Flags>IsSplit</Flags>
          <SplitIndex>0</SplitIndex>
          <CPUSize>394</CPUSize>
          <GPUSize>1092288</GPUSize>
          <Unknown7>0</Unknown7>
        </PrimitiveEntry>
        <PrimitiveEntry>
          <Name>mary_body_high.matlib_pc</Name>
          <Type>22</Type>
          <Allocator>0</Allocator>
          <Flags />
          <SplitIndex>0</SplitIndex>
          <CPUSize>2912</CPUSize>
          <GPUSize>0</GPUSize>
          <Unknown7>0</Unknown7>
        </PrimitiveEntry>
      </Primitives>
    </ContainerEntry>

So i'm going to split that into two. I'm not sure how much of this to overwrite the other entry with, but i'll give it a try.

Into the sections for custmesh_871704894 and custmesh_871704894f (the body) i put
Code:
      <Type>8</Type>
      <Flags>Unknown7</Flags>
      <DataOffset>6144</DataOffset>
      <Parent />
      <Extra />
      <CompressedSize>1288954</CompressedSize>
      <Sizes>
        <PrimitiveSize>
          <CPUSize>394</CPUSize>
          <GPUSize>1092288</GPUSize>
        </PrimitiveSize>
        <PrimitiveSize>
          <CPUSize>2912</CPUSize>
          <GPUSize>0</GPUSize>
        </PrimitiveSize>
      </Sizes>
      <Primitives>
        <PrimitiveEntry>
          <Name>mary_body_high.cpeg_pc</Name>
          <Type>16</Type>
          <Allocator>0</Allocator>
          <Flags>IsSplit</Flags>
          <SplitIndex>0</SplitIndex>
          <CPUSize>394</CPUSize>
          <GPUSize>1092288</GPUSize>
          <Unknown7>0</Unknown7>
        </PrimitiveEntry>
        <PrimitiveEntry>
          <Name>mary_body_high.matlib_pc</Name>
          <Type>22</Type>
          <Allocator>0</Allocator>
          <Flags />
          <SplitIndex>0</SplitIndex>
          <CPUSize>2912</CPUSize>
          <GPUSize>0</GPUSize>
          <Unknown7>0</Unknown7>
        </PrimitiveEntry>
      </Primitives>

Then into the head files' sections, i'll put
Code:
      <Type>8</Type>
      <Flags>Unknown7</Flags>
      <DataOffset>6144</DataOffset>
      <Parent />
      <Extra />
      <CompressedSize>1288954</CompressedSize>
      <Sizes>
        <PrimitiveSize>
          <CPUSize>294</CPUSize>
          <GPUSize>1059504</GPUSize>
        </PrimitiveSize>
        <PrimitiveSize>
          <CPUSize>1568</CPUSize>
          <GPUSize>0</GPUSize>
        </PrimitiveSize>
      </Sizes>
      <Primitives>
        <PrimitiveEntry>
          <Name>mary_head_high.cpeg_pc</Name>
          <Type>16</Type>
          <Allocator>0</Allocator>
          <Flags>IsSplit</Flags>
          <SplitIndex>0</SplitIndex>
          <CPUSize>294</CPUSize>
          <GPUSize>1059504</GPUSize>
          <Unknown7>0</Unknown7>
        </PrimitiveEntry>
        <PrimitiveEntry>
          <Name>mary_head_high.matlib_pc</Name>
          <Type>22</Type>
          <Allocator>0</Allocator>
          <Flags />
          <SplitIndex>0</SplitIndex>
          <CPUSize>1568</CPUSize>
          <GPUSize>0</GPUSize>
          <Unknown7>0</Unknown7>
        </PrimitiveEntry>
      </Primitives>

Then i convert it back to ASM and run Gibbed's updateasm tool to update the sizes and whatnot automatically, for the str2 files that i created. I can see that it recognizes my changes:
Code:
E:\Games\Steam Games (common)\saints row the third>".\Downloads\mod tools\gibbedvolition-rev96\Gibbed.SaintsRow3.UpdateASM.exe" customize_item.asm_pc .
=> 'custmesh_871704894f'
==> 'mary_body_high.cpeg_pc'
==> 'mary_body_high.matlib_pc'
=> 'custmesh_-629684167'
==> 'mary_head_high.cpeg_pc'
==> 'mary_head_high.matlib_pc'
=> 'custmesh_871704894'
==> 'mary_body_high.cpeg_pc'
==> 'mary_body_high.matlib_pc'
=> 'custmesh_-629684167f'
==> 'mary_head_high.cpeg_pc'
==> 'mary_head_high.matlib_pc'

This still crashes. Are there other steps i need to take? Are mary's matlib files no substitute for cyrus's cmesh files?
 
...
This still crashes. Are there other steps i need to take? Are mary's matlib files no substitute for cyrus's cmesh files?


The str2 you grabbed was just the high res textures of the character, which is why it has _high in the name. You need to find the other str2 related to mary(in the preload maybe if not loose?) and that str2 should look very similar to the others. If you're not playing coop(console knowledge, this could be different on PC?) we use the memory from the coop player slot to load high res textures for characters near you. That is what the _high character str2 files are all about.
 
Sorry, after reading back the main difference between NPC's is that their heads are all morphs. So, mary_head_high.cmeshx is not something you would likely ever find. In fact the Cyrus_Head_High.cmeshx is possibly one of a few NPC head meshes in game. The *_head_high.cpegs are still used for applying the texture/materials to the basehead that morphs into the respective NPC morph. I'll break more of this down later with character releases but here's a quick run down.

NPC's
> Body
- gat.cmeshx - Skinned Mesh ( Main Table Reference ) character_definitions.xtbl
- gat.rigx - Skeleton
- gat.cpeg - Cpu Peg
- gat.gpeg - Gpu Peg

> Head
- npc_basehead.cmeshx ( Main Head Mesh For most all NPC's except LawEnforcement - These heads are referenced in the items3d.xtbl )
- gat_head.morphx ( The basehead is attached to the gat.rigx skeleton, which includes the head bones, inheriting bone transforms then it morphs to johnny gats specific morph )
- gat_head.cpeg
- gat_head.gpeg
- gat_head.matlib ( material xml - only used to build the peg )
- gat_head_high.cpeg - High res textures ( used when closest to the player character )
- gat_head_high.gpeg

So as far as getting npc heads on as customization items its likely much more tricky than I first mentioned. We do have a console command in game where we do switch the player to npc - "character gat" This will allow us to debug and control specific npc characters using that character's mesh and associated animation set. There is also a console command to override the animation set that the player is using so that we can set it back to the player. I don't know if these are exposed to Lua functions. It's possible that they are not. It seems like that would be nice to get setup.
 
There is also a console command to override the animation set that the player is using so that we can set it back to the player. I don't know if these are exposed to Lua functions. It's possible that they are not. It seems like that would be nice to get setup.

That sounds ideal to what we're trying to do.
 
Back
Top