Game Development Community

Random fxShapeReplicator

by Dave Young · in Torque Game Engine · 04/12/2006 (1:17 pm) · 8 replies

Has anyone seen a version of fxShapeReplicator that will place a random shape from among a list of shapes?

Say you had 5 different pine tree models, you could pix 5 models from a list and the replicator would choose randomly as it is placing them.

Is this something that has been done before already?

#1
04/13/2006 (6:10 am)
I got this working, was pretty easy to do, took about an hour. I did it for TSE. If anyone ever finds this post and wants to know how to do it, email me!
#2
04/13/2006 (8:34 am)
Dave, Fantastic!

That would be an excellent resource for TGE as well. Any reason it would not work in TGE that you know of?
#3
04/13/2006 (8:50 am)
No reason. the changes I made were extremely elementary, did not involve graphic updates or anything. I will port to TGE later and make a resource out of it.
#4
04/13/2006 (9:24 am)
Dave, Excellent, that would be a really useful contribution to the community.
#5
04/13/2006 (8:04 pm)
Oddly enough I had already started working on the same resource. I was planning to add 16 shapes to my replicator. I am having some problems with it though that perhaps you can shed some light on. I've made my changes to TLK1.4 although I don't see why it would be different for straight TGE (I don't mess with any TLK code). I'll post my changes so maybe I can get some help with this:

First, in fxShapeReplicator.h:
// Field Data.
   class tagFieldData
   {
      public:

      U32                mSeed;
	  //BS: changed to array of shapes
      StringTableEntry   mShapeFile[16];
	  //END
      U32                mShapeCount;
further down...
tagFieldData()
      {
         // Set Defaults.
         mSeed               = 1376312589;
		 //BS: set default for each shape
		 for(int i=0;i<16;i++)
		 {
			mShapeFile[i]          = StringTable->insert("N/A");
		 }
		 //END
         mShapeCount         = 10;

Pretty simple, just make an array for the shape names, and set the defaults. I set the default string to "N/A" because for some reason when I left it at "" it would not write the fields to the mission file (unless I populate them with shape paths).

Then fxShapeReplicator.cc:
addGroup( "Media" );
	addField( "ShapeFile1",          TypeFilename,   Offset( mFieldData.mShapeFile[0],              fxShapeReplicator ) );
	addField( "ShapeFile2",          TypeFilename,   Offset( mFieldData.mShapeFile[1],              fxShapeReplicator ) );
	addField( "ShapeFile3",          TypeFilename,   Offset( mFieldData.mShapeFile[2],              fxShapeReplicator ) );
	addField( "ShapeFile4",          TypeFilename,   Offset( mFieldData.mShapeFile[3],              fxShapeReplicator ) );
	addField( "ShapeFile5",          TypeFilename,   Offset( mFieldData.mShapeFile[4],              fxShapeReplicator ) );
	addField( "ShapeFile6",          TypeFilename,   Offset( mFieldData.mShapeFile[5],              fxShapeReplicator ) );
	addField( "ShapeFile7",          TypeFilename,   Offset( mFieldData.mShapeFile[6],              fxShapeReplicator ) );
	addField( "ShapeFile8",          TypeFilename,   Offset( mFieldData.mShapeFile[7],              fxShapeReplicator ) );
	addField( "ShapeFile9",          TypeFilename,   Offset( mFieldData.mShapeFile[8],              fxShapeReplicator ) );
	addField( "ShapeFile10",          TypeFilename,   Offset( mFieldData.mShapeFile[9],              fxShapeReplicator ) );
	addField( "ShapeFile11",          TypeFilename,   Offset( mFieldData.mShapeFile[10],              fxShapeReplicator ) );
	addField( "ShapeFile12",          TypeFilename,   Offset( mFieldData.mShapeFile[11],              fxShapeReplicator ) );
	addField( "ShapeFile13",          TypeFilename,   Offset( mFieldData.mShapeFile[12],              fxShapeReplicator ) );
	addField( "ShapeFile14",          TypeFilename,   Offset( mFieldData.mShapeFile[13],              fxShapeReplicator ) );
	addField( "ShapeFile15",          TypeFilename,   Offset( mFieldData.mShapeFile[14],              fxShapeReplicator ) );
	addField( "ShapeFile16",          TypeFilename,   Offset( mFieldData.mShapeFile[15],              fxShapeReplicator ) );
    endGroup( "Media" );
I'll mention that I tried to use a for loop on that one, but I couldn't get that to work either so I had to type out all 16...
void fxShapeReplicator::CreateShapes(void)
{
    F32             HypX, HypY;
    F32             Angle;
    U32             RelocationRetry;
    Point3F         ShapePosition;
    Point3F         ShapeStart;
    Point3F         ShapeEnd;
    Point3F         ShapeScale;
    EulerF          ShapeRotation;
    QuatF           QRotation;
    bool            CollisionResult;
    RayInfo         RayEvent;
    TSShape*        pShape;
    Container*      pContainer;
	//BS: added 3 variables
	bool			bValid = false;
	MRandomLCG		randomIndex;
	S32				pickIndex = 0;
	//END
further down...
// Cannot continue without shapes!
	for(int i=0;i<16;i++)
	{
		if(mFieldData.mShapeFile[i] != "" && mFieldData.mShapeFile[i] != "N/A")
		{
			bValid = true;
			break;
		}
	}
	// cannot continue with no shapes!
    if (!bValid) return;
There I just check if at least one of the shapes has a shape path in it to continue..
// pick a random shape index
		randomIndex.setSeed(RandomGen.randI(0,mFieldData.mSeed));
		pickIndex = randomIndex.randI(0,15);
		do
		{
			pickIndex = randomIndex.randI(0,15);
			Con::warnf(ConsoleLogEntry::General, "[%d] - was picked!", pickIndex);

		}while(mFieldData.mShapeFile[pickIndex] == "N/A" || mFieldData.mShapeFile[pickIndex] == "");

        // Set the 'shapeName' field.
        fxStatic->setField("shapeName", mFieldData.mShapeFile[pickIndex]);
Now, here is a major trouble spot, this is *supposed* to keep getting a random index until it hits a valid shape. It doesn't and I can't figure out why, unless I have ALL of my 16 shapes filled out, it won't work. (works great if I do hehe)
further down...
// Write Replication Flag.
    if (stream->writeFlag(mask & ReplicationMask))
    {
        stream->writeAffineTransform(mObjToWorld);                      // Replicator Position.

        stream->writeInt(mFieldData.mSeed, 32);                         // Replicator Seed.
        stream->writeInt(mFieldData.mShapeCount, 32);                   // Shapes Count.
        stream->writeInt(mFieldData.mShapeRetries, 32);                 // Shapes Retries.
		//BS: changed to array
		for(int i=0;i<16;i++)
		{
			stream->writeString(mFieldData.mShapeFile[i]);
		}
	//END
and then...
// Read Replication Details.
    if(stream->readFlag())
    {
        MatrixF     ReplicatorObjectMatrix;

        stream->readAffineTransform(&ReplicatorObjectMatrix);               // Replication Position.

        mFieldData.mSeed                    = stream->readInt(32);          // Replicator Seed.
        mFieldData.mShapeCount              = stream->readInt(32);          // Shapes Count.
        mFieldData.mShapeRetries            = stream->readInt(32);          // Shapes Retries.
		//BS: changed to array
		for(int i=0;i<16;i++)
		{
			mFieldData.mShapeFile[i]               = stream->readSTString();       // Shape File.
		}
		//END

That's it, should compile fine, but the biggest problem is that I have to fill all 16 of the slots. I picked 16 because in the sharp tree pack there are sometimes 15 or so varieties of trees, and that makes a much more interesting forest...

I hope that you (Dave) or anyone else can help with this, I think that it'll be a great resource (maybe do it to the foliage replicator too)
#6
04/13/2006 (8:07 pm)
Dave, you should post a TSE resource. I'd find this handy.
#7
04/14/2006 (2:32 am)
Ben, what I did to help the problem was to keep the checks for shapeFile[1] that are in CreateShape. If that was empty I return. This means that only the first shape must be filled. Then, if any others are empty, I fill it with the contents of shapeFile[1]. I did this before the call to CreateShapes, eliminating the need for a do->while loop. Because this loop runs for each shape in the replicator, I decided to take it out to speed up the replicator. Making the shapefilename validation and substitutions before this point helped considerably.

That way, *any* random number in the range will work, returning a valid shape.

Our implementations are very similar. You posted the code first, so why don't you go ahead and finish it up as a resource!
#8
04/14/2006 (7:09 pm)
Thanks a lot for the help, it turns out I needed to set the mShapeFile to NULL instead of using the StringTable->insert(""); ... odd that the replicator worked fine with one variable set to that...

Anyway, your suggestions did help me to clean up the code a bit. I went ahead and made a resource out of it, so when that gets approved it should be up!