Game Development Community

dev|Pro Game Development Curriculum

twSurfaceReference Object

by Ken Finney · 12/13/2002 (3:12 pm) · 36 comments

Download Code File

The twSurfaceReference object is a TGE enhancement used to create an accessible reference to the texture/surface characteristics of any point on a terrain.

The example included provides code fragments that allows you to modify the fxGrassReplicator to use this capability.

You must have the fxGressReplicator by Bendik Stang already installed in your game.

You can also apply the same changes to all of the replicator objects created by Melv May to get them to work the same way.

This resource assumes that you are familiar with the fxGrassReplicator, TGE, VC++, C++ programming, TGE script programming and the example scripts. If you are familiar with these things, then you should be able to resolve any conflicts that arise with these changes. If you have made changes or enhancements to any of the TGE source code or scripts, I can't warrant that this resource will work.



Installation
============

Back up your game source code and scripts prior to installation of this enhancement.


Engine Source changes
---------------------
1. Copy the files:


twSurfaceReference.cc
twSurfaceReference.h

from the twStructureReference.zip into the engine directory where your
fxGrassReplicator files reside.



2. Add both files to your TGE project.



3. Open fxGrassReplicator.cc

3a. Add this include to near the end of the includes at the top,

[b]
            #include "twSurfaceReference.h"
[/b]

BEFORE the include for fxGrassReplicator.h.



3b. Add this function to any place you like within the file:

[b]
            //--------------------------------------------
            // GetTerrainTextures
            //   (adapted from original code written by James Holmes)
            //
            // Get the terrain texture under an x,y coord
            //
            // Fills an array with the alpha value of each of the
            // textures under that point.
            //
            //   distance - How far to look down for the terrain block
            //   pt - Point in world space
            //   alphas - U8 array of size NUM_SURFACES
            //
            bool fxGrassReplicator::GetTerrainTextures(float distance, Point3F& pt, U8 *alphas)
            {
            RayInfo rInfo;

               if (gClientContainer.castRay(pt, Point3F(pt.x, pt.y, pt.z - distance ), TerrainObjectType, &rInfo))
               {
               U32 CollisionType = rInfo.object->getTypeMask();

                  if (CollisionType & TerrainObjectType)
                  {
            		   TerrainBlock* ter = static_cast<TerrainBlock*>(rInfo.object);
            		   Point2I gPos;
            		   const MatrixF & mat = ter->getTransform();

            		   Point3F origin;
            		   mat.getColumn(3, &origin);
            		   F32 squareSize = (F32) ter->getSquareSize();
            		   F32 halfSquareSize = squareSize / 2;

            		   float x = (pt.x - origin.x + halfSquareSize) / squareSize;
            		   float y = (pt.y - origin.y + halfSquareSize) / squareSize;

            		   ter->getMaterialAlpha(Point2I(x,y), alphas);
            		   return true;
                  }
               }
               return false;
            }
[/b]



3c. Add the following lines to the end of the function initPersistFields()

[b]
	addField( "SurfaceExclusionMode",		TypeBool,		Offset( mFieldData.mSurfaceExclusionMode,			fxGrassReplicator ) );
   addField( "SurfaceType",      TypeEnum,      Offset(mFieldData.mTheSurface,		fxGrassReplicator ), 1, &gSurfaceTypeTable );
[/b]



3d. In the function CreateGrass() AFTER these lines:

[i]
				if ((CollisionType & WaterObjectType) && !mFieldData.mAllowWaterSurface &&
					!gClientContainer.castRay( GrassStart, GrassEnd, FXGRASSREPLICATOR_NOWATER_COLLISION_MASK, &RayEvent)) continue;
[/i]


add the following fragment:
[b]
            if ((twSurfaceReference::ESurfaceType)mFieldData.mTheSurface)  // if we are checking for specific surfaces and not just 'any'
            {
               if ( BaseSurfaces[(twSurfaceReference::ESurfaceType)mFieldData.mTheSurface].isAvailable)
               {
				   CollisionResult = false;
               U8 alphas[TerrainBlock::MaterialGroups];
                  // Checks if the new position is over a blended terrain texture (a 'surface' ) that is *exactly* the same as the surface where
                  // the specified SurfaceReference marker was placed.

                  if (GetTerrainTextures(4000.0f, GrassStart, alphas))
                  {
                  bool skip = mFieldData.mSurfaceExclusionMode;

                     for (int i=0; i < TerrainBlock::MaterialGroups; i++)
                     {
                        if (alphas[ i] != BaseSurfaces[(twSurfaceReference::ESurfaceType)mFieldData.mTheSurface].alphas[ i])
                        {
                           skip = !mFieldData.mSurfaceExclusionMode;                 // even one non-match means this is not the same surface
                           break;
                        }
                     }
                     if (skip) continue;              // don't place grass here
                  }
               }
             }
[/b]



3e. In the function packUpdate() AFTER these lines:

[i]
      		stream->writeFlag(mFieldData.mIsSquare);
      		stream->write(mFieldData.mRotateAngle);
[/i]

add the following fragment:
[b]
      		stream->writeFlag(mFieldData.mSurfaceExclusionMode);					// allow/disallow toggle
            stream->write( mFieldData.mTheSurface );                          // the surface reference
[/b]



3f. In the function unpackUpdate() AFTER these lines:

[i]
      		mFieldData.mIsSquare = stream->readFlag();
      		stream->read(&mFieldData.mRotateAngle);							// if not; use this angle
[/i]

add the following fragment:
[b]
      		mFieldData.mSurfaceExclusionMode = stream->readFlag();				// Allow/Disallow toggle
            stream->read( &mFieldData.mTheSurface);                           // the surface reference
[/b]





4. Open fxGrassReplicator.h

4a. AFTER these lines:

[i]
   		bool				mIsSquare;
   		F32					mRotateAngle;
[/i]

add the following fragment:
[b]
         bool              mSurfaceExclusionMode;              // if 1 then allow, if 0 then disallow
         U32      mTheSurface;
[/b]



4b. AFTER these lines:

[i]
			mIsSquare				= true;
			mRotateAngle			= 0.0f;
[/i]

add the following fragment:
[b]
         mSurfaceExclusionMode			= false;
         mTheSurface            = (U32)twSurfaceReference::eAny;
[/b]



4c. AFTER these lines:


[i]
			public:
			    fxGrassReplicator();
			    ~fxGrassReplicator();"
[/i]

add the following line:
[b]
         bool GetTerrainTextures(float,Point3F&, U8 *);
[/b]



5. Open /engine/console/simBase.h

and add the following line in the nameSpace Sim block somewhere near line 509 (put it as the last xxxxSet entry)

[b]
      DeclareNamedSet(twSurfaceSet)  //(Note: no semi-colon)
[/b]



6. Open /engine/console/simBase.cc
and add the following line in the nameSpace Sim block somewhere near line 19 (put it as the last xxxxSet entry)

[b]
      ImplementNamedSet(twSurfaceSet) //(Note: no semi-colon)
[/b]





7. Open /engine/console/simManager.cc
and add the following line in function void init() somewhere near line 269 (put it as the last xxxxSet entry)

[b]
      InstantiateNamedSet(twSurfaceSet);  // Now there IS a semi-colon
[/b]


8. Do a clean build of your project.


Script Integration changes
---------------------

9. Open /example/common/editor/ObjectBuilderGui.gui
(somewhere near line 460, after // Environment )

and add this function:

[b]
      function ObjectBuilderGui::buildtwSurfaceReference(%this)
      {
      	%this.className = "twSurfaceReference";
      	%this.process();
      }
[/b]



10. Open /example/common/editor/EditorGui.cs
(in function Creator::init( %this ) where 'n' is the next number in the sequence
of array assignments)

[b]
         %Environment_Item[n] = "twSurfaceReference";
[/b]



11. Open /example/common/client/missionDownload.cs
In function clientCmdMissionStartPhase3(%seq,%missionName) somewhere near line 65
of array assignments)

AFTER
[i]
         onPhase2Complete();
[/i]


add this line BEFORE any other Start*(); function calls:
[b]
	      StartSurfaceReferencer();
[/b]


Using in a mission file
=======================

12. Add to your mission like any other object. Examples of a twSurfaceRefence mission
object and a modified fxGrassReplicator object are contained in the file 'samples.txt'
in the twSurfaceReference.zip file.

13. Set up your reference surface:

Place the SurfaceReference object over the precise surface that you want to
reference later for your grass. Select a type from the SurfaceType drop-down menu.

14. Place your grass:

14a. Place a modified fxGrassReplicator object in an appropriate location. Make sure that there
is some surface texture the same as you referenced in step 13 above. Select the same
SurfaceType from the drop down menu. Hit 'Apply'.

14b. To make sure your grass does NOT appear on a given referenced surface, then check the
'SurfaceExclusionMode' box. Hit 'Apply'.



NOTES:

- In regular placement mode, the twSurfaceReference looks for texture alphas that are EXACTLY the same for the given type in both the referencer and the grass object at time of placement.

- In exclusion mode, the twSurfaceReference looks for texture alphas that are COMPLETELY different for the given type in both the referencer and the grass object at time of placement.

- The function 'GetTerrainTextures' is an adaptation of code written by James Holmes for his Terrain Path Following add-on.

- the screenshot (small as it is) shows twSurfaceReference being used by fxGrassReplicator, fxFoliageReplicator, AND fxShapeReplicator: a tree-line road (shapes), several hedge-lines (shapes, grass and foliage) and a few copses and spinneys of trees.


[edit]:fixed the incorrect function reference in the instructions at step 3d. should be CreateGrass and not the other.
[edit]:fixed typo in the file name in step 10.
[edit]:added omitted step 4c.
[edit]:added missing semicolon in step 7.
Page «Previous 1 2
#2
12/09/2002 (12:09 pm)
This is super!!

I'll go ahead and implement it in the fxGrassReplicator.

Just what we all needed!

:D
Bendik

Edit: Until it is approved, I cannot access the .zip
#4
12/09/2002 (12:44 pm)
Correction 1: (in bold)
10. Open /example/common/editor/EditorGui.[b]cs[/b] 
(in function Creator::init( %this ) where 'n' is the next number in the sequence 
of array assignments)

Correction 2:
In the fxGrassReplicator.h
after:
public:
	fxGrassReplicator();
	~fxGrassReplicator();"
add this:
bool GetTerrainTextures(float,Point3F&, U8 *);

:)

The tool works very well, but it made me find a bug in the fxGrassReplicator with the square placement area.
If you get problems be sure to use a circular area (uncheck isSquareArea)
#5
12/09/2002 (2:22 pm)
I just love these improvements, good work Ken!

James
#6
12/09/2002 (2:59 pm)
Thanks Bendik - errors fixed.
#7
12/09/2002 (3:02 pm)
Probably a future revision is that the GetTerrainTextures function should be renamed CompareSurfaces, and used from the twSurfaceReference class instead of repeating the code in each new class that wants to use it ...
#8
12/13/2002 (3:13 pm)
That screenshot looks very cool Ken :)
#9
12/13/2002 (8:14 pm)
Hard to believe its the same engine as Marble Blast, huh ?

:-)
#10
12/14/2002 (1:01 pm)
@Ken,
You might want to test for a null return for these casts.
dynamic_cast<SimSet*>(Sim::findObject("twSurfaceSet"))->addObject(this);
I managed to generate an exception due to no object being found. :)

I believe how I did this was to paste your object references and definition from your sample.txt into my mission file. I did it without much thought as to where my surface representations needed to be defined and placed correctly. The instructions were too vague for me so I guessed (always bad).

Edit: 14a was where I lost ya.
#11
12/16/2002 (6:48 am)
Wendell, double-check your c code changes. I just ran a test with several replicators referencing non-existent surface references (ie. none at all in the mission) with no problem.

First make sure that steps 5,6 & 7 have been done, awith no typos. Note that in steps 5 & 6 there is NO semi-colon used as a statement terminator. In step 7 there IS a semi-colon.

Also, make sure that you added twSurfaceReference.cc to your project, so that it gets included in the build. Also make sure to do a clean build.
#12
12/16/2002 (1:42 pm)
@Ken - Boom. 5, 6, and 7 were not there. That is what I get for assuming Bendiks' inclusion of your edits into fxGrassReplicator.cc made all the necessary edits. Nope. The above along with the .cs edits need doing. (I caught the .cs but not the simBase files)
(Step 7 says it has a semicolon but none is there in the doc above. I like to cut and paste. FYI)

So these edits haven't fixed all. So now I will go through your edits to insure Bendiks' merges are correct.

And last but not least. I never have modified any terrain so how to use this mod in my mission with whatever objects are needed will be a discovery process. I will read the docs :P and see if this all becomes clearer. Like I said 14a left me clueless.

Thanks Ken
#13
12/16/2002 (2:58 pm)
Wendell - sorry about that -the missing semicolon has been added to step 7.

All the necessary info is there in step 14 - I can't deduce where your confusion is...
#14
12/16/2002 (3:52 pm)
Can you explain how the "SurfaceTypeEnums" are linked to particular textures? I can't quite get my head around this :)
#15
12/16/2002 (4:37 pm)
Desmond - are you talking about in the code, or in using the replicators & referencers ?

Assuming the former, what happens is that the blender creates blended textures by adjusting the alpha for each texture that is placed at a given location. Each of these texture alphas gets a given 'layer' in the 'stack', if you will, of alphas, and are applied in the same order for a given blending combination. I've defined a 'surface' to be that unique combination of texture alphas at a given spot.

If the latter:
A referencer object is placed at a suitable location that visually matches what you want. For example, to reference a road surface, I plant a surface refencer in the middle of road made with the texture painter, ensuring that no blending 'spill' has occurred from adjacent grass, for example. Let us say I used a grey texture I called 'gravel'. At that point in the middle of the area painted as gravel, the referencer will get an alpha blend list that only has the 'gravel' texture at 0 alpha, and no other texture alphas. In the object editor (press F11 & then F3) I select that surface referencer, and then assign a surface name from the pulldown menu - in this case I might use 'Road'. This is kept in a globally available list.

Now, when I placea replicator object (it has to be one modified to work with the referencer, like the grass rep'er in the snippet above), I select from its drop-down menu the 'Road' surface, and then check the SurfaceExclusionMode check box.

Now when the replicator does its stuff, the modified code checks to find the alpha blend comb at a given location where it has decided to place some grass. It then uses the result of that test to compare against the the list of surfaces held by the global table created by twSurfaceReferencer. In this case, if there is a hit (an EXACT match), then the replicator aborts this placement, and moves on to the next position as decided by its placement algorithm, and the testing continues.

To make a hedgeline, I created a dark green texture, painted it in a straight line on the terrain, placed a referencer on an appropriate spot, selected a surface type from the pulldown, then placed trees and bushes with replicators that had the same surface type name selected but did NOT check the exclusion box.

HTH
#16
12/30/2002 (5:33 pm)
will someone tell me where the fx .cc and .h files go please.
#17
01/03/2003 (3:09 pm)
Thanks Ken, Also, what are the pGrass, Dgrass, PSnow, ... for? Very nice work!

@Chistopher, I placed fxGrassReplicator and twSurfaceReference files in engine/game/fx--works fine.
#18
01/14/2003 (6:01 pm)
THanks Desmond -

the surface names are just that names, their only significance is they were the names I needed. pgrass = patchy grass, dgrass = dark grass, etc. There is nothing to stop you from using pgrass to specify yellow snow, for example :-). oh, and if you didn't know - don't eat yellow snow ;-)
#19
03/08/2003 (11:24 pm)
I am getting an error in debug on line 117 of sceneState.cc it is claiming the code in twSurfaceReference::updateObject has either unbalanced the xform stacks or failed to reset the viewport. Any ideas? I'll continue looking tomorrow, must sleep now.
#20
03/09/2003 (7:56 am)
I don't think my comment posted.. oh well, just wanted to let you know that I got the twSurfaceReference from the grassReplicator posts and it was missing the popMatrix and projection reset before modelview and resetting the viewport. Don't know if this code has the smae problem. Thanks again.
Page «Previous 1 2