//----------------------------------------------------------------------------- // Torque Game Engine // Written by Melvyn May, Started on 13th March 2002, finished 27th March 2002. // // "My code is written for the Torque community, so do your worst with it, // just don't rip-it-off and call it your own without even thanking me". // // - Melv (working hard to become an associate ... hint). // //----------------------------------------------------------------------------- #include "dgl/dgl.h" #include "console/consoleTypes.h" #include "core/bitStream.h" #include "math/mRandom.h" #include "math/mathIO.h" #include "terrain/terrData.h" #include "game/gameConnection.h" #include "console/simBase.h" #include "sceneGraph/sceneGraph.h" #include "fxShapeReplicator.h" //------------------------------------------------------------------------------ // // Put this in /example/common/editor/editor.cs in function [Editor::create()] (around line 66). // // // Ignore Replicated fxStatic Instances. // EWorldEditor.ignoreObjClass("fxShapeReplicatedStatic"); // //------------------------------------------------------------------------------ // // Put this in /example/common/editor/EditorGui.cs in [function Creator::init( %this )] // // %Environment_Item[8] = "fxShapeReplicator"; <-- ADD THIS. // //------------------------------------------------------------------------------ // // Put the function in /example/common/editor/ObjectBuilderGui.gui [around line 458] ... // // function ObjectBuilderGui::buildfxShapeReplicator(%this) // { // %this.className = "fxShapeReplicator"; // %this.process(); // } // //------------------------------------------------------------------------------ // // Put this in /example/common/client/missionDownload.cs in [function clientCmdMissionStartPhase3(%seq,%missionName)] (line 65) // after codeline 'onPhase2Complete();'. // // StartClientReplication(); // //------------------------------------------------------------------------------ // // Put this in /engine/console/simBase.h (around line 509) in // // namespace Sim // { // DeclareNamedSet(fxReplicatorSet) <-- ADD THIS (Note no semi-colon). // //------------------------------------------------------------------------------ // // Put this in /engine/console/simBase.cc (around line 19) in // // ImplementNamedSet(fxReplicatorSet) <-- ADD THIS // //------------------------------------------------------------------------------ // // Put this in /engine/console/simManager.cc [function void init()] (around line 269). // // namespace Sim // { // InstantiateNamedSet(fxReplicatorSet); <-- ADD THIS // //------------------------------------------------------------------------------ extern bool gEditingMission; //------------------------------------------------------------------------------ IMPLEMENT_CO_NETOBJECT_V1(fxShapeReplicator); IMPLEMENT_CO_NETOBJECT_V1(fxShapeReplicatedStatic); //------------------------------------------------------------------------------ // Class: fxShapeReplicator //------------------------------------------------------------------------------ fxShapeReplicator::fxShapeReplicator() { // Setup NetObject. mTypeMask |= StaticObjectType | StaticTSObjectType | StaticRenderedObjectType; mAddedToScene = false; mNetFlags.set(Ghostable | ScopeAlways); // Reset Client Replication Started. mClientReplicationStarted = false; // Reset Shape Count. mCurrentShapeCount = 0; // Reset Creation Area Angle Animation. mCreationAreaAngle = 0; } //------------------------------------------------------------------------------ fxShapeReplicator::~fxShapeReplicator() { } //------------------------------------------------------------------------------ void fxShapeReplicator::initPersistFields() { // Initialise parents' persistent fields. Parent::initPersistFields(); // Add out own persistent fields. addGroup( "Debugging" ); addField( "HideReplications", TypeBool, Offset( mFieldData.mHideReplications, fxShapeReplicator ) ); addField( "ShowPlacementArea", TypeBool, Offset( mFieldData.mShowPlacementArea, fxShapeReplicator ) ); addField( "PlacementAreaHeight",TypeS32, Offset( mFieldData.mPlacementBandHeight, fxShapeReplicator ) ); addField( "PlacementColour", TypeColorF, Offset( mFieldData.mPlaceAreaColour, fxShapeReplicator ) ); endGroup( "Debugging" ); addGroup( "Media" ); addField( "ShapeFile", TypeFilename, Offset( mFieldData.mShapeFile, fxShapeReplicator ) ); endGroup( "Media" ); addGroup( "Replications" ); addField( "Seed", TypeS32, Offset( mFieldData.mSeed, fxShapeReplicator ) ); addField( "ShapeCount", TypeS32, Offset( mFieldData.mShapeCount, fxShapeReplicator ) ); addField( "ShapeRetries", TypeS32, Offset( mFieldData.mShapeRetries, fxShapeReplicator ) ); endGroup( "Replications" ); addGroup( "Placement Radius" ); addField( "InnerRadiusX", TypeS32, Offset( mFieldData.mInnerRadiusX, fxShapeReplicator ) ); addField( "InnerRadiusY", TypeS32, Offset( mFieldData.mInnerRadiusY, fxShapeReplicator ) ); addField( "OuterRadiusX", TypeS32, Offset( mFieldData.mOuterRadiusX, fxShapeReplicator ) ); addField( "OuterRadiusY", TypeS32, Offset( mFieldData.mOuterRadiusY, fxShapeReplicator ) ); endGroup( "Placement Radius" ); addGroup( "Restraints" ); addField( "AllowOnTerrain", TypeBool, Offset( mFieldData.mAllowOnTerrain, fxShapeReplicator ) ); addField( "AllowOnInteriors", TypeBool, Offset( mFieldData.mAllowOnInteriors, fxShapeReplicator ) ); addField( "AllowOnStatics", TypeBool, Offset( mFieldData.mAllowStatics, fxShapeReplicator ) ); addField( "AllowOnWater", TypeBool, Offset( mFieldData.mAllowOnWater, fxShapeReplicator ) ); addField( "AllowWaterSurface", TypeBool, Offset( mFieldData.mAllowWaterSurface, fxShapeReplicator ) ); addField( "AlignToTerrain", TypeBool, Offset( mFieldData.mAlignToTerrain, fxShapeReplicator ) ); addField( "Interactions", TypeBool, Offset( mFieldData.mInteractions, fxShapeReplicator ) ); addField( "AllowedTerrainSlope",TypeS32, Offset( mFieldData.mAllowedTerrainSlope, fxShapeReplicator ) ); addField( "TerrainAlignment", TypePoint3F, Offset( mFieldData.mTerrainAlignment, fxShapeReplicator ) ); endGroup( "Restraints" ); addGroup( "Object Transforms" ); addField( "FixShapeAspect", TypeBool, Offset( mFieldData.mFixShapeAspect, fxShapeReplicator ) ); addField( "ShapeScaleMin", TypePoint3F, Offset( mFieldData.mShapeScaleMin, fxShapeReplicator ) ); addField( "ShapeScaleMax", TypePoint3F, Offset( mFieldData.mShapeScaleMax, fxShapeReplicator ) ); addField( "ShapeRotateMin", TypePoint3F, Offset( mFieldData.mShapeRotateMin, fxShapeReplicator ) ); addField( "ShapeRotateMax", TypePoint3F, Offset( mFieldData.mShapeRotateMax, fxShapeReplicator ) ); addField( "OffsetZ", TypeS32, Offset( mFieldData.mOffsetZ, fxShapeReplicator ) ); endGroup( "Object Transforms" ); } //------------------------------------------------------------------------------ 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; // Don't create shapes if we are hiding replications. if (mFieldData.mHideReplications) return; // Cannot continue without shapes! if (mFieldData.mShapeFile == "") return; // Check that we can position somewhere! if (!( mFieldData.mAllowOnTerrain || mFieldData.mAllowOnInteriors || mFieldData.mAllowStatics || mFieldData.mAllowOnWater)) { // Problem ... Con::warnf(ConsoleLogEntry::General, "[%s] - Could not place object, All alloweds are off!", getName()); // Return here. return; } // Check Shapes. AssertFatal(mCurrentShapeCount==0,"Shapes already present, this should not be possible!") // Check that we have a shape... if (!mFieldData.mShapeFile) return; // Set Seed. RandomGen.setSeed(mFieldData.mSeed); // Set shape vector. mReplicatedShapes.reserve(mFieldData.mShapeCount); // Add shapes. for (U32 idx = 0; idx < mFieldData.mShapeCount; idx++) { fxShapeReplicatedStatic* fxStatic; // Create our static shape. fxStatic = new fxShapeReplicatedStatic(); // Set the 'shapeName' field. fxStatic->setField("shapeName", mFieldData.mShapeFile); // Is this Replicator on the Server? if (isServerObject()) // Yes, so stop it from Ghosting. (Hack, Hack, Hack!) fxStatic->touchNetFlags(Ghostable, false); else // No, so flag as ghost object. (Another damn Hack!) fxStatic->touchNetFlags(IsGhost, true); // Register the Object. if (!fxStatic->registerObject()) { // Problem ... Con::warnf(ConsoleLogEntry::General, "[%s] - Could not load shape file '%s'!", getName(), mFieldData.mShapeFile); // Destroy Shape. delete fxStatic; // Destroy existing hapes. DestroyShapes(); // Quit. return; } // Get Allocated Shape. pShape = fxStatic->getShape(); // Reset Relocation Retry. RelocationRetry = mFieldData.mShapeRetries; // Find it a home ... do { // Calculate a random offset HypX = RandomGen.randF(mFieldData.mInnerRadiusX, mFieldData.mOuterRadiusX); HypY = RandomGen.randF(mFieldData.mInnerRadiusY, mFieldData.mOuterRadiusY); Angle = RandomGen.randF(0, M_2PI); // Calculate the new random position (in local space). Point3F randomShapePosLocal; randomShapePosLocal.x = HypX * mCos(Angle); randomShapePosLocal.y = HypY * mSin(Angle); // Transform into world space coordinates Point3F shapePosWorld; MatrixF objToWorld = getRenderTransform(); objToWorld.mulP(randomShapePosLocal, &shapePosWorld); ShapePosition = shapePosWorld; // Initialise RayCast Search Start/End Positions. ShapeStart = ShapeEnd = ShapePosition; ShapeStart.z = 2000.f; ShapeEnd.z= -2000.f; // Is this the Server? if (isServerObject()) // Perform Ray Cast Collision on Server Terrain. CollisionResult = gServerContainer.castRay(ShapeStart, ShapeEnd, FXREPLICATOR_COLLISION_MASK, &RayEvent); else // Perform Ray Cast Collision on Client Terrain. CollisionResult = gClientContainer.castRay( ShapeStart, ShapeEnd, FXREPLICATOR_COLLISION_MASK, &RayEvent); // Did we hit anything? if (CollisionResult) { // For now, let's pretend we didn't get a collision. CollisionResult = false; // Yes, so get it's type. U32 CollisionType = RayEvent.object->getTypeMask(); // Check Illegal Placements. if (((CollisionType & TerrainObjectType) && !mFieldData.mAllowOnTerrain) || ((CollisionType & InteriorObjectType) && !mFieldData.mAllowOnInteriors) || ((CollisionType & StaticTSObjectType) && !mFieldData.mAllowStatics) || ((CollisionType & WaterObjectType) && !mFieldData.mAllowOnWater) ) continue; // If we collided with water and are not allowing on the water surface then let's find the // terrain underneath and pass this on as the original collision else fail. // // NOTE:- We need to do this on the server/client as appropriate. if ((CollisionType & WaterObjectType) && !mFieldData.mAllowWaterSurface) { // Is this the Server? if (isServerObject()) { // Yes, so do it on the server container. if (!gServerContainer.castRay( ShapeStart, ShapeEnd, FXREPLICATOR_NOWATER_COLLISION_MASK, &RayEvent)) continue; } else { // No, so do it on the client container. if (!gClientContainer.castRay( ShapeStart, ShapeEnd, FXREPLICATOR_NOWATER_COLLISION_MASK, &RayEvent)) continue; } } // We passed with flying colours so carry on. CollisionResult = true; } // Invalidate if we are below Allowed Terrain Angle. if (RayEvent.normal.z < mSin(mDegToRad(90.0f-mFieldData.mAllowedTerrainSlope))) CollisionResult = false; // Wait until we get a collision. } while(!CollisionResult && --RelocationRetry); // Check for Relocation Problem. if (RelocationRetry > 0) { // Adjust Impact point. RayEvent.point.z += mFieldData.mOffsetZ; // Set New Position. ShapePosition = RayEvent.point; } else { // Warning. Con::warnf(ConsoleLogEntry::General, "[%s] - Could not find satisfactory position for shape '%s' on %s!", getName(), mFieldData.mShapeFile,isServerObject()?"Server":"Client"); // Unregister Object. fxStatic->unregisterObject(); // Destroy Shape. delete fxStatic; // Skip to next. continue; } // Get Shape Transform. MatrixF XForm = fxStatic->getTransform(); // Are we aligning to Terrain? if (mFieldData.mAlignToTerrain) { // Yes, so set rotation to Terrain Impact Normal. ShapeRotation = RayEvent.normal * mFieldData.mTerrainAlignment; } else { // No, so choose a new Rotation (in Radians). ShapeRotation.set( mDegToRad(RandomGen.randF(mFieldData.mShapeRotateMin.x, mFieldData.mShapeRotateMax.x)), mDegToRad(RandomGen.randF(mFieldData.mShapeRotateMin.y, mFieldData.mShapeRotateMax.y)), mDegToRad(RandomGen.randF(mFieldData.mShapeRotateMin.z, mFieldData.mShapeRotateMax.z))); } // Set Quaternion Roation. QRotation.set(ShapeRotation); // Set Transform Rotation. QRotation.setMatrix(&XForm); // Set Position. XForm.setColumn(3, ShapePosition); // Set Shape Position / Rotation. fxStatic->setTransform(XForm); // Choose a new Scale. F32 scaleX, scaleY, scaleZ; // Choose a random X-Scale. scaleX = RandomGen.randF(mFieldData.mShapeScaleMin.x, mFieldData.mShapeScaleMax.x); // Fix Shape Aspect? if ( mFieldData.mFixShapeAspect ) { // Yes, so use Scale-X for all axis. scaleY = scaleZ = scaleX; } else { // No, so choose Scale-Y/Z. scaleY = RandomGen.randF(mFieldData.mShapeScaleMin.y, mFieldData.mShapeScaleMax.y); scaleZ = RandomGen.randF(mFieldData.mShapeScaleMin.z, mFieldData.mShapeScaleMax.z); } // Set Shape Scale. ShapeScale.set( scaleX, scaleY, scaleZ ); // Set Shape Scale. fxStatic->setScale(ShapeScale); // Lock it. fxStatic->setLocked(true); // Store Shape in Replicated Shapes Vector. mReplicatedShapes[mCurrentShapeCount++] = fxStatic; } } //------------------------------------------------------------------------------ void fxShapeReplicator::DestroyShapes(void) { // Finish if we didn't create any shapes. if (mCurrentShapeCount == 0) return; // Remove shapes. for (U32 idx = 0; idx < mCurrentShapeCount; idx++) { fxShapeReplicatedStatic* fxStatic; // Fetch the Shape Object. fxStatic = mReplicatedShapes[idx]; // Got a Shape? if (fxStatic) { // Unlock it. fxStatic->setLocked(false); // Unregister the object. fxStatic->unregisterObject(); // Delete it. delete fxStatic; } } // Empty the Replicated Shapes Vector. mReplicatedShapes.empty(); // Reset Shape Count. mCurrentShapeCount = 0; } //------------------------------------------------------------------------------ void fxShapeReplicator::RenewShapes(void) { // Destroy any shapes. DestroyShapes(); // Don't create shapes on the Server if we don't need interactions. if (isServerObject() && !mFieldData.mInteractions) return; // Create Shapes. CreateShapes(); } //------------------------------------------------------------------------------ void fxShapeReplicator::StartUp(void) { // Flag, Client Replication Started. mClientReplicationStarted = true; // Renew shapes. RenewShapes(); } //------------------------------------------------------------------------------ bool fxShapeReplicator::onAdd() { if(!Parent::onAdd()) return(false); // Add the Replicator to the Replicator Set. dynamic_cast(Sim::findObject("fxReplicatorSet"))->addObject(this); // Set Default Object Box. mObjBox.min.set( -0.5, -0.5, -0.5 ); mObjBox.max.set( 0.5, 0.5, 0.5 ); // Reset the World Box. resetWorldBox(); // Are we editing the Mission? if(gEditingMission) { // Yes, so set the Render Transform. setRenderTransform(mObjToWorld); // Add to Scene. addToScene(); mAddedToScene = true; // If we are in the editor and we are on the client then // we can manually startup replication. if (isClientObject()) mClientReplicationStarted = true; } // Renew shapes on Server. if (isServerObject()) RenewShapes(); // Return OK. return(true); } //------------------------------------------------------------------------------ void fxShapeReplicator::onRemove() { // Remove the Replicator from the Replicator Set. dynamic_cast(Sim::findObject("fxReplicatorSet"))->removeObject(this); // Are we editing the Mission? if(gEditingMission) { // Yes, so remove from Scene. removeFromScene(); mAddedToScene = false; } // Destroy Shapes. DestroyShapes(); // Do Parent. Parent::onRemove(); } //------------------------------------------------------------------------------ void fxShapeReplicator::inspectPostApply() { // Set Parent. Parent::inspectPostApply(); // Renew Shapes. RenewShapes(); // Set Replication Mask. setMaskBits(ReplicationMask); } //------------------------------------------------------------------------------ void fxShapeReplicator::onEditorEnable() { // Are we in the Scene? if(!mAddedToScene) { // No, so add to scene. addToScene(); mAddedToScene = true; } } //------------------------------------------------------------------------------ void fxShapeReplicator::onEditorDisable() { // Are we in the Scene? if(mAddedToScene) { // Yes, so remove from scene. removeFromScene(); mAddedToScene = false; } } //------------------------------------------------------------------------------ ConsoleFunction(StartClientReplication, void, 1, 1, "StartClientReplication()") { // Find the Replicator Set. SimSet *fxReplicatorSet = dynamic_cast(Sim::findObject("fxReplicatorSet")); // Return if Error. if (!fxReplicatorSet) return; // StartUp Replication Object. for (SimSetIterator itr(fxReplicatorSet); *itr; ++itr) { // Fetch the Replicator Object. fxShapeReplicator* Replicator = static_cast(*itr); // Start Client Objects Only. if (Replicator->isClientObject()) Replicator->StartUp(); } // Info ... Con::printf("Client Replication Startup has Happened!"); } //------------------------------------------------------------------------------ bool fxShapeReplicator::prepRenderImage(SceneState* state, const U32 stateKey, const U32 startZone, const bool modifyBaseZoneState) { // Return if last state. if (isLastState(state, stateKey)) return false; // Set Last State. setLastState(state, stateKey); // Is Object Rendered? if (state->isObjectRendered(this)) { // Yes, so get a SceneRenderImage. SceneRenderImage* image = new SceneRenderImage; // Populate it. image->obj = this; image->sortType = SceneRenderImage::Normal; // Insert it into the scene images. state->insertRenderImage(image); } return false; } //------------------------------------------------------------------------------ void fxShapeReplicator::renderObject(SceneState* state, SceneRenderImage*) { // Return if placement area not needed. if (!mFieldData.mShowPlacementArea) return; // Check we are in Canonical State. AssertFatal(dglIsInCanonicalState(), "Error, GL not in canonical state on entry"); // Setup out the Projection Matrix/Viewport. RectI viewport; glMatrixMode(GL_PROJECTION); glPushMatrix(); dglGetViewport(&viewport); state->setupBaseProjection(); // Setup our rendering state. glPushMatrix(); dglMultMatrix(&getTransform()); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA); // Do we need to draw the Inner Radius? if (mFieldData.mInnerRadiusX || mFieldData.mInnerRadiusY) { // Yes, so draw Inner Radius. glBegin(GL_TRIANGLE_STRIP); for (U32 Angle = mCreationAreaAngle; Angle < (mCreationAreaAngle+360); Angle++) { F32 XPos, YPos; // Calculate Position. XPos = mFieldData.mInnerRadiusX * mCos(mDegToRad(-(F32)Angle)); YPos = mFieldData.mInnerRadiusY * mSin(mDegToRad(-(F32)Angle)); // Set Colour. glColor4f( mFieldData.mPlaceAreaColour.red, mFieldData.mPlaceAreaColour.green, mFieldData.mPlaceAreaColour.blue, AREA_ANIMATION_ARC * (Angle-mCreationAreaAngle)); // Draw Arc Line. glVertex3f( XPos, YPos, -(F32)mFieldData.mPlacementBandHeight/2.0f); glVertex3f( XPos, YPos, +(F32)mFieldData.mPlacementBandHeight/2.0f); } glEnd(); } // Do we need to draw the Outer Radius? if (mFieldData.mOuterRadiusX || mFieldData.mOuterRadiusY) { // Yes, so draw Outer Radius. glBegin(GL_TRIANGLE_STRIP); for (U32 Angle = mCreationAreaAngle; Angle < (mCreationAreaAngle+360); Angle++) { F32 XPos, YPos; // Calculate Position. XPos = mFieldData.mOuterRadiusX * mCos(mDegToRad(-(F32)Angle)); YPos = mFieldData.mOuterRadiusY * mSin(mDegToRad(-(F32)Angle)); // Set Colour. glColor4f( mFieldData.mPlaceAreaColour.red, mFieldData.mPlaceAreaColour.green, mFieldData.mPlaceAreaColour.blue, AREA_ANIMATION_ARC * (Angle-mCreationAreaAngle)); // Draw Arc Line. glVertex3f( XPos, YPos, -(F32)mFieldData.mPlacementBandHeight/2.0f); glVertex3f( XPos, YPos, +(F32)mFieldData.mPlacementBandHeight/2.0f); } glEnd(); } // Restore rendering state. glDisable(GL_BLEND); glPopMatrix(); // Animate Area Selection. mCreationAreaAngle += 20; mCreationAreaAngle = mCreationAreaAngle % 360; // Restore out nice and friendly canonical state. glMatrixMode(GL_PROJECTION); glPopMatrix(); glMatrixMode(GL_MODELVIEW); dglSetViewport(viewport); // Check we have restored Canonical State. AssertFatal(dglIsInCanonicalState(), "Error, GL not in canonical state on exit"); } //------------------------------------------------------------------------------ U32 fxShapeReplicator::packUpdate(NetConnection * con, U32 mask, BitStream * stream) { // Pack Parent. U32 retMask = Parent::packUpdate(con, mask, stream); // 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. stream->writeString(mFieldData.mShapeFile); stream->writeInt(mFieldData.mInnerRadiusX, 32); // Shapes Inner Radius X. stream->writeInt(mFieldData.mInnerRadiusY, 32); // Shapes Inner Radius Y. stream->writeInt(mFieldData.mOuterRadiusX, 32); // Shapes Outer Radius X. stream->writeInt(mFieldData.mOuterRadiusY, 32); // Shapes Outer Radius Y. mathWrite(*stream, mFieldData.mShapeScaleMin); // Shapes Scale Min. mathWrite(*stream, mFieldData.mShapeScaleMax); // Shapes Scale Max. mathWrite(*stream, mFieldData.mShapeRotateMin); // Shapes Rotate Min. mathWrite(*stream, mFieldData.mShapeRotateMax); // Shapes Rotate Max. stream->writeSignedInt(mFieldData.mOffsetZ, 32); // Shapes Offset Z. stream->writeFlag(mFieldData.mAllowOnTerrain); // Allow on Terrain. stream->writeFlag(mFieldData.mAllowOnInteriors); // Allow on Interiors. stream->writeFlag(mFieldData.mAllowStatics); // Allow on Statics. stream->writeFlag(mFieldData.mAllowOnWater); // Allow on Water. stream->writeFlag(mFieldData.mAllowWaterSurface); // Allow on Water Surface. stream->writeSignedInt(mFieldData.mAllowedTerrainSlope, 32); // Shapes Offset Z. stream->writeFlag(mFieldData.mAlignToTerrain); // Shapes AlignToTerrain. mathWrite(*stream, mFieldData.mTerrainAlignment); // Write Terrain Alignment. stream->writeFlag(mFieldData.mHideReplications); // Hide Replications. stream->writeFlag(mFieldData.mInteractions); // Shape Interactions. stream->writeFlag(mFieldData.mShowPlacementArea); // Show Placement Area Flag. stream->writeInt(mFieldData.mPlacementBandHeight, 32); // Placement Area Height. stream->write(mFieldData.mPlaceAreaColour); } // Were done ... return(retMask); } //------------------------------------------------------------------------------ void fxShapeReplicator::unpackUpdate(NetConnection * con, BitStream * stream) { // Unpack Parent. Parent::unpackUpdate(con, stream); // 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. mFieldData.mShapeFile = stream->readSTString(); // Shape File. mFieldData.mInnerRadiusX = stream->readInt(32); // Shapes Inner Radius X. mFieldData.mInnerRadiusY = stream->readInt(32); // Shapes Inner Radius Y. mFieldData.mOuterRadiusX = stream->readInt(32); // Shapes Outer Radius X. mFieldData.mOuterRadiusY = stream->readInt(32); // Shapes Outer Radius Y. mathRead(*stream, &mFieldData.mShapeScaleMin); // Shapes Scale Min. mathRead(*stream, &mFieldData.mShapeScaleMax); // Shapes Scale Max. mathRead(*stream, &mFieldData.mShapeRotateMin); // Shapes Rotate Min. mathRead(*stream, &mFieldData.mShapeRotateMax); // Shapes Rotate Max. mFieldData.mOffsetZ = stream->readSignedInt(32); // Shapes Offset Z. mFieldData.mAllowOnTerrain = stream->readFlag(); // Allow on Terrain. mFieldData.mAllowOnInteriors = stream->readFlag(); // Allow on Interiors. mFieldData.mAllowStatics = stream->readFlag(); // Allow on Statics. mFieldData.mAllowOnWater = stream->readFlag(); // Allow on Water. mFieldData.mAllowWaterSurface = stream->readFlag(); // Allow on Water Surface. mFieldData.mAllowedTerrainSlope = stream->readSignedInt(32); // Allowed Terrain Slope. mFieldData.mAlignToTerrain = stream->readFlag(); // Read AlignToTerrain. mathRead(*stream, &mFieldData.mTerrainAlignment); // Read Terrain Alignment. mFieldData.mHideReplications = stream->readFlag(); // Hide Replications. mFieldData.mInteractions = stream->readFlag(); // Read Interactions. mFieldData.mShowPlacementArea = stream->readFlag(); // Show Placement Area Flag. mFieldData.mPlacementBandHeight = stream->readInt(32); // Placement Area Height. stream->read(&mFieldData.mPlaceAreaColour); // Set Transform. setTransform(ReplicatorObjectMatrix); // Renew Shapes (only if replication has started). if (mClientReplicationStarted) RenewShapes(); } }