Game Development Community

Does setSkinName work for anyone?

by Juan Aramburu · in Torque Game Engine Advanced · 07/13/2006 (2:18 pm) · 12 replies

If you have 3 skins for a StaticShape, say base, blue, and red:

are you able to do an
%obj.setSkinName(red); //changes to red
%obj.setSkinName(blue); //changes to blue


Can someone please verify if they can do (multiple) setSkinName/s in TSE?

#1
07/14/2006 (2:04 pm)
Ok, TSE milestone 3.5 now requires all materials to be defined in a materials.cs file. If a material is not defined the error texture will show on you shapes. I have rewritten the reskin functionality to work with the latest TSE milestone 3.5. I have expanded it's functionality to allow you to change a material to any material defined in any of your materials.cs files. You can switch back and fourth as many times as you want. Here's the code changes:

Open ts/tsShapeInstance.h and look for the following line:
void reSkin(StringHandle& newBaseHandle);
Change it to:
void reSkin(StringHandle& oldMaterial,StringHandle& newMaterial);
I added an arg and changed the names of the agrs to make more sense. Save this file.

Open ts/tsShapeInstance.cpp and look for the TSShapeInstance::reSkin function and change it to:
void TSShapeInstance::reSkin(StringHandle& oldMaterial,StringHandle& newMaterial)
{
//BCS - Swaps skin on shapebase and static objects
const char* MatName = null;
const char* newMatName = null;
      if (oldMaterial.isValidString() && newMaterial.isValidString()) {
      	MatName = oldMaterial.getString();
	newMatName = newMaterial.getString();
      	}
   if (MatName == NULL || newMatName == NULL){return;}
   if (ownMaterialList() == false) {cloneMaterialList();}
   // Cycle through the materials.
   TSMaterialList* pMatList = getMaterialList();
   for (S32 j = 0; j < pMatList->mMaterialNames.size(); j++)
  {
      const char* pName = pMatList->mMaterialNames[j];
      if (pName == NULL){continue;}
      if(!dStricmp(pName,MatName))
      {
         MatInstance * matInst = pMatList->getMaterialInst(j);
         if(!matInst->swapMaterial(newMatName)){
	Con::errorf("reSkin failed. Material %s not found.",newMatName);
         }
      }
   }
}
Save this file. This function calls swapMaterial() which we need to create in the materials/matInstance.h

Open materials/matInstance.h and add the following around line 110 in the public section of the class:
//BCS - added for material swap
	bool swapMaterial(const char*);
Save this file.

Open materials/matInstance.cpp and add the following function to the bottom of the file:
//BCS - added for material swap
bool MatInstance::swapMaterial(const char* newName)
{
	Material * myMat;
	//if we find the new material swap it out
	if(!Sim::findObject(newName, myMat))
	{
		return false;
	}
	else
	{
		mMaterial = myMat;
		reInit();
		return true;
	}
	myMat = NULL;
}
Save this file.

Open game/ShapeBase.h and add the following line around line 709 right after the mSkinNameHandle line:
//BCS - added for material swap
	StringHandle     mNewSkinNameHandle;
We need a second argument for updating the skin over the network. That's what this is used for.

Search for setSkinName(const char*); and change it to:
void setSkinName(const char*,const char*);
Again we need that second arg. Save this file.

Open game/ShapeBase.cpp and look for the setSkinName console method. It's towards the bottom of the file. Change it to this:
ConsoleMethod( ShapeBase, setSkinName, void, 4, 4, "(oldMaterialName,newMaterialName)")
{
   object->setSkinName(argv[2],argv[3]);
}
All we did was add a second arg. This allows us to pass the old and new material name.

Search for the ShapeBase::setSkinName function and change it to:
void ShapeBase::setSkinName(const char* name,const char* newSkinName)
{
   if (!isGhost()) {
      if (name[0] != '[[4f3ed0acc984e]]') {
         // Use tags for better network performance
         // Should be a tag, but we'll convert to one if it isn't.
         if (name[0] == StringTagPrefixByte) {
            mSkinNameHandle = StringHandle(U32(dAtoi(name + 1)));
         }
         else {
            mSkinNameHandle = StringHandle(name);
         }
      }
      else {
         mSkinNameHandle = StringHandle();
      }
      if (newSkinName[0] != '[[4f3ed0acc984e]]') {
         // Use tags for better network performance
         // Should be a tag, but we'll convert to one if it isn't.
         if (newSkinName[0] == StringTagPrefixByte) {
            mNewSkinNameHandle = StringHandle(U32(dAtoi(newSkinName + 1)));
         }
         else {
            mNewSkinNameHandle = StringHandle(newSkinName);
         }
      }
      else {
         mNewSkinNameHandle = StringHandle();
      }
      setMaskBits(SkinMask);
   }
}
All we did was add the second arg that is passed to us from the console function.

Now find the ShapeBase::packUpdate function and add the following around line 2864 right under con->packStringHandleU(stream, mSkinNameHandle);
con->packStringHandleU(stream, mNewSkinNameHandle);
This packs our second argument for network update.

Now find the ShapeBase::unpackUpdate function and replace the existing SkinMask unpack with this one(around line 3040):
//BCS - changed for material swap
      if (stream->readFlag()) {  // SkinMask
         StringHandle skinDesiredNameHandle = con->unpackStringHandleU(stream);
	   StringHandle newskinDesiredNameHandle = con->unpackStringHandleU(stream);
         if (mSkinNameHandle != newskinDesiredNameHandle) {
            mSkinNameHandle = newskinDesiredNameHandle;
            if (mShapeInstance) {
               mShapeInstance->reSkin(skinDesiredNameHandle,newskinDesiredNameHandle);
               if (mSkinNameHandle.isValidString()) {
                  mSkinHash = _StringTable::hashString(mSkinNameHandle.getString());
               }
            }
         }
      }
Again, this unpacks two arguments instead of one.

Now find the reSkin function around line 852 and change it to:
mShapeInstance->reSkin(mSkinNameHandle,mSkinNameHandle);
Save this file.

Open game/ShapeImage.cpp and look for the reSkin function around line 1291. Change it to:
image.shapeInstance->reSkin(image.skinNameHandle,skinNameHandle);
Search again for the reSkin function around line 1333 and change it to:
image.shapeInstance->reSkin(skinNameHandle,skinNameHandle);
Save this file and compile.

To use this simply specify the orginal material and the new material.

Obj.setSkinName(original_Material_Name, New_Material_Name);

The original material name will not change even after swapping materials. For example, to swap the Orc_Material with the OrcEye material and back we would do the following:
$Game::Player.setSkinName(Orc_Material,OrcEye);
$Game::Player.setSkinName(Orc_Material, SpaceOrc);

I designed it this way so that you wouldn't have to keep track of the names as you swap them out. The original name is the original mapTo name. The new name is the name of the new material
new Material(OrcEye)
{
baseTex[0] = "~/data/shapes/spaceOrc/orc_ID6_eye";
emissive[0] = true;
glow[0] = true;
};
#2
07/14/2006 (2:07 pm)
This works for shapebase objects. I will be submitting code for material swapping on static shapes and interiors this weekend. It simply builds upon this foundation.
#3
07/14/2006 (2:16 pm)
Thanks, will try it out!

In the meantime, I just created three separate dts's & corresponding datablocks using those dts's. Then when I want to 'reskin' I just call %obj.setDataBlock();

Also milestone 3.5 is the stuff that was just released, correct? So it won't work with the release before that? Cause we have yet to port all of our existing code into 3.5 (if that is the newest one that was just checked in).
#4
07/14/2006 (8:07 pm)
The code above will work with milestone 3.0 but the same rules apply(i.e. materials should be defined in a materials.cs file and can only swap between them not between the fixed function textures).
#5
07/17/2006 (11:55 am)
Tried it out but I'm getting a "null ghost encountered" error. These are StaticShapes so maybe that's why?
#6
07/17/2006 (6:46 pm)
OK, apply this code in addition to the shapebase code above to allow material swapping on static shapes.

Open game/tsStatic.h
add the following around lin e18. right after the resmanager stuff
//BCS - added for swap material
#ifndef _NETSTRINGTABLE_H_
#include "sim/netStringTable.h"
#endif

now add this around line 93 right after Vector mLOSDetails;
//BCS - added for material swap
   U32 mSkinHash;
   StringHandle     mSkinNameHandle;
   StringHandle     mNewSkinNameHandle;

now add this to the end of the class right after unpackUpdate(line 115)
//BCS - added for swap material 
   enum MaskBits {
      SkinMask        = Parent::NextFreeMask,
      NextFreeMask    = Parent::NextFreeMask  << 1
   };
   void setSkinName(const char*,const char*);
Save this file.

Open game/tsStatic.cpp. add the following to the end of the file:
//BCS - added for material swap
void TSStatic::setSkinName(const char* name,const char* newSkinName)
{
	// this method gets the skin names ready for network passage
   if (!isGhost()) {
      if (name[0] != '[[4f3ed0ad36049]]') {
         if (name[0] == StringTagPrefixByte) {
            mSkinNameHandle = StringHandle(U32(dAtoi(name + 1)));
         }
         else {
            mSkinNameHandle = StringHandle(name);
         }
      }
      else {
         mSkinNameHandle = StringHandle();
      }
      if (newSkinName[0] != '[[4f3ed0ad36049]]') {
         if (newSkinName[0] == StringTagPrefixByte) {
            mNewSkinNameHandle = StringHandle(U32(dAtoi(newSkinName + 1)));
         }
         else {
            mNewSkinNameHandle = StringHandle(newSkinName);
         }
      }
      else {
         mNewSkinNameHandle = StringHandle();
      }
      setMaskBits(SkinMask);
   }
}
//BCS - added for swap material
ConsoleMethod( TSStatic, setSkinName, void, 4, 4, "(materialName,newMaterialName)")
{
   object->setSkinName(argv[2],argv[3]);
}

find the TSStatic::packUpdate method and add this right before return retMask;
//BCS - added for material swap
   if (stream->writeFlag(mask & SkinMask)) {
      con->packStringHandleU(stream, mSkinNameHandle);
      con->packStringHandleU(stream, mNewSkinNameHandle);
   }

Now find the TSStatic::unpackUpdate method and add this to the end of the methos before the closing bracket
mShapeName = stream->readSTString();
   //BCS - added for material swap
	if (stream->readFlag()) {  // SkinMask
      StringHandle skinDesiredNameHandle = con->unpackStringHandleU(stream);
		StringHandle newskinDesiredNameHandle = con->unpackStringHandleU(stream);
      if (mSkinNameHandle != newskinDesiredNameHandle) {
         mSkinNameHandle = newskinDesiredNameHandle;
         if (mShapeInstance) {
            mShapeInstance->reSkin(skinDesiredNameHandle,newskinDesiredNameHandle);
            if (mSkinNameHandle.isValidString()) {
               mSkinHash = _StringTable::hashString(mSkinNameHandle.getString());
            }
         }
      }
   }
Save and compile.

To use this simply specify the orginal material and the new material.

Obj.setSkinName(original_Material_Name, New_Material_Name);

I designed it so you wouldn't have to keep track of the names as you swap them out. The original name is the original mapTo name. The new name

is the name of the new material. This works the same as the shapebase method above.
#7
07/17/2006 (10:17 pm)
Thank you again. Will see what happens.
#8
07/20/2006 (6:42 pm)
Bryan Stroebel, just wanted to say thankyou again; the second half of the code worked perfectly. At first I had a little trouble, but that was just because it seems:

#1) the base material filename (as defined in the dts) has to have the same name as the new Material() declaration.

#2) I got confused and thought that the two parameters to setSkinName were old_material and new_material (meaning I would have to keep track of the old in order to change to the new). But then I reread what you posted and see that the "old_material" parameter = original_material = material filename as defined in the dts.

Edit: mention of miniscule issues that are now updated in Bryan's original post(s).

I found that entering the mission/world editor (don't remember if it was f10 or f11, but one of em) seems to mess with something and both the old & new handle strings are failing the isValidString()

PS: The first error that I said I posted: that was my fault; your 10th code box which adds to the packUpdate, I had overwrote the existing line with the one you posted, instead of throwing your line under it. So I was essentially 'packing' one stringhandle but trying to unpack two.

Thanks again, works perfectly. The fact that it uses materials is very advantageous.
#9
07/20/2006 (7:22 pm)
@Juan - thanks. I meant to initialize those variables..... I guess I forgot. I edited my previous post to update the code. Thanks.
#10
07/30/2006 (9:35 pm)
Bryan,

Do you already have a version of the reSkin() method that can swap materials for interior shapes as well?

John K.
#11
07/31/2006 (8:12 pm)
@John - yes. It's a resource. But I haven't updated it or check to see if it works with the latest build. I will check tomorrow and update it.
#12
08/13/2006 (6:57 pm)
Thanks for your work on this.