Add Radial Force and Vorticity to PhysicalZone
by Orion Elenzil · 05/23/2007 (10:13 pm) · 5 comments
See source code comments below for description.
Modifications based on TGE 1.3.5:
Basic familiarity with the TGE engine and C++ is assumed.
PhysicalZone.h
Add the following comments up at the top:
find the line "Point3F mAppliedForce;",
and put these lines after it:
find the line "const Point3F& getForce() const { return mAppliedForce; }",
and put these lines after it:
PhysicalZone.cc
add the following lines to the constructor:
add the following lines at the bottom of PhysicalZone::initPersistFields():
in PhysicalZone::onAdd(),
find the line "setPolyhedron(temp)",
and add these lines after it (before addToScene()):
in PhysicalZone::PackUpdate(),
find the line "stream->writeFlag(mActive);",
and add the following lines after it:
in PhysicalZone::unpackUpdate(),
find the line "mActive = stream->readFlag();",
and add the following lines after it:
add the following lines at the bottom of the file:
ShapeBase.cc
in physicalZoneFind(),
find the line "shape->mAppliedForce += pz->getForce();",
and after it add the following line:
.. and that's it !
Modifications based on TGE 1.3.5:
Basic familiarity with the TGE engine and C++ is assumed.
PhysicalZone.h
Add the following comments up at the top:
//-----------------------------------------------------------------------------
// Radial Force in Physical Zones.
// Orion Elenzil
// Doppelganger 200705
//
// This resource provides two new forces to PhysicalZones,
// in addition to the beloved AppliedForce, VelocityMod, and GravityMod.
//
// The first new force is Radial, in that it operates along the line
// between the player and the center of the physical zone,
// or optionally the center Z-Axis of the zone.
//
// The second is vorticity, which pushes the player in a circle
// tangentially to the Z-axis.
//
// The best way to picture these forces is as a hollow cylinder centered on the zone,
// with one force at the inner wall, another force at the outer wall,
// and interpolated forces in between.
//
// One of the simplest uses of this is to create a region which is very difficult to walk to.
// Say a magic altar. To achieve something like that, the inner wall of the cylinder would
// have radius zero and a strong force pushing outward. The outer wall would have radius
// of say five, and say zero force. When the player approaches the altar, they find that
// it gets more and more difficult to walk towards it.
//
// Another use could be similar to a black hole.
// To do this, the inner radius should be something small but positive like 0.5,
// and have a strong inward force, and the outer radius again has relatively small force.
//
// Other more esoteric possibilities exist as well.
//
// For example, one could create a hollow force-ring,
// where people inside can walk around but can't get out,
// and people outside can't get in.
//
// The inverse of that is attractive,
// where you create a ring which sucks people to its center,
// and then can walk around the ring but can't leave it.
//
// A vertical twister is easily achieved by combining a vertical VelocityMod
// with both radial attraction and vorticity.
//
// There is no visualization of the Radial Forces in the mission editor, sorry.
//
//
//
// Usage
// -----
// The radius & force associateed with inner & outer walls are specified as three-component points: [b]radialForcePt1[/b] and [b]radialForcePt2[/b].
// radialForcePt1.x is the radius at the inner wall
// radialForcePt1.y is the force at the inner wall
// radialForcePt1.z is the vorticity at the inner wall
// radialForcePt2.x is the radius at the outer wall
// radialForcePt2.y is the force at the outer wall
// radialForcePt2.y is the vorticity at the outer wall
//
// in a mission file for the black hole example, these might look like:
// new PhysicalZone(phzzy) {
// usual stuff ..
// radialForcePt1 = "0.5 -5000 3000";
// radialForcePt2 = "5 0 0";
// };
//
//
// another parameter which can be supplied is [b]radialForceCylindrical[/b].
// this parameter is a boolean and is default to [b]true[/b].
// if set to [b]false[/b], this parameter causes the radial force to act as spherical shells
// instead of cylindrical shells. i haven't experimented much/at all with this mode.
//
// vorticity is only applied when radialForceCylindrical is true.
//-----------------------------------------------------------------------------find the line "Point3F mAppliedForce;",
and put these lines after it:
Point3F mRadialForcePt1; Point3F mRadialForcePt2; bool mRadialForceCylindrical;
find the line "const Point3F& getForce() const { return mAppliedForce; }",
and put these lines after it:
VectorF getRadialForce (const Point3F& worldPoint); F32 getRadialForceMagnitude(F32 distance ); F32 getRadialForceOrbit (F32 distance );
PhysicalZone.cc
add the following lines to the constructor:
mRadialForcePt1.set(1.0f, 0.0f, 0.0f); mRadialForcePt2.set(3.0f, 0.0f, 0.0f); mRadialForceCylindrical = true;
add the following lines at the bottom of PhysicalZone::initPersistFields():
addGroup("Radial Force");
addField("radialForcePt1" , TypePoint3F , Offset(mRadialForcePt1 , PhysicalZone));
addField("radialForcePt2" , TypePoint3F , Offset(mRadialForcePt2 , PhysicalZone));
addField("radialForceCylindrical" , TypeBool , Offset(mRadialForceCylindrical, PhysicalZone));
endGroup("Radial Force");in PhysicalZone::onAdd(),
find the line "setPolyhedron(temp)",
and add these lines after it (before addToScene()):
//---------------------------------------------------------
// Radial Force Sanity Checks
if (mRadialForcePt1.x < 0)
{
Con::errorf("PhysicalZone: radialForcePt1 negative. Swapping.");
mRadialForcePt1.x *= -1.0f;
}
if (mRadialForcePt2.x < 0)
{
Con::errorf("PhysicalZone: radialForcePt2 negative. Swapping.");
mRadialForcePt2.x *= -1.0f;
}
if (mRadialForcePt1.x > mRadialForcePt2.x)
{
Con::errorf("PhysicalZone: radialForcePts out of order. Reordering");
Point3F tmp = mRadialForcePt1;
mRadialForcePt1 = mRadialForcePt2;
mRadialForcePt2 = tmp;
}
if (mRadialForcePt2.x - mRadialForcePt1.x < 0.0001f)
{
Con::errorf("PhysicalZone: radialForcePoints too close. Moving.");
mRadialForcePt2.x = mRadialForcePt1.x + 0.0002f;
}
//---------------------------------------------------------in PhysicalZone::PackUpdate(),
find the line "stream->writeFlag(mActive);",
and add the following lines after it:
// RadialForce
mathWrite(*stream, mRadialForcePt1);
mathWrite(*stream, mRadialForcePt2);
stream->write(mRadialForceCylindrical);in PhysicalZone::unpackUpdate(),
find the line "mActive = stream->readFlag();",
and add the following lines after it:
// RadialForce
mathRead(*stream, &mRadialForcePt1);
mathRead(*stream, &mRadialForcePt2);
stream->read(&mRadialForceCylindrical);add the following lines at the bottom of the file:
//--------------------------------------
VectorF PhysicalZone::getRadialForce(const Point3F& worldPoint)
{
Point3F pos;
getWorldBox().getCenter(&pos);
VectorF v = worldPoint - pos;
if (mRadialForceCylindrical)
v.z = 0.0f;
F32 vLen = v.len();
VectorF vNorm = v * 1.0f / vLen;
F32 forceMagnitude = getRadialForceMagnitude(vLen);
VectorF fm = vNorm * forceMagnitude;
VectorF fo(0.0f, 0.0f, 0.0f);
if (mRadialForceCylindrical)
{
F32 forceOrbit = getRadialForceOrbit(vLen);
VectorF vo = mCross(VectorF(0.0f, 0.0f, 1.0f), vNorm);
fo = vo * forceOrbit;
}
return fm + fo;
}
F32 PhysicalZone::getRadialForceMagnitude(F32 distance )
{
if (distance < mRadialForcePt1.x)
return 0.0f;
if (distance > mRadialForcePt2.x)
return 0.0f;
F32 radiusRange = mRadialForcePt2.x - mRadialForcePt1.x;
F32 forceRange = mRadialForcePt2.y - mRadialForcePt1.y;
AssertFatal((radiusRange < -0.00001f) || (radiusRange > 0.00001f), "radiusRange degenerate");
F32 distNormalized = (distance - mRadialForcePt1.x) / radiusRange;
// Good old linear interpolation.
// Something like cosine interpolation might be smoother/better, but linear is probably okay
F32 force = distNormalized * forceRange + mRadialForcePt1.y;
return force;
}
F32 PhysicalZone::getRadialForceOrbit(F32 distance )
{
if (distance < mRadialForcePt1.x)
return 0.0f;
if (distance > mRadialForcePt2.x)
return 0.0f;
F32 radiusRange = mRadialForcePt2.x - mRadialForcePt1.x;
F32 forceRange = mRadialForcePt2.z - mRadialForcePt1.z;
AssertFatal((radiusRange < -0.00001f) || (radiusRange > 0.00001f), "radiusRange degenerate");
F32 distNormalized = (distance - mRadialForcePt1.x) / radiusRange;
// Good old linear interpolation.
// Something like cosine interpolation might be smoother/better, but linear is probably okay
F32 force = distNormalized * forceRange + mRadialForcePt1.z;
return force;
}ShapeBase.cc
in physicalZoneFind(),
find the line "shape->mAppliedForce += pz->getForce();",
and after it add the following line:
shape->mAppliedForce += pz->getRadialForce(shape->getPosition());
.. and that's it !
#2
05/24/2007 (8:15 am)
sounds interesting! Do you have a video of that effect in action? :)
#3
05/24/2007 (11:41 am)
This is going to fit wonderfully into my space game.... Thanx!!!!
#4
05/25/2007 (9:06 am)
Very inspiring ressource.
#5
09/21/2007 (2:42 pm)
That's brilliant! Force-fields made easy, and I'm thinking about some cool grenade types...
Associate Fyodor "bank" Osokin
huge thanks for sharing this with the community, Orion!