Game Development Community

dev|Pro Game Development Curriculum

Applying Materials by Mesh Name

by Joshua Halls (Xerves) · 03/30/2009 (5:58 pm) · 10 comments

Just a note, this is my first resource so please be kind :-). Also, we are using TGEA 1.7.1 right now and not 1.8 so I am not 100% sure if this will work nor am I 100% sure this will work in 1.7.1 as there are a lot of changes in place since we are using the MMO KIT. Please let me know if you run into any issues.

This resource is intended to be used with the Mesh Hiding Resource, but I believe technically it would work without it.

T3d/shapebase.h


The following include needs to be added to the top of the file.

#include "core/tDictionary.h"

Find these following lines of code

CollisionTimeout* mTimeoutList;
    static CollisionTimeout* sFreeTimeoutList;

After it add

//Skin Dictonary for Meshes -- X
   typedef Map<StringTableEntry,StringTableEntry> pDict;
   pDict mSkinDictionary;
   //setMeshSkin function
   void setMeshSkin(StringTableEntry mesh, StringTableEntry texture);

You will next need to find the HideMeshMask define that was created in the Mesh Hiding resource, you need to add another mask. You should change it to look like this (it should

#ifdef _res__hideMeshResource
      HideMeshMask    = Parent::NextFreeMask << 8,
      SkinMeshMask    = Parent::NextFreeMask << 9,
      SoundMaskN      = Parent::NextFreeMask << 10,       ///< Extends + MaxSoundThreads bits

T3D/shapeBase.cpp


Change the following lines in packUpdate

if(!stream->writeFlag(mask & (NameMask | DamageMask | SoundMask |
#ifdef _res__hideMeshResource
         HideMeshMask |
#endif
         ThreadMask | ImageMask | CloakMask | MountedMask | InvincibleMask | ShieldMask | SkinMask)))

To This

if(!stream->writeFlag(mask & (NameMask | DamageMask | SoundMask |
#ifdef _res__hideMeshResource
         HideMeshMask |
#endif
          SkinMeshMask | ThreadMask | ImageMask | CloakMask | MountedMask | InvincibleMask | ShieldMask | SkinMask)))

Then change the following a bit further down

if (stream->writeFlag(mask & (NameMask | ShieldMask | CloakMask | InvincibleMask | SkinMask 
#ifdef _res__hideMeshResource
      | HideMeshMask 
#endif

To This

if (stream->writeFlag(mask & (NameMask | ShieldMask | CloakMask | InvincibleMask | SkinMask | SkinMeshMask
#ifdef _res__hideMeshResource
      | HideMeshMask 
#endif

Further down after this bit of code

#ifdef _res__hideMeshResource
      if (stream->writeFlag(mask & HideMeshMask)) {
         stream->writeInt(mToggledMeshes.size(), 7);
         for(int x = 0; x < mToggledMeshes.size(); x++)
            stream->writeInt(mToggledMeshes[x], 8);
      }
#endif

Add the following

if (stream->writeFlag(mask & SkinMeshMask)) 
       {
          // -- Start Skin changes per Mesh
          stream->write(mSkinDictionary.size());
          for(pDict::Iterator it = mSkinDictionary.begin(); it != mSkinDictionary.end(); it++)
          {
             stream->writeString(it->value); //Key
             stream->writeString(it->key); //Value
          }
          // -- End Skin changes per Mesh
       }

In unpackUpdate after the following Mesh Code

#ifdef _res__hideMeshResource
      if (stream->readFlag())
      { // HideMeshMask
         mToggledMeshes.clear();
         S32 count = stream->readInt(7);
         for(S32 x = 0; x < count; x++)
            mToggledMeshes.push_back(stream->readInt(8));
         //Con::warnf("hiddenMesh::: %d", count);
         updateToggledMeshes();
      }
#endif

Add the following

if (stream->readFlag()) //SkinMeshMask
       {
          U32 meshSkinCnt = 0;
          stream->read(&meshSkinCnt);
          for(S32 i=0; i < meshSkinCnt; i++)
          {
              setMeshSkin(stream->readSTString(),stream->readSTString());
          }
       }

Now at the end of shapebase.cpp add the following block of code.

void ShapeBase::setMeshSkin(StringTableEntry mesh, StringTableEntry texture)
{
   char tex[512];
   const char* matname;
   dStrcpy(tex,texture);
   texture=StringTable->insert(dStrlwr(tex),true);
   if (isGhost())
   {
      // Find the Material name from the Mesh we specified
      for (int i=0; i<mShapeInstance->getShape()->objects.size(); i++)
      {
         S32 nameIndex = mShapeInstance->getShape()->objects[i].nameIndex;
         if (nameIndex>=0)
         {
            //Compare the mesh we specified to the object
            if (!dStricmp(mesh, mShapeInstance->getShape()->getName(nameIndex)))
            {
               TSMesh * imesh = mShapeInstance->mMeshObjects[i].getMesh(0);
               if (mesh)
               {
                  U32 material = imesh->primitives[0].matIndex & TSDrawPrimitive::MaterialMask;
                  if (imesh->primitives[0].matIndex & TSDrawPrimitive::NoMaterial) 
                  {
                     Con::errorf("No material on this mesh");
                     return;
                  } 
                  else 
                  {
                     TSMaterialList * materials = mShapeInstance->getMaterialList();
                     matname = materials->getMaterialName(material);
                     break;
                  }
               }
            }
         }       
      }
      //If we found the material name lets cycle through and find it and set the texture
      if (matname)
      {
         if (!mShapeInstance->ownMaterialList())
            mShapeInstance->cloneMaterialList();

         // Cycle through the materials.
 
         TSMaterialList* pMatList = mShapeInstance->getMaterialList();
         for (S32 j = 0; j < pMatList->mMaterialNames.size(); j++) 
         {
            if (!dStrcmp(pMatList->mMaterialNames[j],matname))
            {
               //Make sure this texture already isn't already applied -- X
               if (mSkinDictionary.contains(mesh) && !dStrcmp(mSkinDictionary[mesh],texture))
               {
                  return;
               }
               if (texture)
               {
                  pMatList->setMaterial(j,texture);
                  pMatList->mapMaterials();
                  mShapeInstance->initMatInstances(); 
               }
            }
         } 
      }
      //Set the value in the dictionary.
      mSkinDictionary[mesh] = texture;
   }
   else
   {
      if( !mSkinDictionary.contains( mesh ) || dStrcmp(mSkinDictionary[mesh],texture))
      {
         mSkinDictionary[mesh] = texture;
         setMaskBits(SkinMeshMask);
      }
   }
}

ConsoleMethod( ShapeBase, setMeshSkin, void, 4, 4, "setMeshSkin(MeshName,texture)")
{
   if (object->isGhost())
      return; //only valid server side

   object->setMeshSkin(StringTable->insert(argv[2]),StringTable->insert(argv[3]));
}

That should be it. Compile and hopefully everything works. As you can see by the Console Method the setMeshSkin call needs to be called from the server side. This will make sure it properly gets communicated over the network the change needs to be made. I don't use TorqueScript for the scripting method as we use Python, so hopefully someone would be better able to fill that in. The process looks like this though.

char.setMeshSkin("myMesh", "~/scriptsAndAssets/data/shapes/player/newMaterial")

About the author

Part of the team that works on The Repopulation, a SciFi based MMO using a heavily modified version of the Torque MMO Kit - T3D. I also take care of the T3D version of the Torque MMO Kit.


#1
03/30/2009 (10:34 pm)
Wow, I can see myself using this in a number of situations. Thanks for sharing!
#2
03/31/2009 (1:15 am)
very useful. ;)
#3
03/31/2009 (7:05 pm)
What if one mesh has got two sub meshes? So your resource should only work on a "1 mesh : 1 material" assumption~
#4
04/01/2009 (9:41 am)
TSMesh * imesh = mShapeInstance->mMeshObjects[i].getMesh(0);
               if (mesh)
               {
                  U32 material = imesh->primitives[0].matIndex & TSDrawPrimitive::MaterialMask;
                  if (imesh->primitives[0].matIndex & TSDrawPrimitive::NoMaterial)

I am guessing that is where those 0s are coming into play for the sub materials. I believe for the most part our models are using 1 material per mesh so it wasn't needed.
#5
05/19/2009 (3:37 pm)
Found an error in this, I updated the original resource, but posting the change if anyone is using it.

TSMaterialList * materials = mShapeInstance->getShape()->materialList;

Should be

TSMaterialList * materials = mShapeInstance->getMaterialList();
#6
05/19/2009 (3:39 pm)
Also, for T3D users.

mShapeInstance->initMatInstances();

Change that to

mShapeInstance->initMaterialList();
#7
06/21/2009 (4:57 am)
Hi, am trying to implement this in 1.8.1 AFX, I get the following error on build:-
Quote:
1>------ Build started: Project: ADV, Configuration: Debug Win32 ------
1>Compiling...
1>shapeBase.cpp
1>d:torqueafx113_combo_tgea181enginesourcet3dshapebase.cpp(4615) : error C2248: 'MaterialList::mMaterialNames' : cannot access protected member declared in class 'MaterialList'
here is the affending code line
for (S32 j = 0; j < pMatList->mMaterialNames.size(); j++)
Quote:
1> d:torqueafx113_combo_tgea181enginesourcematerialsmateriallist.h(80) : see declaration of 'MaterialList::mMaterialNames'

1> d:torqueafx113_combo_tgea181enginesourcematerialsmateriallist.h(21) : see declaration of 'MaterialList'
1>

d:torqueafx113_combo_tgea181enginesourcet3dshapebase.cpp(4617) : error C2248: 'MaterialList::mMaterialNames' : cannot access protected member declared in class 'MaterialList'
1>

d:torqueafx113_combo_tgea181enginesourcematerialsmateriallist.h(80) : see declaration of 'MaterialList::mMaterialNames'
1>

d:torqueafx113_combo_tgea181enginesourcematerialsmateriallist.h(21) : see declaration of 'MaterialList'
1>

d:torqueafx113_combo_tgea181enginesourcet3dshapebase.cpp(4628) : error C2039: 'initMatInstances' : is not a member of 'TSShapeInstance'
1>

d:torqueafx113_combo_tgea181enginesourcetstsshapeinstance.h(78) : see declaration of 'TSShapeInstance'

I'm guessing it's caused by the difference between 1.7.1 & 1.8.1 as there dosn't seem to be any AFX changes in this area that could be intefering, anyone any Ideas?

I know it's proly something real simple, but at the min don't have time to try figure it out, the dog needs walking :-)
#8
06/21/2009 (10:14 am)
Looks like mMaterialNames was moved over from a public variable to a protected one. The easiest thing to do would be be put it back in public. If you go look at the header file you should see it is now in the protected area. The best thing to do would be to find a way to do what is needed in the class so it isn't exposed, but I don't believe that change was even in T3D for some reason. The other error the fix is in the post above for T3D.

--Josh
#9
06/21/2009 (5:37 pm)
ok, moved
Vector<String> mMaterialNames;
out of protected area, plus applied the T3D fix project now compiles and runs.

MeshHide works a treat but when I
d.setmeshskin("body","~/scriptsAndAssets/data/shapes/DesertEagle/gold.body.jpg")
I get echo'd in console log:-
Quote:
==>d.setmeshskin("body","~/scriptsAndAssets/data/shapes/DesertEagle/gold.body.jpg");
Material Info for object Default Material 0 - DE_Normal
[0] Files: shaders/procedural/shaderV_c0010004.hlsl, shaders/procedural/shaderP_c0010004.hlsl Pix Version: 3.00
Material Info for object DEbase - base.body
[0] Files: shaders/procedural/shaderV_c8410004.hlsl, shaders/procedural/shaderP_c8410004.hlsl Pix Version: 3.00
Material Info for object MuzzleFlash - flash
[0] Files: shaders/procedural/shaderV_e0080004.hlsl, shaders/procedural/shaderP_e0080004.hlsl Pix Version: 3.00
Material Info for object DEClip - DEClip
[0] Files: shaders/procedural/shaderV_c8410004.hlsl, shaders/procedural/shaderP_c8410004.hlsl Pix Version: 3.00
Material Info for object DEShell - DEShell
[0] Files: shaders/procedural/shaderV_c8410004.hlsl, shaders/procedural/shaderP_c8410004.hlsl Pix Version: 3.00
Material Info for object Default Material 1 - Desert Eagle
[0] Files: shaders/procedural/shaderV_c0010004.hlsl, shaders/procedural/shaderP_c0010004.hlsl Pix Version: 3.00
and nothing happens..
I have tried with several different shapes with from 1 to many meshes and the results are the same, no change.

have you come across anything like this before Josh?

ok, trying on a fresh install, mine is pretty heavily modified, so is possible something else is intefering, I hope its not something to do with the 1.8.1 only able to apply 1 skin change bug..

will keep posted
#10
06/22/2009 (9:51 am)
ok, fresh install of 1.8.1 AFX and same problem, mesh skin dosn't change, I even commented out the if isghost return bit just incase but no joy.

will have a hammer at this later see if I can find the problem. But am begining to think it's 1.8.1 :-(