wz

bhkMoppBvTreeShape

131 posts in this topic

OPCODE 0x70 is a GOTO command. The param is actually a DWORD which is added to base address of MOPP and the mopp code execution jumps there:

 

EX:

136F2D30 base addr of mopp code in memory

 

command: 70 00 00 0a 2a:

 

Code execution will jump to 136F2D30 + 00000a2a = 136F375A

 

This is confirmed for Skyrim.

Share this post


Link to post
Share on other sites

Hey, cool! Thanks for the information. I'll adapt my Tool MoppDecoder - will be part of next version of NifUtilsSuite

Share this post


Link to post
Share on other sites

According to ttl269s post here I developed a first theory here. Thanks to jonwd7 I had a chance to look into several PS3-Skyrim-NIFs and found the theory proofed.

 

Swapping "Mopp Code" and "unknown byte" leads to the byte being always 01 for PC-NIFs and 00 for PS3-NIFs. By this I think we could rename "unknown byte" to "build type" (or something more intuitive) as implemented by Havok:

 

enum BuildType
{
BUILT_WITH_CHUNK_SUBDIVISION,
BUILT_WITHOUT_CHUNK_SUBDIVISION,
BUILD_NOT_SET
};

hkEnum m_buildType; //+default(hkpMoppCode::BUILT_WITHOUT_CHUNK_SUBDIVISION)

 

PC-NIFs are always generated with BUILT_WITHOUT_CHUNK_SUBDIVISION and PS3-NIFs 'for spu processing' with BUILT_WITH_CHUNK_SUBDIVISION.

Share this post


Link to post
Share on other sites

So might chunk subdivision be why most of the time my output collision contains more chunks than the input collision? This was not the case back before NifUtilsSuite (when it was just ChunkMerge).

 

For example I will import old collision, fix something about the collision, then export the collision. The original will have, say, 3 NiTriShapes named SKY_HAV_MAT_METAL... the modified collision I export will have the same. But when I ChunkMerge it into the original NIF, it may have 3 bhkCMSDChunks and 1 bhkCMSDBigTris. That is a best case scenario, sometimes the # of Chunks blows up.

 

In the past I believe the Chunk # would always match exactly the NiTriShape # I fed it.

 

Recently, while fixing the collision on that one mesh I sent you (the crashy one, and the patched version), the number of chunks became pretty high. Like 19 or so even though the original had <10. Weirder still if I would combine the collision mesh into one large shape / material, I would end up with 37 chunks instead.

Share this post


Link to post
Share on other sites

Sorry, but I don't think that MOPP data build type is responsible for creating a lot of chunks in bhkCompressedMeshShapeData. In fact, bhkCompressedMeshShapeData is created before MOPP data 'cause last depends on first. Here's a snippet of the code:

 

// create compressedMeshShape
pCompMesh = shapeBuilder.createMeshShape(0.001f, hkpCompressedMeshShape::MATERIAL_SINGLE_VALUE_PER_CHUNK);

// add geometries to compressedMeshShape
for (vector::iterator geoIter = geometryMap.begin(); geoIter != geometryMap.end(); geoIter++)
{       
   [...]

   // add geometry to shape
   subPartId = shapeBuilder.beginSubpart(pCompMesh);
   shapeBuilder.addGeometry(*geoIter, hkMatrix4::getIdentity(), pCompMesh);
   shapeBuilder.endSubpart(pCompMesh);
   shapeBuilder.addInstance(subPartId, hkMatrix4::getIdentity(), pCompMesh);

} // for (vector::iterator geoIter = geometryMap.begin(); geoIter != geometryMap.end(); geoIter++)

// create welding info
mci.m_enableChunkSubdivision = true; 
pMoppCode = hkpMoppUtility::buildCode(pCompMesh, mci);
pMoppBvTree = new hkpMoppBvTreeShape(pCompMesh, pMoppCode);
hkpMeshWeldingUtility::computeWeldingInfo(pCompMesh, pMoppBvTree, hkpWeldingUtility::WELDING_TYPE_TWO_SIDED);

As you could see each found geometry (from NiTriStrips or old style collision data) is added to bhkCompressedMeshShapeData. Diving them into chunks is part of Havoks 'black box' magic. As written in Havoks documentation one step of this is splitting convex geometries into non convex ones. May be this could be the reason why you discover a high number of chunks?

 

There's one parameter which could influence the number:

 

// initialize shape Builder
shapeBuilder.m_stripperPasses = 5000;

I used a default value and had never time to 'play' with it. Might be time to do so....

Share this post


Link to post
Share on other sites

Mega necro bump, but I felt it necessary to share the findings we've made in Discord.

TL;DR - It's honestly easier to simply remove the MoppBvTreeShape from the hierarchy and use the bhkCompressedShape/bhkNiTriStripsShape/etc. directly.

Have only tested FO3 to any degree so far, but for Skyrim the collision still shows up correctly in Preview when extracting the BvTreeShape from the hierarchy.   I have a hard time seeing any downsides as Havok's MOPP has always been explicitly and solely about memory reduction for consoles circa PS2/Wii era.   The MOPP is actually a program which requires a virtual machine to run it.   So each shape gets a program stored alongside it and this program has to run for all collision detection.  So depending on shape complexity it's possible that having no MOPP at all is actually better.  The MOPP is a binary tree however.  So maybe on more complex shapes it does accelerate collision detection instead of causing more CPU cycles.  I think I am going to try to test this out by batch removing all MOPP and seeing what happens to CPU % and FPS.

For anyone curious I have also found the actual opcodes enum:

enum hkpMoppCommands
{
  HK_MOPP_RETURN = 0x0,
  HK_MOPP_SCALE1 = 0x1,
  HK_MOPP_SCALE2 = 0x2,
  HK_MOPP_SCALE3 = 0x3,
  HK_MOPP_SCALE4 = 0x4,
  HK_MOPP_JUMP8 = 0x5,
  HK_MOPP_JUMP16 = 0x6,
  HK_MOPP_JUMP24 = 0x7,
  HK_MOPP_JUMP32 = 0x8,
  HK_MOPP_TERM_REOFFSET8 = 0x9,
  HK_MOPP_TERM_REOFFSET16 = 0xA,
  HK_MOPP_TERM_REOFFSET32 = 0xB,
  HK_MOPP_JUMP_CHUNK = 0xC,
  HK_MOPP_DATA_OFFSET = 0xD,
  HK_MOPP_SPLIT_X = 0x10,
  HK_MOPP_SPLIT_Y = 0x11,
  HK_MOPP_SPLIT_Z = 0x12,
  HK_MOPP_SPLIT_YZ = 0x13,
  HK_MOPP_SPLIT_YMZ = 0x14,
  HK_MOPP_SPLIT_XZ = 0x15,
  HK_MOPP_SPLIT_XMZ = 0x16,
  HK_MOPP_SPLIT_XY = 0x17,
  HK_MOPP_SPLIT_XMY = 0x18,
  HK_MOPP_SPLIT_XYZ = 0x19,
  HK_MOPP_SPLIT_XYMZ = 0x1A,
  HK_MOPP_SPLIT_XMYZ = 0x1B,
  HK_MOPP_SPLIT_XMYMZ = 0x1C,
  HK_MOPP_SINGLE_SPLIT_X = 0x20,
  HK_MOPP_SINGLE_SPLIT_Y = 0x21,
  HK_MOPP_SINGLE_SPLIT_Z = 0x22,
  HK_MOPP_SPLIT_JUMP_X = 0x23,
  HK_MOPP_SPLIT_JUMP_Y = 0x24,
  HK_MOPP_SPLIT_JUMP_Z = 0x25,
  HK_MOPP_DOUBLE_CUT_X = 0x26,
  HK_MOPP_DOUBLE_CUT_Y = 0x27,
  HK_MOPP_DOUBLE_CUT_Z = 0x28,
  HK_MOPP_DOUBLE_CUT24_X = 0x29,
  HK_MOPP_DOUBLE_CUT24_Y = 0x2A,
  HK_MOPP_DOUBLE_CUT24_Z = 0x2B,
  HK_MOPP_TERM4_0 = 0x30,
  HK_MOPP_TERM4_1 = 0x31,
  HK_MOPP_TERM4_2 = 0x32,
  HK_MOPP_TERM4_3 = 0x33,
  HK_MOPP_TERM4_4 = 0x34,
  HK_MOPP_TERM4_5 = 0x35,
  HK_MOPP_TERM4_6 = 0x36,
  HK_MOPP_TERM4_7 = 0x37,
  HK_MOPP_TERM4_8 = 0x38,
  HK_MOPP_TERM4_9 = 0x39,
  HK_MOPP_TERM4_A = 0x3A,
  HK_MOPP_TERM4_B = 0x3B,
  HK_MOPP_TERM4_C = 0x3C,
  HK_MOPP_TERM4_D = 0x3D,
  HK_MOPP_TERM4_E = 0x3E,
  HK_MOPP_TERM4_F = 0x3F,
  HK_MOPP_TERM4_10 = 0x40,
  HK_MOPP_TERM4_11 = 0x41,
  HK_MOPP_TERM4_12 = 0x42,
  HK_MOPP_TERM4_13 = 0x43,
  HK_MOPP_TERM4_14 = 0x44,
  HK_MOPP_TERM4_15 = 0x45,
  HK_MOPP_TERM4_16 = 0x46,
  HK_MOPP_TERM4_17 = 0x47,
  HK_MOPP_TERM4_18 = 0x48,
  HK_MOPP_TERM4_19 = 0x49,
  HK_MOPP_TERM4_1A = 0x4A,
  HK_MOPP_TERM4_1B = 0x4B,
  HK_MOPP_TERM4_1C = 0x4C,
  HK_MOPP_TERM4_1D = 0x4D,
  HK_MOPP_TERM4_1E = 0x4E,
  HK_MOPP_TERM4_1F = 0x4F,
  HK_MOPP_TERM8 = 0x50,
  HK_MOPP_TERM16 = 0x51,
  HK_MOPP_TERM24 = 0x52,
  HK_MOPP_TERM32 = 0x53,
  HK_MOPP_NTERM8 = 0x54,
  HK_MOPP_NTERM16 = 0x55,
  HK_MOPP_NTERM24 = 0x56,
  HK_MOPP_NTERM32 = 0x57,
  HK_MOPP_PROPERTY8_0 = 0x60,
  HK_MOPP_PROPERTY8_1 = 0x61,
  HK_MOPP_PROPERTY8_2 = 0x62,
  HK_MOPP_PROPERTY8_3 = 0x63,
  HK_MOPP_PROPERTY16_0 = 0x64,
  HK_MOPP_PROPERTY16_1 = 0x65,
  HK_MOPP_PROPERTY16_2 = 0x66,
  HK_MOPP_PROPERTY16_3 = 0x67,
  HK_MOPP_PROPERTY32_0 = 0x68,
  HK_MOPP_PROPERTY32_1 = 0x69,
  HK_MOPP_PROPERTY32_2 = 0x6A,
  HK_MOPP_PROPERTY32_3 = 0x6B,
  HK_MOPP_JUMP_CHUNK32 = 0x70,
};

So the unknown opcodes `0x01` though `0x04` have something to do with scale.

I will be working on understanding MOPP for a while even though I'm feeling like the best course of action is to simply strip it.   If I find anything else pertinent I will share it here.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!


Register a new account

Sign in

Already have an account? Sign in here.


Sign In Now