Mesh MOPP makes objects fall through

Post » Wed Jul 10, 2013 10:36 pm

Hello!

Long story short - I am making a mod which involes new meshes. Those new meshes need a collision, so I've made a collision and trying to convert it to MOPP using havok sdk and a command-line tool. Problem is , that while collision looks perfect in CK ( fitting etc ) and collisions from 90 degree ( going into a wall etc ) works perfect, flat surfaces you can walk on ( like let's say, a street mesh ) will let me fall through in certain places, like there are holes. Moreover, running havok sim ( i've put quite a few of barrels on the said surface ) went well ( like everything is ok ), but when i saved those barrels position into esp and then loaded into game, they fell thru aswell. I can confirm that vertices and triangles are correct while passed into the mopping function.

Function making the mopp collision is actually injectCollisionData from the Skyfox69 NifUtils ( thank you! ). Snippet:

bool injectCollisionData(vector& geometryMap, bhkMoppBvTreeShapeRef pMoppShape, bhkCompressedMeshShapeDataRef pData){    if (pMoppShape == NULL)   return false;    if (pData      =http://forums.bethsoft.com/topic/1465956-mesh-mopp-makes-objects-fall-through/= NULL)   return false;    if (geometryMap.empty())  return false;    //----  Havok  ----  START    hkpCompressedMeshShape*                    pCompMesh  (NULL);    hkpMoppCode*                            pMoppCode  (NULL);    hkpMoppBvTreeShape*                        pMoppBvTree(NULL);    hkpCompressedMeshShapeBuilder            shapeBuilder;    hkpMoppCompilerInput                    mci;    vector                                geometryIdxVec;    vector                    tMtrlVec;    SkyrimHavokMaterial                        material   (SKY_HAV_MAT_STONE);    int                                        subPartId  (0);    int                                        tChunkSize (0);    //  initialize shape Builder    shapeBuilder.m_stripperPasses = 5000;    //  create compressedMeshShape    shapeBuilder.m_weldVertices = true;//    shapeBuilder.    pCompMesh = shapeBuilder.createMeshShape(0.001f, hkpCompressedMeshShape::MATERIAL_SINGLE_VALUE_PER_CHUNK);    //  set scale        //  add geometries to compressedMeshShape    for (vector::iterator geoIter = geometryMap.begin(); geoIter != geometryMap.end(); geoIter++)    {        size_t        matIdx(0);        //  determine material index        material = (SkyrimHavokMaterial) geoIter->m_triangles[0].m_material;        //  material already known?        for (matIdx=0; matIdx < tMtrlVec.size(); ++matIdx)        {            if (tMtrlVec[matIdx].skyrimMaterial == material)        break;        }        //  add new material?        if (matIdx >= tMtrlVec.size())        {            bhkCMSDMaterial        tChunkMat;            //  create single material            tChunkMat.skyrimMaterial = material;            tChunkMat.skyrimLayer = 1;            //  add material to list            tMtrlVec.push_back(tChunkMat);        }        //  set material index to each triangle of geometry        for (int idx(0); idx < geoIter->m_triangles.getSize(); ++idx)        {            geoIter->m_triangles[idx].m_material = matIdx;        }        //  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.setAbsoluteFitToleranceOfAxisAlignedTriangles( hkVector4( 0.1945f, 0.1945f, 0.1945f ) );    mci.setAbsoluteFitToleranceOfTriangles(0.1945f);    mci.setAbsoluteFitToleranceOfInternalNodes(0.3f);    mci.m_enableChunkSubdivision = true;    pMoppCode   = hkpMoppUtility::buildCode(pCompMesh, mci);    pMoppBvTree = new hkpMoppBvTreeShape(pCompMesh, pMoppCode);        hkpMeshWeldingUtility::computeWeldingInfo(pCompMesh, pMoppBvTree, hkpWeldingUtility::WELDING_TYPE_TWO_SIDED);    //----  Havok  ----  END    //----  Merge  ----  START    hkArray  chunkListHvk;    vector                    chunkListNif = pData->GetChunks();    vector                    tByteVec;    vector                         tVec4Vec;    vector                  tBTriVec;    vector                tTranVec;    map        tMtrlMap;    short                                   chunkIdxNif(0);    //  --- modify MoppBvTree ---    //  set origin    pMoppShape->SetMoppOrigin(Vector3(pMoppBvTree->getMoppCode()->m_info.m_offset(0), pMoppBvTree->getMoppCode()->m_info.m_offset(1), pMoppBvTree->getMoppCode()->m_info.m_offset(2)));    //there was set scale.    pMoppShape->SetMoppScale(pMoppBvTree->getMoppCode()->m_info.getScale());    //  copy mopp data    tByteVec.resize(pMoppBvTree->m_moppDataSize);    tByteVec[0] = pMoppBvTree->m_moppData[pMoppBvTree->m_moppDataSize - 1];    for (hkUint32 i(0); i < (pMoppBvTree->m_moppDataSize - 1); ++i)    {        tByteVec[i+1] = pMoppBvTree->m_moppData[i];    }    pMoppShape->SetMoppCode(tByteVec);    //  set boundings    pData->SetBoundsMin(Vector4(pCompMesh->m_bounds.m_min(0), pCompMesh->m_bounds.m_min(1), pCompMesh->m_bounds.m_min(2), pCompMesh->m_bounds.m_min(3)));    pData->SetBoundsMax(Vector4(pCompMesh->m_bounds.m_max(0), pCompMesh->m_bounds.m_max(1), pCompMesh->m_bounds.m_max(2), pCompMesh->m_bounds.m_max(3)));    //  resize and copy bigVerts    pData->SetNumBigVerts(pCompMesh->m_bigVertices.getSize());    tVec4Vec = pData->GetBigVerts();    tVec4Vec.resize(pData->GetNumBigVerts());    for (unsigned int idx(0); idx < pData->GetNumBigVerts(); ++idx)    {        tVec4Vec[idx].x = pCompMesh->m_bigVertices[idx](0);        tVec4Vec[idx].y = pCompMesh->m_bigVertices[idx](1);        tVec4Vec[idx].z = pCompMesh->m_bigVertices[idx](2);        tVec4Vec[idx].w = pCompMesh->m_bigVertices[idx](3);    }    pData->SetBigVerts(tVec4Vec);    //  resize and copy bigTris    pData->SetNumBigTris(pCompMesh->m_bigTriangles.getSize());    tBTriVec = pData->GetBigTris();    tBTriVec.resize(pData->GetNumBigTris());    for (unsigned int idx(0); idx < pData->GetNumBigTris(); ++idx)    {        tBTriVec[idx].triangle1     = pCompMesh->m_bigTriangles[idx].m_a;        tBTriVec[idx].triangle2     = pCompMesh->m_bigTriangles[idx].m_b;        tBTriVec[idx].triangle3     = pCompMesh->m_bigTriangles[idx].m_c;        tBTriVec[idx].unknownInt1   = pCompMesh->m_bigTriangles[idx].m_material;        tBTriVec[idx].unknownShort1 = pCompMesh->m_bigTriangles[idx].m_weldingInfo;    }    pData->SetBigTris(tBTriVec);    //  resize and copy transform data    pData->SetNumTransforms(pCompMesh->m_transforms.getSize());    tTranVec = pData->GetChunkTransforms();    tTranVec.resize(pData->GetNumTransforms());    for (unsigned int idx(0); idx < pData->GetNumTransforms(); ++idx)    {        tTranVec[idx].translation.x = pCompMesh->m_transforms[idx].m_translation(0);        tTranVec[idx].translation.y = pCompMesh->m_transforms[idx].m_translation(1);        tTranVec[idx].translation.z = pCompMesh->m_transforms[idx].m_translation(2);        tTranVec[idx].translation.w = pCompMesh->m_transforms[idx].m_translation(3);        tTranVec[idx].rotation.x    = pCompMesh->m_transforms[idx].m_rotation(0);        tTranVec[idx].rotation.y    = pCompMesh->m_transforms[idx].m_rotation(1);        tTranVec[idx].rotation.z    = pCompMesh->m_transforms[idx].m_rotation(2);        tTranVec[idx].rotation.w    = pCompMesh->m_transforms[idx].m_rotation(3);    }    pData->SetChunkTransforms(tTranVec);    //  set material list    pData->SetChunkMaterials(tMtrlVec);    //  get chunk list from mesh    chunkListHvk = pCompMesh->m_chunks;    // resize nif chunk list    chunkListNif.resize(chunkListHvk.getSize());    //  for each chunk    for (hkArray::iterator pCIterHvk = pCompMesh->m_chunks.begin(); pCIterHvk != pCompMesh->m_chunks.end(); pCIterHvk++)    {        //  get nif chunk        bhkCMSDChunk&    chunkNif = chunkListNif[chunkIdxNif];            //  set offset => translation        chunkNif.translation.x = pCIterHvk->m_offset(0);        chunkNif.translation.y = pCIterHvk->m_offset(1);        chunkNif.translation.z = pCIterHvk->m_offset(2);        chunkNif.translation.w = pCIterHvk->m_offset(3);        //  force flags to fixed values        chunkNif.materialIndex  = pCIterHvk->m_materialInfo;        chunkNif.unknownShort1  = 65535;        chunkNif.transformIndex = pCIterHvk->m_transformIndex;        //  vertices        chunkNif.numVertices = pCIterHvk->m_vertices.getSize();        chunkNif.vertices.resize(chunkNif.numVertices);        for (unsigned int i(0); i < chunkNif.numVertices; ++i)        {            chunkNif.vertices[i] = pCIterHvk->m_vertices[i];        }        //  indices        chunkNif.numIndices = pCIterHvk->m_indices.getSize();        chunkNif.indices.resize(chunkNif.numIndices);        for (unsigned int i(0); i < chunkNif.numIndices; ++i)        {            chunkNif.indices[i] = pCIterHvk->m_indices[i];        }        //  strips        chunkNif.numStrips = pCIterHvk->m_stripLengths.getSize();        chunkNif.strips.resize(chunkNif.numStrips);        for (unsigned int i(0); i < chunkNif.numStrips; ++i)        {            chunkNif.strips[i] = pCIterHvk->m_stripLengths[i];        }        //  welding        chunkNif.numIndices2 = pCIterHvk->m_weldingInfo.getSize();        chunkNif.indices2.resize(chunkNif.numIndices2);        for (unsigned int i(0); i < chunkNif.numIndices2; ++i)        {            chunkNif.indices2[i] = pCIterHvk->m_weldingInfo[i];        }        //  next chunk        ++chunkIdxNif;    }  //  for (hkArray::iterator pCIterHvk =        //  set modified chunk list to compressed mesh shape data    pData->SetChunks(chunkListNif);        //----  Merge  ----  END    return true;}

I'm really out of ideas, perhaps someone smarter than me can help : /

Thank you very much!

User avatar
Marina Leigh
 
Posts: 3339
Joined: Wed Jun 21, 2006 7:59 pm

Post » Wed Jul 10, 2013 6:25 pm

Not much help, but this is something I'm extremely interested in.

I've been experimenting for the past few days using 3ds to make collisions using the niftool plugin for 3ds.

I made a text tutorial for the method at tesalliance, but they've yet to approve it so click the spoiler at the bottom if you care to read.

Are you familiar with havok? Or is there a tutorial for this method of creating MOPP collisions? mopp isn't included in the 3ds niftools and was something I wanted to experiment with. Another thing I noticed was that my custom meshes don't have havok, so any info you can spare would be well appreciated :icecream:

Spoiler

This tutorial assumes that you have a firm grasp on how to use 3ds and nifskope, and also that you have the proper niftools for 3ds installed.

Note: This written tutorial is based on StarJacker0 's youtube tutorial, which you can find here:

https://www.youtube.com/watch?v=tyrx4iLvkP0

There are a number of differences, notably that according to Neomonkeus:



which you will understand better once you read through the tutorial, for now it means that StarJacker0 's tutorial will make your collision about 7 times larger than your static mesh. But it is still a good idea to watch it for a visual backup to understand what I'm writing. Another thing is that StarJacker0 created a movable object, i believe, and so some of the parameters he set are unnecessary for you if you are creating an unmovable static.

I have not tested this in terms of creating a movable object, but combining the two tutorials, you should have the information necessary to attempt one if you choose.

**Note** Even though this method works well, it may be that there are better ways to do some things. PM me or leave a reply and let me know if you see if anything can be improved. thanks!

1. Create a custom object. In my case I created a wooden ladder.



2. I then created a box primitive shape that encompassed the ladder fully. In another tutorial the author just created a clone of the object he made, which would work as well, but i wanted the lowest cost mesh I could make, so I went with a simple box around the ladder. And name it "bhkConvexVerticesShape" (idk if you need to actually name it this, but the tutorial I watched did name it this)



3. Hit the "R" key to scale or click the Scale button on the options bar at the top. I used the "Select and Non-Uniform scaling option by hitting "R" until it cycled to it. I then right clicked scale button on the top options bar which brought up the "Scale Transform Type-In" box and then reduced all three of the numbers to 30% of what it was on the left hand side. Mine was 100 on all axises, so I changed them all to 30.



4. Convert the box or clone of your object to an editable poly or mesh by right clicking the new collision object you created.



5. Add a bhkRigidBody modifier to the collision box or clone, it is located under the OBJECT-SPACE MODIFIERS section of the modifier drop down.



6. Return to the Create tab, the orange crosshair in the upper right hand corner. Underneath the Create tab header there is a tab called "Helpers" which is in between the cameras tab and the space warps tab. Use the drop down menu to select "Niftools". then select bhkRigidBody from the Object Type menu and then immediately left click directly under and center of your collision box or clone. then hit the "W" hotkey to make sure its center and even with the bottom of the collision box or clone and also center and at the bottom of the original mesh. After left clicking there should be a small little invisible box that's created with the edges labeled with white lines, you may need to toggle the "J" key to see them. (I'm not quite sure you need to align this little box, but another tutorial showed that he did).



7. Make sure your new little invisible box (bhkRigidBody) is selected and click on the "Modify" tab to the right of the "Create" tab. Under the Parameters header, select the type of the object, mine was a wooden ladder so I chose Wood. (Not sure this matters b/c i dont think it exports correctly, we'll change it in nifskope). Then click on the "Add" button under the "Collision Meshes:" box. Make sure you click on the Collison box or clone and not the original mesh. You can also click the add button then immediately click on the "Select by Name" box at the top of the options bar; the little button box with the lines and the mouse pointer, and then find your bhkConvexVerticesShape object, highlight it, then click ok.



8. Once your bhkConvexVerticesShape is added, traverse back over to the right of the screen under the Rigid Body Parameters header, just make sure mass is at 0. and the motion system drop down is 7.Fixed and the Quality Type is 1.Fixed (assuming you are doing a unmovable static, but all these i believe can be changed in nifskope; i did read that funny things happen if you try to export a nif with the mass higher than zero).



9. Now click on your collision box or clone and make sure you are in the "modify" tab. Under the Material drop down select your type, mine was wood again. (again, i dont think this matters that much right now). Under the drop down select Convex Shape in the Bounding Volume radio buttons. Then under the SubShape Properties I put the layer as a static in the drop down.



10. Export all into a nif with Game: Skryim; Export: only Hidden Nodes, Collision ticked; Mesh: only Flatten Hierarchy, Update Tangent Space, Collapse Transforms, Zero Transforms ticked and the weld vertices at .01; Misc: only Sort Nodes ticked; Skin Modifier: shouldn't matter what's ticked or unticked for this; Animations: Nif W/O Animation, Transforms ticked, priority 0.0; And then make sure its a BSFadeNode and Export.



11. Open nifskope, Convert the top NiNode to a BSFadeNode if it isn't already. open up the trees, find the bhkCollisionObject Branch then fully open it up. Find the bhkRigidBodyT node, highlight it and scroll down the bottom box until you locate the "Unknown 7 Shorts" tree and open it up. Make sure and change the bottom four lines to zero. The very bottom and very top should be already set to zero. leaving slot 2 and 3 the only ones filled with the default numbers. (dont ask me why I saw it in the tutorial)



12. From the Unknown 7 Shorts tree, scroll down until you find the Mass line and identify the lines under it until your reach the Penetration Depth line. Those should be 11 lines with the type set a "float", Mass, linear damping, angular damping, etc. These, if i understand right, are for how an object is to be moved. Since I didn't make a moving object, but an unmovable static ladder, I left them pretty much where they were. I played around with them all but none of them made any difference except for the penetration depth, which I changed to .050, just as the tutorial I watched did. I also changed the Max Linear Velocity and the Max Angular Velocity to 1.0, which were set to 200 by default and then also the friction to .3, just for kicks.



13. Underneath Penetration Depth, is the Motion System Line, I set the drop down box to MO_Sys_Fixed, Underneath that is the Deactivator type line, which I set to Deactivator_Never in the drop down box. Underneath that, is the Solver Deactivation line , which i also set to Solver_Deactivation_Off. Lastly, underneath that is the Quality Type; Motion Quality line, which I set to Mo_Qual_Fixed. )another important thing, i think, which should be set right by default is the "Collision Response?" line, mine is set to Response_Simple_Contact



14. Now back into the Block List, Underneath the bhkRigidBodyT node, after you expand it, should be a final node in the tree named, "bhkConvexVerticesShape" (assuming that's what you named your collision box or clone). Once highlighted, under the block details, the top line should be greyed out, and reads Material ; HavokMaterial ; Hav_Mat_Stone. (not sure how to change this or if it even matters) but under this, is the skyrim material line, which by double left clicking under the Value header on the line, you should get a small drop down list of all the materials. I again selected Sky_Hav_Mat_Wood (i believe this is for the sound it makes when you hit it with a sword or something).



15. Underneath the skyrim Material line, should be a line called "Radius"; mine was set to 16.1xxx ; I messed about with this and ended up settling on .2 as the value. I did notice that this affects how big your collision is. and the lowest I could get it down to was .05 before i couldn't find any appreciable effect. but i did leave it at 16.1 and it kept blowing me out of my cell. so try something really small at first and then go up.



16. Make sure your textures are set up and whatnot and save!





**Note** for my ladder the collsion was exactly how I wanted it. You can always adjust the scaling up or down in 3ds if you want a larger or smaller collision, you can also change the radius and also the penetration depth to fine tune how it reacts in game.
User avatar
Marquis deVille
 
Posts: 3409
Joined: Thu Jul 26, 2007 8:24 am


Return to V - Skyrim