Invisible Wall
by Ronan · 03/25/2017 (5:06 pm) · 2 comments
Seeing the need for an invisible wall in T3D I made this adaptation based on PhysicalZone I hope it caters to all.
invisibleWall.h
invisibleWall.cpp
in file T3D/player.cpp
change
to
find function
Point3F Player::_move( const F32 travelTime, Collision *outCol )
replace
with
in T3d/physics/bt/btPlayer.cpp replace class BtPlayerSweepCallback with this
to add in world editor
in tools/worldeditor/scripts/editors/creator.ed.cs add in level group
in tools/worldeditor/gui/objectBuilderGui.ed.gui add
Use this function to toggle wall transposition using variable
$IgnoreWall = true; // Player can cross the wall
$IgnoreWall = false; // Player can not cross wall
To prevent errors, the wall depth should be as large as possible!
I hope I have helped :)
invisibleWall.h
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _H_INVISIBLEWALL
#define _H_INVISIBLEWALL
#ifndef _SCENEOBJECT_H_
#include "scene/sceneObject.h"
#endif
#ifndef _EARLYOUTPOLYLIST_H_
#include "collision/earlyOutPolyList.h"
#endif
#ifndef _MPOLYHEDRON_H_
#include "math/mPolyhedron.h"
#endif
class Convex;
class PhysicsBody;
class InvisibleWall : public SceneObject
{
typedef SceneObject Parent;
enum UpdateMasks {
ActiveMask = Parent::NextFreeMask << 0,
NextFreeMask = Parent::NextFreeMask << 1
};
protected:
static bool smRenderWall;
// Basically ripped from trigger
Polyhedron mPolyhedron;
EarlyOutPolyList mClippedList;
bool mActive;
PhysicsBody *mPhysicsRep;
Convex* mConvexList;
void buildConvex(const Box3F& box, Convex* convex);
public:
InvisibleWall();
~InvisibleWall();
// SimObject
DECLARE_CONOBJECT(InvisibleWall);
static void consoleInit();
static void initPersistFields();
bool onAdd();
void onRemove();
void inspectPostApply();
// NetObject
U32 packUpdate (NetConnection *conn, U32 mask, BitStream *stream);
void unpackUpdate(NetConnection *conn, BitStream *stream);
// SceneObject
void setTransform(const MatrixF &&mat);
void prepRenderImage( SceneRenderState* state );
void setPolyhedron(const Polyhedron&);
bool testObject(SceneObject*);
bool testBox( const Box3F &box ) const;
void renderObject( ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance *overrideMat );
void activate();
void deactivate();
inline bool isActive() const { return mActive; }
bool isInvisibleWall(SceneObject* who);
PhysicsBody* getPhysicsRep();
};
#endif // _H_INVISIBLEWALLinvisibleWall.cpp
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "T3D/invisibleWall.h"
#include "core/stream/bitStream.h"
#include "collision/boxConvex.h"
#include "collision/clippedPolyList.h"
#include "console/consoleTypes.h"
#include "math/mathIO.h"
#include "scene/sceneRenderState.h"
#include "T3D/trigger.h"
#include "gfx/gfxTransformSaver.h"
#include "renderInstance/renderPassManager.h"
#include "gfx/gfxDrawUtil.h"
#include "console/engineAPI.h"
#include "physics/physicsPlugin.h"
#include "physics/physicsBody.h"
#include "physics/physicsCollision.h"
IMPLEMENT_CO_NETOBJECT_V1(InvisibleWall);
bool InvisibleWall::smRenderWall = true;
DefineEngineMethod(InvisibleWall, activate, void, (),, "Activate the wall.n")
{
if (object->isClientObject())
return;
object->activate();
}
DefineEngineMethod(InvisibleWall, deactivate, void, (),, "Deactivate the wall.n" )
{
if (object->isClientObject())
return;
object->deactivate();
}
//--------------------------------------------------------------------------
//--------------------------------------
//
InvisibleWall::InvisibleWall()
{
mNetFlags.set(Ghostable | ScopeAlways);
mTypeMask |= InvisibleWallObjectType;
mConvexList = new Convex;
mActive = true;
mPhysicsRep = NULL;
}
InvisibleWall::~InvisibleWall()
{
delete mConvexList;
mConvexList = NULL;
}
//--------------------------------------------------------------------------
void InvisibleWall::consoleInit()
{
Con::addVariable( "$InvisibleWall::renderWall", TypeBool, &&smRenderWall, "If true, a box will render around the location of all walls.n");
}
void InvisibleWall::initPersistFields()
{
addGroup("Misc");
addField("polyhedron", TypeTriggerPolyhedron, Offset(mPolyhedron, InvisibleWall),
"The polyhedron type is really a quadrilateral and consists of a corner"
"point followed by three vectors representing the edges extending from the corner." );
endGroup("Misc");
Parent::initPersistFields();
}
//--------------------------------------------------------------------------
bool InvisibleWall::onAdd()
{
if(!Parent::onAdd())
return false;
Polyhedron temp = mPolyhedron;
setPolyhedron(temp);
addToScene();
if ( PHYSICSMGR/* && isServerObject() */)
{
PhysicsCollision *colShape = PHYSICSMGR->createCollision();
Box3F scaledBox = mObjBox;
scaledBox.minExtents.convolve(mObjScale);
scaledBox.maxExtents.convolve(mObjScale);
colShape->addBox( scaledBox.getExtents() * 0.5f, MatrixF::Identity );
PhysicsWorld *world = PHYSICSMGR->getWorld( isServerObject() ? "server" : "client" );
mPhysicsRep = PHYSICSMGR->createBody();
mPhysicsRep->init( colShape, 0, PhysicsBody::BF_TRIGGER | PhysicsBody::BF_KINEMATIC, this, world );
MatrixF tr = getTransform();
MatrixF localTr(true);
localTr.setPosition(scaledBox.getCenter());
tr = tr*localTr;
mPhysicsRep->setTransform(tr);
}
return true;
}
void InvisibleWall::onRemove()
{
SAFE_DELETE(mPhysicsRep);
mConvexList->nukeList();
removeFromScene();
Parent::onRemove();
}
PhysicsBody* InvisibleWall::getPhysicsRep()
{
return mPhysicsRep;
}
void InvisibleWall::inspectPostApply()
{
setPolyhedron(mPolyhedron);
Parent::inspectPostApply();
}
//------------------------------------------------------------------------------
void InvisibleWall::setTransform(const MatrixF && mat)
{
Parent::setTransform(mat);
MatrixF base(true);
base.scale(Point3F(1.0/mObjScale.x,
1.0/mObjScale.y,
1.0/mObjScale.z));
base.mul(mWorldToObj);
mClippedList.setBaseTransform(base);
if (isServerObject())
setMaskBits(InitialUpdateMask);
}
extern bool gEditorEnabled;
void InvisibleWall::prepRenderImage( SceneRenderState *state )
{
// only render if selected or render flag is set
if (!gEditorEnabled || !smRenderWall )
return;
ObjectRenderInst *ri = state->getRenderPass()->allocInst<ObjectRenderInst>();
ri->renderDelegate.bind( this, &&InvisibleWall::renderObject );
ri->type = RenderPassManager::RIT_Editor;
ri->defaultKey = 0;
ri->defaultKey2 = 0;
state->getRenderPass()->addInst( ri );
}
void InvisibleWall::renderObject( ObjectRenderInst *ri,
SceneRenderState *state,
BaseMatInstance *overrideMat )
{
if (overrideMat)
return;
GFXStateBlockDesc desc;
desc.setZReadWrite( true, false );
desc.setBlend( true );
desc.setCullMode( GFXCullNone );
GFXTransformSaver saver;
MatrixF mat = getRenderTransform();
mat.scale( getScale() );
GFX->multWorld( mat );
GFXDrawUtil *drawer = GFX->getDrawUtil();
drawer->drawPolyhedron( desc, mPolyhedron, ColorI( 255, 0, 0, 45 ) );
desc.setFillModeWireframe();
drawer->drawPolyhedron( desc, mPolyhedron, ColorF::BLACK );
}
//--------------------------------------------------------------------------
U32 InvisibleWall::packUpdate(NetConnection* con, U32 mask, BitStream* stream)
{
U32 i;
U32 retMask = Parent::packUpdate(con, mask, stream);
if (stream->writeFlag((mask && InitialUpdateMask) != 0)) {
// Note that we don't really care about efficiency here, since this is an
// edit-only ghost...
mathWrite(*stream, mObjToWorld);
mathWrite(*stream, mObjScale);
// Write the polyhedron
stream->write(mPolyhedron.pointList.size());
for (i = 0; i < mPolyhedron.pointList.size(); i++)
mathWrite(*stream, mPolyhedron.pointList[i]);
stream->write(mPolyhedron.planeList.size());
for (i = 0; i < mPolyhedron.planeList.size(); i++)
mathWrite(*stream, mPolyhedron.planeList[i]);
stream->write(mPolyhedron.edgeList.size());
for (i = 0; i < mPolyhedron.edgeList.size(); i++) {
const Polyhedron::Edge& rEdge = mPolyhedron.edgeList[i];
stream->write(rEdge.face[0]);
stream->write(rEdge.face[1]);
stream->write(rEdge.vertex[0]);
stream->write(rEdge.vertex[1]);
}
stream->writeFlag(mActive);
} else {
stream->writeFlag(mActive);
}
return retMask;
}
void InvisibleWall::unpackUpdate(NetConnection* con, BitStream* stream)
{
Parent::unpackUpdate(con, stream);
if (stream->readFlag()) {
U32 i, size;
MatrixF temp;
Point3F tempScale;
Polyhedron tempPH;
// Transform
mathRead(*stream, &temp);
mathRead(*stream, &tempScale);
// Read the polyhedron
stream->read(&&size);
tempPH.pointList.setSize(size);
for (i = 0; i < tempPH.pointList.size(); i++)
mathRead(*stream, &tempPH.pointList[i]);
stream->read(&&size);
tempPH.planeList.setSize(size);
for (i = 0; i < tempPH.planeList.size(); i++)
mathRead(*stream, &tempPH.planeList[i]);
stream->read(&&size);
tempPH.edgeList.setSize(size);
for (i = 0; i < tempPH.edgeList.size(); i++) {
Polyhedron::Edge& rEdge = tempPH.edgeList[i];
stream->read(&&rEdge.face[0]);
stream->read(&&rEdge.face[1]);
stream->read(&&rEdge.vertex[0]);
stream->read(&&rEdge.vertex[1]);
}
setPolyhedron(tempPH);
setScale(tempScale);
setTransform(temp);
mActive = stream->readFlag();
} else {
mActive = stream->readFlag();
}
}
//--------------------------------------------------------------------------
void InvisibleWall::setPolyhedron(const Polyhedron& rPolyhedron)
{
mPolyhedron = rPolyhedron;
if (mPolyhedron.pointList.size() != 0) {
mObjBox.minExtents.set(1e10, 1e10, 1e10);
mObjBox.maxExtents.set(-1e10, -1e10, -1e10);
for (U32 i = 0; i < mPolyhedron.pointList.size(); i++) {
mObjBox.minExtents.setMin(mPolyhedron.pointList[i]);
mObjBox.maxExtents.setMax(mPolyhedron.pointList[i]);
}
} else {
mObjBox.minExtents.set(-0.5, -0.5, -0.5);
mObjBox.maxExtents.set( 0.5, 0.5, 0.5);
}
MatrixF xform = getTransform();
setTransform(xform);
mClippedList.clear();
mClippedList.mPlaneList = mPolyhedron.planeList;
MatrixF base(true);
base.scale(Point3F(1.0/mObjScale.x,
1.0/mObjScale.y,
1.0/mObjScale.z));
base.mul(mWorldToObj);
mClippedList.setBaseTransform(base);
}
//--------------------------------------------------------------------------
void InvisibleWall::buildConvex(const Box3F& box, Convex* convex)
{
// These should really come out of a pool
mConvexList->collectGarbage();
Box3F realBox = box;
mWorldToObj.mul(realBox);
realBox.minExtents.convolveInverse(mObjScale);
realBox.maxExtents.convolveInverse(mObjScale);
if (realBox.isOverlapped(getObjBox()) == false)
return;
// Just return a box convex for the entire shape...
Convex* cc = 0;
CollisionWorkingList& wl = convex->getWorkingList();
for (CollisionWorkingList* itr = wl.wLink.mNext; itr != &wl; itr = itr->wLink.mNext) {
if (itr->mConvex->getType() == BoxConvexType &&
itr->mConvex->getObject() == this) {
cc = itr->mConvex;
break;
}
}
if (cc)
return;
// Create a new convex.
BoxConvex* cp = new BoxConvex;
mConvexList->registerObject(cp);
convex->addToWorkingList(cp);
cp->init(this);
mObjBox.getCenter(&&cp->mCenter);
cp->mSize.x = mObjBox.len_x() / 2.0f;
cp->mSize.y = mObjBox.len_y() / 2.0f;
cp->mSize.z = mObjBox.len_z() / 2.0f;
}
bool InvisibleWall::testObject(SceneObject* enter)
{
// TODO: This doesn't look like it's testing against the polyhedron at
// all. And whats the point of building a convex if no collision methods
// are implemented?
if (mPolyhedron.pointList.size() == 0)
return false;
mClippedList.clear();
SphereF sphere;
sphere.center = (mWorldBox.minExtents + mWorldBox.maxExtents) * 0.5;
VectorF bv = mWorldBox.maxExtents - sphere.center;
sphere.radius = bv.len();
enter->buildPolyList(PLC_Collision, &&mClippedList, mWorldBox, sphere);
return mClippedList.isEmpty() == false;
}
bool InvisibleWall::testBox( const Box3F &&box ) const
{
return mWorldBox.isOverlapped( box );
}
bool InvisibleWall::isInvisibleWall(SceneObject* who)
{
bool b = false;
if(who)
{
b = true;
const char* result = Con::executef("isWallForObject", this->getIdString(), who->getIdString());
if(dStricmp(result, "") > 0)
b = dAtob(result);
return b;
}
return false;
}
void InvisibleWall::activate()
{
if (mActive != true)
setMaskBits(ActiveMask);
mActive = true;
}
void InvisibleWall::deactivate()
{
if (mActive != false)
setMaskBits(ActiveMask);
mActive = false;
}in T3D/objectTypes.h add at bottomInvisibleWallObjectType = BIT(26),
in file T3D/player.cpp
change
static U32 sCollisionMoveMask = TerrainObjectType |
WaterObjectType |
PlayerObjectType |
StaticShapeObjectType |
VehicleObjectType |
PhysicalZoneObjectType;to
static U32 sCollisionMoveMask = TerrainObjectType |
WaterObjectType |
PlayerObjectType |
StaticShapeObjectType |
VehicleObjectType |
InvisibleWallObjectType |
PhysicalZoneObjectType; find function
Point3F Player::_move( const F32 travelTime, Collision *outCol )
replace
if (plistBox.isOverlapped(convexBox))
{
if (pConvex->getObject()->getTypeMask() & PhysicalZoneObjectType)
pConvex->getPolyList(&&sPhysZonePolyList);
else
pConvex->getPolyList(&&sExtrudedPolyList);
}with
if (plistBox.isOverlapped(convexBox))
{
if (pConvex->getObject()->getTypeMask() & InvisibleWallObjectType)
{
InvisibleWall* pWall = (InvisibleWall*)pConvex->getObject();
bool b = pWall->isInvisibleWall(this);
if (pWall->isActive() &&&& b)
pConvex->getPolyList(&&sExtrudedPolyList);
}
else
if (pConvex->getObject()->getTypeMask() & PhysicalZoneObjectType)
{
pConvex->getPolyList(&&sPhysZonePolyList);
}
else
pConvex->getPolyList(&&sExtrudedPolyList);
}in T3d/physics/bt/btPlayer.cpp replace class BtPlayerSweepCallback with this
class BtPlayerSweepCallback : public btCollisionWorld::ClosestConvexResultCallback
{
typedef btCollisionWorld::ClosestConvexResultCallback Parent;
public:
BtPlayerSweepCallback( btCollisionObject *me, const btVector3 &moveVec )
: Parent( btVector3(0.0, 0.0, 0.0), btVector3(0.0, 0.0, 0.0) ),
mMe( me ),
mMoveVec( moveVec )
{
}
virtual bool needsCollision(btBroadphaseProxy* proxy0) const
{
if ( proxy0->m_clientObject == mMe )
return false;
// behavior of invisible wall
btCollisionObject* colObj = (btCollisionObject*)(proxy0->m_clientObject);
SceneObject* sceneObj = PhysicsUserData::getObject(colObj->getUserPointer());
if (sceneObj && (sceneObj->getTypeMask() & InvisibleWallObjectType))
{
SceneObject* playerObject = PhysicsUserData::getObject(mMe->getUserPointer());
InvisibleWall* pZone = static_cast<InvisibleWall*>(sceneObj);
if (pZone->isActive() && pZone->isInvisibleWall(playerObject))
return true;
}
if ( !colObj->hasContactResponse() )
return false;
return Parent::needsCollision( proxy0 );
}
virtual btScalar addSingleResult( btCollisionWorld::LocalConvexResult &convexResult,
bool normalInWorldSpace )
{
// NOTE: I shouldn't have to do any of this, but Bullet
// has some weird bugs.
//
// For one the plane type will return hits on a Z up surface
// for sweeps that have no Z sweep component.
//
// Second the normal returned here is sometimes backwards
// to the sweep direction... no clue why.
//
F32 dotN = mMoveVec.dot( convexResult.m_hitNormalLocal );
if ( mFabs( dotN ) < 0.1f )
return 1.0f;
return Parent::addSingleResult( convexResult, normalInWorldSpace );
}
protected:
btVector3 mMoveVec;
btCollisionObject *mMe;
};to add in world editor
in tools/worldeditor/scripts/editors/creator.ed.cs add in level group
%this.registerMissionObject( "InvisibleWall", "Invisible Wall" );
in tools/worldeditor/gui/objectBuilderGui.ed.gui add
function ObjectBuilderGui::buildInvisibleWall(%this)
{
%this.objectClassName = "InvisibleWall";
%this.addField("polyhedron", "TypeTriggerPolyhedron", "Polyhedron", "-0.5 0.5 0.0 1.0 0.0 0.0 0.0 -1.0 0.0 0.0 0.0 1.0");
%this.process();
}Use this function to toggle wall transposition using variable
$IgnoreWall = false;
function isWallForObject(%wall, %obj)
{
return !$IgnoreWall;
}$IgnoreWall = true; // Player can cross the wall
$IgnoreWall = false; // Player can not cross wall
To prevent errors, the wall depth should be as large as possible!
I hope I have helped :)
#2
06/08/2017 (4:22 pm)
Nice, I will try it when I get the time, so far I used a physical zone with a push force applied pointing inside the level so players cannot go outside. 
Torque Owner DreamPharaoh
Gods and Nemesis