Game Development Community

Dynamic Skins and Material Swap Issue

by game4Rest · in Torque 3D Professional · 10/30/2009 (11:20 pm) · 26 replies

I started discussion on this topic from here. But to catch with the changes, and as a response to a request, I decided to surmarize what I've been doing and ask some help.

Before I go any further, I'd like to make it clear that this worked OK just until T3D beta 4 release.Currently, according to the material instance's vertext format, it gives me different assertFatal messages.
In the TSMaterialList::setMaterial that I'm going to add below, when I chose to init matInst like this,
matInst->init(features, getGFXVertexFormat<GFXVertexPNT>()), it says that "Not enough room in the buffer for this data!".
But when I initilize it like this way, matInst->init(features, getGFXVertexFormat<GFXVertexPTTT>()),
it says that "Something went bad with ShaderGen. The normal should be already defined.".
If I comment out those assertFatal lines, I can see that skins are changed. But when I quit the game, as we can expect, it crashes my game hard.
It seems like it has something to do with shader thing. But I don't have enough knowledge about it. So anyone please let me know the cause of this problem and shed some light on me?

Thanks in advance.

Here goes code changes that I made to make original resource work in T3D 1.0.

In the source/ts/tsShapeInstance.h, after
class SceneState;
add this.
//HJ: dynamic skins and material swap
class DynamicSkinData   
{   
public :   
	Vector<StringTableEntry> mTextures; // texture layers to blend together   
	StringTableEntry mMaterialName;   
	StringTableEntry mSkinTag;   
}; 
//end:

And after
void reSkin( String newBaseName, String oldBaseName = String::EmptyString );

add this.
//HJ: dynamic skin and material swap
   void SetDynamicSkin(DynamicSkinData* data);
   //end:

Next, at the end of source/ts/tsShapeInstance.cpp,
add this,
//HJ: dynamic skins and material swap
void TSShapeInstance::SetDynamicSkin(DynamicSkinData* data)
{
	//First, check if the skin tag(materialName) exsits in our matInstance list.
	//if it doesn't we don't do anything as this is incorrect skin data.
	//(can't replace a skin we don't have)
	TSMaterialList* pMatList = getMaterialList();

	S32 foundMatInstIndex = -1;
	for(S32 j=0; j < pMatList->mMaterialNames.size(); j++)
	{
		const char* pName = pMatList->mMaterialNames[j];
		if(pName == NULL)
			continue;
		if(0==dStricmp(pName, data->mSkinTag))
		{
			foundMatInstIndex = j;
			break;
		}
	}

	if(-1 == foundMatInstIndex)
		return;

	//Now check if the material mapped to this skin tag matches the one we want to map to it.
	//if it does, then our work is already done and we can simply return
	BaseMatInstance* matInst = pMatList->getMaterialInst(foundMatInstIndex);   

	Material* currentMat = NULL;   
	if (matInst) {   
		currentMat = dynamic_cast<Material*>(matInst->getMaterial());   
	}   
	Material *mat = dynamic_cast<Material*>( Sim::findObject(data->mMaterialName) );  
	if(mat)
	{
		//it's already mapped at the current material
		if(mat == currentMat)
			return;

		//The material already exists. so lets just map it to this matInst/skin tag slot
		pMatList->setMaterial(foundMatInstIndex, mat);
	}
	else
	{
		//Oh dear, the material doesn't exist. So we have to create it then map it to the matInst/skin
		//tag slot.
		//the trick to creating a new material is that we are going to copy the details of a
		//"Template" material. The material we choose to copy is based on how many textures we are blending.
		//If 3, then we choose the material that is set up to blend 3 textures, etc. That way designers can 
		//control EXACTLY how these materials operates without needing to recompile code.

		S32 maxNumTextures = Con::getIntVariable("$pref::DynamicSkins::MaxBlendedTextures");

		//Clamp the number of textures, for sanity sake
		S32 numBlendedTextures = data->mTextures.size();
		if(numBlendedTextures > maxNumTextures)
			numBlendedTextures = maxNumTextures;

		//Read the name of the desired material template from prefs
		char materialTemplate[512];
		dSprintf(materialTemplate, sizeof(materialTemplate), "$pref::DynamicSkins::MaterialTemplate_%d", numBlendedTextures);

		const char* templateName = NULL;
		templateName = Con::getVariable(materialTemplate);

		//if we couldn't read the name, return
		if(!templateName)
			return;

		//We're going for custom materials here. If players want normal materials that option will need to
		//be added in code.
		CustomMaterial* templateMat = dynamic_cast<CustomMaterial*>(Sim::findObject(templateName));
		if(!templateMat)
			return;

		//We found it, so let's copy it
		CustomMaterial* newMat = new CustomMaterial();
		*newMat = *templateMat;

		//Now change the variables we want on our copy.
		newMat->assignName(data->mMaterialName);
		//don't make the "mapTo" equal to the skin tag. We don't want all objects which have a "Head" texture(skin tag)
		//using our dynamic material for instance. Then ALL of them would have the same material as "Head"
		newMat->mMapTo = StringTable->insert(data->mMaterialName);

		//replace textures
		for(S32 i=0; i< data->mTextures.size(); i++)
			newMat->mTexFilename[i] = data->mTextures[i];
		AssertFatal(newMat->registerObject(), "Unable to register dynamic material!");
		Sim::getRootGroup()->addObject(newMat);

		pMatList->setMaterial(foundMatInstIndex, newMat);

	}
}

Now open up file source/ts/tsShape.h.
Add this.
[code]
//HJ: dynamic skin and material swap
#ifndef _MATERIAL_H_   
#include "materials/materialDefinition.h"   
#endif


#ifndef _CUSTOMMATERIAL_H_
#include "materials/customMaterialDefinition.h"
#endif
//end:
[/code]

Still in the same file,after
void push_back(const char * name, U32 flags, Material* mat);
add this.
//HJ: dynamic skins and material swap
   bool setMaterial(U32 index, Material* newMat);
   //end:

Now let's move to the next file source/ts/tsMaterialList.cpp.
At first, add this line.
#include "materials/materialFeatureTypes.h"   //HJ: beta 5. dynamic skins and material swap

And then, add this problematic method.
//HJ: dynamic skins and material swap
bool TSMaterialList::setMaterial(U32 index, Material* newMat)   
{   
	if (index < 0 || index > mMaterials.size()) {   
		return false;   
	}   
	if ( newMat ) {   
		if (mMatInstList[index]->getMaterial() == newMat) {   
			return true;   
		}   
		// dump the old mat instance   
		if (mMatInstList[index]) {   
			delete mMatInstList[index];   
		}   
		mMatInstList[index] = NULL;   

		// change texture   
		CustomMaterial* cust = dynamic_cast<CustomMaterial*>(newMat);   
		String texPath;   
		if (cust) {   
			texPath = cust->mTexFilename[0];   
		} else{   
			texPath = newMat->mBaseTexFilename[0];   
		}   

		GFXTexHandle tex = GFXTexHandle(texPath, &GFXDefaultStaticDiffuseProfile, avar("%s() - NA (line %d)", __FUNCTION__, __LINE__));   
		if (!tex.isValid()) {   
			return false;   
		}   
		// change texture   
		mMaterials[index]   = tex;   

		BaseMatInstance *matInst = newMat->createMatInstance();   
		mMatInstList[index] = matInst;   


		// GFX2_RENDER_MERGE    
		//HJ: changed from original to work with T3D   
		    
		//fd.features[MFT_RTLighting] = true;				//beta 5: commented out by deepscratch's solution
		FeatureSet features = MATMGR->getDefaultFeatures(); 
		//GFXVertexPNT *tsVertex = NULL;
		features.addFeature( MFT_VertTransform );
		  		features.addFeature( MFT_DiffuseMap );
				features.addFeature( MFT_RTLighting);
				features.addFeature(MFT_NormalMap);
		  		features.addFeature( MFT_OverlayMap );
		  		features.addFeature( MFT_DetailMap );
		  		features.addFeature( MFT_DiffuseColor );
		  		features.addFeature( MFT_ColorMultiply );
		  		features.addFeature( MFT_AlphaTest );
		  		features.addFeature( MFT_IsTranslucent );

		//MaterialFeatureData fd;
		//fd.features = features;
		//fd.materialFeatures = features;

		matInst->init(features, matInst->getVertexFormat());// getGFXVertexFormat<GFXVertexPTTT>() );     
		//end:
	}   
	return true;   
}   
//end:
I think the code right above is causing the problem that I have. But anyway, let's move to the next file.

In the source/T3D/shapeBase.h.
add this.
//HJ: dynamic skins and material swap
#ifndef _TSSHAPEINSTANCE_H_
#include "ts/tsShapeinstance.h"
#endif
//end:

After this,
[code]
friend void physicalZoneFind(SceneObject*, void*);
Add these.
//HJ: dynamic skins and material swap
protected :   
	Vector<DynamicSkinData> mDynamicSkins;    

	void BuildDynamicSkins();   
	void ClearDynamicSkins(); 
	//end:

After
SoundMaskN      = Parent::NextFreeMask << 9,

Add this,
DynamicSkinMask = Parent::NextFreeMask << 10,	//HJ: dynamic skins and material swap

Still in the same file, after
const char* getSkinName();
   /// @}
Add these methods.
//HJ: dyanmic skin and material swap
   void UpdateDynamicSkins();
   bool AddDynamicSkinLayerTexture(const char* skinTag, const char* texture, const char* matName);   
   bool ResetDynamicSkin(const char* skinTag, const char* texture, const char* matName);
   const char* getMeshTexture(StringTableEntry mesh);		//HJ: get the texture name on the specified mesh.
   //end:

We're getting close to the end. Let's move to the source/T3D/shapeBase.cpp.
First of all, add this in the destructor of ShapeBase(ShapeBase::~ShapeBase()).
//HJ: Dynamic skins and material swap
   // make sure list cleaned up   
   ClearDynamicSkins(); 
   //end:

From now, we have to be cautious to make it work correctly.
Find the these lines in the packUpdate.
// mask off images that aren't updated
      for(i = 0; i < MaxMountedImages; i++)
         if(!mMountedImageList[i].dataBlock)
            mask &= ~(ImageMaskN << i);
Right after them, add these.
//HJ: dynamic skins and material swap
	  if(mDynamicSkins.size() == 0)   
		  mask &= ~DynamicSkinMask;   
	  else  
		  mask |= DynamicSkinMask;
	  //end:

And find these lines.
// Group some of the uncommon stuff together.
   if (stream->writeFlag(mask & (NameMask | ShieldMask | HideCloakMask | InvincibleMask | SkinMask....
Change it like this.
// Group some of the uncommon stuff together.
   if (stream->writeFlag(mask & (NameMask | ShieldMask | HideCloakMask | InvincibleMask | SkinMask | DynamicSkinMask   //HJ: dynamic skins and material swap
	   | MeshHiddenMask )))

In the same if clause, after
if (stream->writeFlag(mask & InvincibleMask)) {
         stream->write(mInvincibleTime);
         stream->write(mInvincibleSpeed);
      }
Add this.
//HJ: dynamic skin and material swap
	  if(stream->writeFlag(mask & DynamicSkinMask))   
	  {   
		  // the number of dynamic skins   
		  stream->writeInt(mDynamicSkins.size(), 10);   

		  for(S32 x = 0; x < mDynamicSkins.size(); x++)   
		  {   
			  stream->writeString(mDynamicSkins[x].mSkinTag);   
			  stream->writeString(mDynamicSkins[x].mMaterialName);   
			  stream->writeInt(mDynamicSkins[x].mTextures.size(), 10);   

			  for(S32 y = 0; y < mDynamicSkins[x].mTextures.size(); y++)   
			  {   
				  stream->writeString(mDynamicSkins[x].mTextures[y]);   
			  }   

		  }   
	  } 
	  //end:

In the unpackUpdate, after
if (stream->readFlag()) 
      {
         // InvincibleMask
         F32 time, speed;
         stream->read(&time);
         stream->read(&speed);
         setupInvincibleEffect(time, speed);
      }

Add this.
//HJ: dynamic skins and material swapping
	  if(stream->readFlag())     
	  {   
		  ClearDynamicSkins();   

		  // the number of dynamic skins   
		  S32 numDynamicSkins = stream->readInt(10);   

		  for(S32 x = 0; x < numDynamicSkins; x++)   
		  {   
			  DynamicSkinData data;   
			  mDynamicSkins.push_back(data);   
			  S32 index = mDynamicSkins.size() -1;   

			  mDynamicSkins[index].mSkinTag = stream->readSTString();   
			  mDynamicSkins[index].mMaterialName = stream->readSTString();   

			  // the number of dynamic skins   
			  S32 numDynamicTextures = stream->readInt(10);   

			  for(S32 y = 0; y < numDynamicTextures; y++)   
			  {   
				  mDynamicSkins[index].mTextures.push_back(stream->readSTString());                       
			  }   
		  }   

		  BuildDynamicSkins();   
	  }
	  //end:

At the end of file and these.
//HJ: dynamic skins and material swap
const char* ShapeBase::getMeshTexture(StringTableEntry mesh)			//HJ: Not included in the original resource. added for my special needs
{
	if(!mesh)
	{
		return NULL;
	}

	//At first, we need to find the given mesh.
	for(int i=0;i<mShapeInstance->getShape()->objects.size();i++)
	{
		S32 idx = mShapeInstance->getShape()->objects[i].nameIndex;
		if(idx>=0)
		{
			if(0==dStricmp(mesh, mShapeInstance->getShape()->getName(idx)))	//Found the mesh
			{
				TSMesh* iMesh = mShapeInstance->mMeshObjects[i].getMesh(0);
				U32		material= iMesh->primitives[0].matIndex & TSDrawPrimitive::MaterialMask;
				if(iMesh->primitives[0].matIndex & TSDrawPrimitive::NoMaterial)
				{
					Con::errorf("No material on this mesh");
					return NULL;
				}
				else
				{
					TSMaterialList* materials = mShapeInstance->getShape()->materialList;
					const char* matName = materials->getMaterialName(material);
					return matName;
				}
			}
		}
	}

	return NULL;
}

ConsoleMethod( ShapeBase, getMeshTexture, const char*, 3, 3, "(string meshname)")   
{   
	return object->getMeshTexture(argv[2]);   
} 

void ShapeBase::BuildDynamicSkins()   
{      
	for(S32 i = 0; i < mDynamicSkins.size(); i ++)   
		mShapeInstance->SetDynamicSkin( &mDynamicSkins[i]);   
}   

void ShapeBase::ClearDynamicSkins()   
{   
	for(S32 i = 0; i < mDynamicSkins.size(); i++)   
		mDynamicSkins[i].mTextures.clear();   

	mDynamicSkins.clear();   
}

void    ShapeBase::UpdateDynamicSkins()   
{   
	setMaskBits(DynamicSkinMask);   
} 

ConsoleMethod( ShapeBase, UpdateDynamicSkins, void, 2, 2, "")   
{      
	object->UpdateDynamicSkins();   
}   

bool ShapeBase::AddDynamicSkinLayerTexture(const char* skinTag, const char* texture, const char* matName)   
{   
	// for sanitys sake, lets prevent scripters from going crazy with skin layers.   
	if( mDynamicSkins.size() >= CustomMaterial::MAX_TEX_PER_PASS )   
	{   
		Con::errorf("AddDynamicSkinLayerTexture(): Max number of textures reached! (%d)", mDynamicSkins.size());   
		return false;   
	}   

	bool found = false;   
	for(S32 i = 0; i < mDynamicSkins.size(); i ++)   
	{   
		if(0 == dStricmp(mDynamicSkins[i].mSkinTag, skinTag))   
		{   
			found = true;              
			mDynamicSkins[i].mTextures.push_back( StringTable->insert(texture) );   
			mDynamicSkins[i].mMaterialName = StringTable->insert(matName);   
		}   
	}   

	if(!found)   
	{   

		DynamicSkinData data;   
		mDynamicSkins.push_back(data);   
		S32 index = mDynamicSkins.size() - 1;   

		mDynamicSkins[index].mTextures.clear();// redundant but lets just make sure.   
		mDynamicSkins[index].mTextures.push_back( StringTable->insert(texture) );   
		mDynamicSkins[index].mSkinTag = StringTable->insert(skinTag);   
		mDynamicSkins[index].mMaterialName = StringTable->insert(matName);   
	}   

	return true;   
}   

//--------------------------------------------------------------------------------------------------------   
ConsoleMethod( ShapeBase, AddDynamicSkinLayerTexture, bool, 5, 5, "(string skinTag, string texture, string materialName)")   
{      
	return object->AddDynamicSkinLayerTexture(argv[2],argv[3],argv[4]);   
}   

//--------------------------------------------------------------------------------------------------------   
bool ShapeBase::ResetDynamicSkin(const char* skinTag, const char* texture, const char* matName)   
{   
	if(!texture || !skinTag || !matName)   
		return false;   


	bool found = false;   
	for(S32 i = 0; i < mDynamicSkins.size(); i ++)   
	{   
		if(0 == dStricmp(mDynamicSkins[i].mSkinTag, skinTag))   
		{   
			found = true;   
			mDynamicSkins[i].mTextures.clear();   
			mDynamicSkins[i].mTextures.push_back( StringTable->insert(texture) );   
			mDynamicSkins[i].mMaterialName = StringTable->insert(matName);   
		}   
	}   

	if(!found)   
	{   
		DynamicSkinData data;   
		mDynamicSkins.push_back(data);   
		S32 index = mDynamicSkins.size() - 1;   

		mDynamicSkins[index].mTextures.clear();// redundant but lets just make sure.   
		mDynamicSkins[index].mTextures.push_back( StringTable->insert(texture) );   
		mDynamicSkins[index].mSkinTag = StringTable->insert(skinTag);   
		mDynamicSkins[index].mMaterialName = StringTable->insert(matName);   
	}   

	return true;   
}   

//--------------------------------------------------------------------------------------------------------   
ConsoleMethod( ShapeBase, ResetDynamicSkin, bool, 5, 5, "(string skinTag, string texture, string materialName)")   
{      
	return object->ResetDynamicSkin(argv[2], argv[3], argv[4]);   
}  
//end:

Now, open up file source/materials/materialList.h.
Find the member variable
Vector<String> mMaterialNames;
.
It is protected. Move it to [bold]public[/bold].

Next, in the source/materials/materialDefinition.h.
After
static void initPersistFields();
Add this.
//HJ: dynamic skins and material swap
   Material &operator=(const Material &Material);
   //end:

Now, let's move to the correspondent source/materials/materialDefinition.cpp.
At the end of the file, add this.
//HJ: dynamic skins and material swap
Material & Material::operator =(const Material &material)   
{   
	// tedious, but want to avoid copying simobject data, which a straight memcopy would do.   
	// if you do that you get the simobject ID and then it complains when you try to register it.   

	for (S32 i = 0; i < MAX_STAGES; i++) {   
		mBaseTexFilename[i] = material.mBaseTexFilename[i];      
		mDetailTexFilename[i]  = material.mDetailTexFilename[i];   //HJ: mDetailFilename>>>mDetailTexFilename   
		mBumpTexFilename[i]    = material.mBumpTexFilename[i];      //HJ: mBumpFileName>>>mBumpTexFilename   
		mEnvTexFilename[i]     = material.mEnvTexFilename[i];       //HJ: mEnvFileName>>>mEnvTexFilename   
		mStages[i]          = material.mStages[i];   
		mDiffuse[i]         = material.mDiffuse[i];   
		mSpecular[i]        = material.mSpecular[i];   
		mSpecularPower[i]   = material.mSpecularPower[i];   
		mPixelSpecular[i]   = material.mPixelSpecular[i];   
		//mVertexSpecular[i]  = material.mVertexSpecular[i];	//HJ: commented out. deepscratch
		mExposure[i]        = material.mExposure[i];   
		mAnimFlags[i]       = material.mAnimFlags[i];   
		mScrollDir[i]       = material.mScrollDir[i];   
		mScrollSpeed[i]     = material.mScrollSpeed[i];   
		mScrollOffset[i]    = material.mScrollOffset[i];   
		mRotSpeed[i]        = material.mRotSpeed[i];   
		mRotPivotOffset[i]  = material.mRotPivotOffset[i];   
		mRotPos[i]          = material.mRotPos[i];   
		mWavePos[i]         = material.mWavePos[i];   
		mWaveFreq[i]        = material.mWaveFreq[i];   
		mWaveAmp[i]         = material.mWaveAmp[i];   
		mWaveType[i]        = material.mWaveType[i];   
		mSeqFramePerSec[i]  = material.mSeqFramePerSec[i];   
		mSeqSegSize[i]      = material.mSeqSegSize[i];   
		mGlow[i]            = material.mGlow[i];   
		mEmissive[i]        = material.mEmissive[i];   
		mColorMultiply[i]   = material.mColorMultiply[i];   
	}   

	mDoubleSided        = material.isDoubleSided();   
	mCubemapName        = material.mCubemapName;   
	mCubemapData          = material.mCubemapData;   
	mDynamicCubemap     = material.mDynamicCubemap;   
	mTranslucent        = material.isTranslucent();   
	mTranslucentBlendOp = material.mTranslucentBlendOp;   
	mTranslucentZWrite  = material.mTranslucentZWrite;   
	mAlphaTest          = material.mAlphaTest;   
	mAlphaRef           = material.mAlphaRef;   
	mPlanarReflection   = material.mPlanarReflection;   
	mMapTo              = material.mMapTo;   

	mIsIFL              = material.isIFL();   

	mPath = material.getPath();   

	return *this;   
}
//end:

We're almost at the end. Let's open up the file source/materials/customMaterialDefinition.h.
Add this
//HJ: dynamic skins and material swap
   CustomMaterial &operator=( const CustomMaterial &Material);
   //end:
just before this line.
DECLARE_CONOBJECT(CustomMaterial);

Finally, just one to add. Let's open up source/materials/customMaterialDefinition.cpp.
After
void CustomMaterial::onRemove()
.
Add this.
//HJ: dynamic skins and material swap
CustomMaterial & CustomMaterial::operator =(const CustomMaterial &material) {   

	Parent::operator =(material);   

	// custom material copy   
	for (S32 i = 0; i < MAX_TEX_PER_PASS; i++) {   
		mTexFilename[i] = material.mTexFilename[i];   
		mFlags[i]       = material.mFlags[i];   
	}   

	if (NULL == material.mFallback) {   
		mFallback = NULL;   
	} else{   
		*mFallback = *material.mFallback;   
	}   
     
	//HJ: no need this
	/*
	if (NULL == material.mDynamicLightingMaterial) {   
		mDynamicLightingMaterial = NULL;   
	} else{   
		mDynamicLightingMaterial = material.mDynamicLightingMaterial;   
	}   

	if (NULL == material.mDynamicLightingMaskMaterial) {   
		mDynamicLightingMaskMaterial = NULL;   
	} else{   
		mDynamicLightingMaskMaterial = material.mDynamicLightingMaskMaterial;   
	}
	*/
	//end:

	mVersion        = material.mVersion;   
	mRefract         = material.mRefract;   
	mMaxTex         = material.mMaxTex;   

	ShaderData* sd = static_cast<ShaderData*>(Sim::findObject(material.mShaderDataName));   
	if ( sd ) {   
		mShaderData = sd;   
	}   
	mShaderDataName = material.mShaderDataName;   

	return *this;   
} 
//end:

That's all.
Page«First 1 2 Next»
#21
03/27/2010 (9:19 am)
Sorry to have kept you waiting too long. I posted related resource few minutes ago in resources corner. It seems like there is a preview process by GG. The body doesn't appear to me yet.
#22
03/27/2010 (9:26 am)
Seems strange I never seen anything about preview process.
I think the post had bug, you should upload your body again by editing your resource.

FYI, I saw it empty also.
#23
03/27/2010 (11:45 am)
Sounds insteresting. I hope your resource will be available soon.
#24
03/28/2010 (3:48 am)
It works now. Thanks for your patience.
#25
04/07/2010 (8:40 pm)
I newly released multiplayer version of this feature. You can find it here
#26
04/07/2010 (10:54 pm)
Thank you!
Page«First 1 2 Next»