123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608 |
- //-----------------------------------------------------------------------------
- // 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.
- //-----------------------------------------------------------------------------
- //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
- // Arcane-FX for MIT Licensed Open Source version of Torque 3D from GarageGames
- // Copyright (C) 2015 Faust Logic, Inc.
- //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
- #include "platform/platform.h"
- #include "T3D/groundPlane.h"
- #include "renderInstance/renderPassManager.h"
- #include "scene/sceneRenderState.h"
- #include "materials/sceneData.h"
- #include "materials/materialDefinition.h"
- #include "materials/materialManager.h"
- #include "materials/baseMatInstance.h"
- #include "math/util/frustum.h"
- #include "math/mPlane.h"
- #include "console/consoleTypes.h"
- #include "console/engineAPI.h"
- #include "core/stream/bitStream.h"
- #include "collision/boxConvex.h"
- #include "collision/abstractPolyList.h"
- #include "T3D/physics/physicsPlugin.h"
- #include "T3D/physics/physicsBody.h"
- #include "T3D/physics/physicsCollision.h"
- #ifdef TORQUE_AFX_ENABLED
- #include "afx/ce/afxZodiacMgr.h"
- #endif
- /// Minimum square size allowed. This is a cheap way to limit the amount
- /// of geometry possibly generated by the GroundPlane (vertex buffers have a
- /// limit, too). Dynamically clipping extents into range is a problem since the
- /// location of the horizon depends on the camera orientation. Just shifting
- /// squareSize as needed also doesn't work as that causes different geometry to
- /// be generated depending on the viewpoint and orientation which affects the
- /// texturing.
- static const F32 sMIN_SQUARE_SIZE = 16;
- IMPLEMENT_CO_NETOBJECT_V1( GroundPlane );
- ConsoleDocClass( GroundPlane,
- "@brief An infinite plane extending in all direction.\n\n"
-
- "%GroundPlane is useful for setting up simple testing scenes, or it can be "
- "placed under an existing scene to keep objects from falling into 'nothing'.\n\n"
- "%GroundPlane may not be moved or rotated, it is always at the world origin.\n\n"
- "@ingroup Terrain"
- );
- GroundPlane::GroundPlane()
- : mSquareSize( 128.0f ),
- mScaleU( 1.0f ),
- mScaleV( 1.0f ),
- mMaterial( NULL ),
- mMaterialInst(NULL),
- mPhysicsRep( NULL ),
- mMin( 0.0f, 0.0f ),
- mMax( 0.0f, 0.0f )
- {
- mTypeMask |= StaticObjectType | StaticShapeObjectType;
- mNetFlags.set( Ghostable | ScopeAlways );
- mConvexList = new Convex;
- mTypeMask |= TerrainLikeObjectType;
- INIT_ASSET(Material);
- }
- GroundPlane::~GroundPlane()
- {
- mMaterial = nullptr;
- if(mMaterialInst)
- SAFE_DELETE(mMaterialInst);
- mConvexList->nukeList();
- SAFE_DELETE( mConvexList );
- }
- void GroundPlane::initPersistFields()
- {
- docsURL;
- addGroup( "Plane" );
- addField( "squareSize", TypeF32, Offset( mSquareSize, GroundPlane ), "Square size in meters to which %GroundPlane subdivides its geometry." );
- addField( "scaleU", TypeF32, Offset( mScaleU, GroundPlane ), "Scale of texture repeat in the U direction." );
- addField( "scaleV", TypeF32, Offset( mScaleV, GroundPlane ), "Scale of texture repeat in the V direction." );
- INITPERSISTFIELD_MATERIALASSET(Material, GroundPlane, "The material used to render the ground plane.");
- endGroup( "Plane" );
-
- Parent::initPersistFields();
- removeField( "scale" );
- removeField( "position" );
- removeField( "rotation" );
- }
- bool GroundPlane::onAdd()
- {
- if( !Parent::onAdd() )
- return false;
- if( isClientObject() )
- _updateMaterial();
-
- if( mSquareSize < sMIN_SQUARE_SIZE )
- {
- Con::errorf( "GroundPlane - squareSize below threshold; re-setting to %.02f", sMIN_SQUARE_SIZE );
- mSquareSize = sMIN_SQUARE_SIZE;
- }
- Parent::setScale( VectorF( 1.0f, 1.0f, 1.0f ) );
- Parent::setTransform( MatrixF::Identity );
- setGlobalBounds();
- resetWorldBox();
- addToScene();
- if ( PHYSICSMGR )
- {
- PhysicsCollision *colShape = PHYSICSMGR->createCollision();
- colShape->addPlane( PlaneF( Point3F::Zero, Point3F( 0, 0, 1 ) ) );
- PhysicsWorld *world = PHYSICSMGR->getWorld( isServerObject() ? "server" : "client" );
- mPhysicsRep = PHYSICSMGR->createBody();
- mPhysicsRep->init( colShape, 0, 0, this, world );
- }
- return true;
- }
- void GroundPlane::onRemove()
- {
- if (!mMaterialAsset.isNull())
- AssetDatabase.releaseAsset(mMaterialAsset.getAssetId());
- //SAFE_DELETE(mMaterialInst);
- SAFE_DELETE( mPhysicsRep );
- removeFromScene();
- Parent::onRemove();
- }
- void GroundPlane::inspectPostApply()
- {
- Parent::inspectPostApply();
- setMaskBits( U32( -1 ) );
- if( mSquareSize < sMIN_SQUARE_SIZE )
- {
- Con::errorf( "GroundPlane - squareSize below threshold; re-setting to %.02f", sMIN_SQUARE_SIZE );
- mSquareSize = sMIN_SQUARE_SIZE;
- }
- setScale( VectorF( 1.0f, 1.0f, 1.0f ) );
- }
- void GroundPlane::setTransform( const MatrixF &mat )
- {
- // Ignore.
- }
- void GroundPlane::setScale( const Point3F& scale )
- {
- // Ignore.
- }
- U32 GroundPlane::packUpdate( NetConnection* connection, U32 mask, BitStream* stream )
- {
- U32 retMask = Parent::packUpdate( connection, mask, stream );
- stream->write( mSquareSize );
- stream->write( mScaleU );
- stream->write( mScaleV );
- PACK_ASSET(connection, Material);
- return retMask;
- }
- void GroundPlane::unpackUpdate( NetConnection* connection, BitStream* stream )
- {
- Parent::unpackUpdate( connection, stream );
- stream->read( &mSquareSize );
- stream->read( &mScaleU );
- stream->read( &mScaleV );
- UNPACK_ASSET(connection, Material);
- // If we're added then something possibly changed in
- // the editor... do an update of the material and the
- // geometry.
- if ( isProperlyAdded() )
- {
- _updateMaterial();
- mVertexBuffer = NULL;
- }
- }
- void GroundPlane::_updateMaterial()
- {
- if (mMaterialAsset.notNull())
- {
- if (mMaterialInst && String(mMaterialAsset->getMaterialDefinitionName()).equal(mMaterialInst->getMaterial()->getName(), String::NoCase))
- return;
- SAFE_DELETE(mMaterialInst);
- mMaterialInst = MATMGR->createMatInstance(mMaterialAsset->getMaterialDefinitionName(), getGFXVertexFormat< VertexType >());
- if (!mMaterialInst)
- Con::errorf("GroundPlane::_updateMaterial - no Material called '%s'", mMaterialAsset->getMaterialDefinitionName());
- }
- }
- bool GroundPlane::castRay( const Point3F& start, const Point3F& end, RayInfo* info )
- {
- PlaneF plane( Point3F( 0.0f, 0.0f, 0.0f ), Point3F( 0.0f, 0.0f, 1.0f ) );
- F32 t = plane.intersect( start, end );
- if( t >= 0.0 && t <= 1.0 )
- {
- info->t = t;
- info->setContactPoint( start, end );
- info->normal.set( 0, 0, 1 );
- info->material = mMaterialInst;
- info->object = this;
- info->distance = 0;
- info->faceDot = 0;
- info->texCoord.set( 0, 0 );
- return true;
- }
- return false;
- }
- void GroundPlane::buildConvex( const Box3F& box, Convex* convex )
- {
- mConvexList->collectGarbage();
- Box3F planeBox = getPlaneBox();
- if ( !box.isOverlapped( planeBox ) )
- return;
- // See if we already have a convex in the working set.
- BoxConvex *boxConvex = NULL;
- CollisionWorkingList &wl = convex->getWorkingList();
- CollisionWorkingList *itr = wl.wLink.mNext;
- for ( ; itr != &wl; itr = itr->wLink.mNext )
- {
- if ( itr->mConvex->getType() == BoxConvexType &&
- itr->mConvex->getObject() == this )
- {
- boxConvex = (BoxConvex*)itr->mConvex;
- break;
- }
- }
- if ( !boxConvex )
- {
- boxConvex = new BoxConvex;
- mConvexList->registerObject( boxConvex );
- boxConvex->init( this );
- convex->addToWorkingList( boxConvex );
- }
- // Update our convex to best match the queried box
- if ( boxConvex )
- {
- Point3F queryCenter = box.getCenter();
- boxConvex->mCenter = Point3F( queryCenter.x, queryCenter.y, -GROUND_PLANE_BOX_HEIGHT_HALF );
- boxConvex->mSize = Point3F( box.getExtents().x,
- box.getExtents().y,
- GROUND_PLANE_BOX_HEIGHT_HALF );
- }
- }
- bool GroundPlane::buildPolyList( PolyListContext context, AbstractPolyList* polyList, const Box3F& box, const SphereF& )
- {
- polyList->setObject( this );
- polyList->setTransform( &MatrixF::Identity, Point3F( 1.0f, 1.0f, 1.0f ) );
- if(context == PLC_Navigation)
- {
- F32 z = getPosition().z;
- Point3F
- p0(box.minExtents.x, box.maxExtents.y, z),
- p1(box.maxExtents.x, box.maxExtents.y, z),
- p2(box.maxExtents.x, box.minExtents.y, z),
- p3(box.minExtents.x, box.minExtents.y, z);
- // Add vertices to poly list.
- U32 v0 = polyList->addPoint(p0);
- polyList->addPoint(p1);
- polyList->addPoint(p2);
- polyList->addPoint(p3);
- // Add plane between first three vertices.
- polyList->begin(0, 0);
- polyList->vertex(v0);
- polyList->vertex(v0+1);
- polyList->vertex(v0+2);
- polyList->plane(v0, v0+1, v0+2);
- polyList->end();
- // Add plane between last three vertices.
- polyList->begin(0, 1);
- polyList->vertex(v0+2);
- polyList->vertex(v0+3);
- polyList->vertex(v0);
- polyList->plane(v0+2, v0+3, v0);
- polyList->end();
- return true;
- }
- Box3F planeBox = getPlaneBox();
- polyList->addBox( planeBox, mMaterialInst );
- return true;
- }
- void GroundPlane::prepRenderImage( SceneRenderState* state )
- {
- PROFILE_SCOPE( GroundPlane_prepRenderImage );
-
- // TODO: Should we skip rendering the ground plane into
- // the shadows? Its not like you can ever get under it.
- if ( !mMaterial )
- return;
- // If we don't have a material instance after the override then
- // we can skip rendering all together.
- BaseMatInstance *matInst = state->getOverrideMaterial(mMaterialInst);
- if ( !matInst )
- return;
- PROFILE_SCOPE( GroundPlane_prepRender );
- // Update the geometry.
- createGeometry( state->getCullingFrustum() );
- if( mVertexBuffer.isNull() )
- return;
- #ifdef TORQUE_AFX_ENABLED
- afxZodiacMgr::renderGroundPlaneZodiacs(state, this);
- #endif
- // Add a render instance.
- RenderPassManager* pass = state->getRenderPass();
- MeshRenderInst* ri = pass->allocInst< MeshRenderInst >();
- ri->type = RenderPassManager::RIT_Mesh;
- ri->vertBuff = &mVertexBuffer;
- ri->primBuff = &mPrimitiveBuffer;
- ri->prim = &mPrimitive;
- ri->matInst = matInst;
- ri->objectToWorld = pass->allocUniqueXform( MatrixF::Identity );
- ri->worldToCamera = pass->allocSharedXform( RenderPassManager::View );
- ri->projection = pass->allocSharedXform( RenderPassManager::Projection );
- ri->visibility = 1.0f;
- ri->translucentSort = matInst->getMaterial()->isTranslucent();
- ri->defaultKey = matInst->getStateHint();
- if( ri->translucentSort )
- ri->type = RenderPassManager::RIT_Translucent;
- // If we need lights then set them up.
- if ( matInst->isForwardLit() )
- {
- LightQuery query;
- query.init( getWorldSphere() );
- query.getLights( ri->lights, 8 );
- }
- pass->addInst( ri );
- }
- /// Generate a subset of the ground plane matching the given frustum.
- void GroundPlane::createGeometry( const Frustum& frustum )
- {
- PROFILE_SCOPE( GroundPlane_createGeometry );
-
- enum { MAX_WIDTH = 256, MAX_HEIGHT = 256 };
-
- // Project the frustum onto the XY grid.
- Point2F min;
- Point2F max;
- projectFrustum( frustum, mSquareSize, min, max );
-
- // Early out if the grid projection hasn't changed.
- if( mVertexBuffer.isValid() &&
- min == mMin &&
- max == mMax )
- return;
- mMin = min;
- mMax = max;
- // Determine the grid extents and allocate the buffers.
- // Adjust square size permanently if with the given frustum,
- // we end up producing more than a certain limit of geometry.
- // This is to prevent this code from causing trouble with
- // long viewing distances.
- // This only affects the client object, of course, and thus
- // has no permanent effect.
-
- U32 width = mCeil( ( max.x - min.x ) / mSquareSize );
- if( width > MAX_WIDTH )
- {
- mSquareSize = mCeil( ( max.x - min.x ) / MAX_WIDTH );
- width = MAX_WIDTH;
- }
- else if( !width )
- width = 1;
-
- U32 height = mCeil( ( max.y - min.y ) / mSquareSize );
- if( height > MAX_HEIGHT )
- {
- mSquareSize = mCeil( ( max.y - min.y ) / MAX_HEIGHT );
- height = MAX_HEIGHT;
- }
- else if( !height )
- height = 1;
- const U32 numVertices = ( width + 1 ) * ( height + 1 );
- const U32 numTriangles = width * height * 2;
- // Only reallocate if the buffers are too small.
- if ( mVertexBuffer.isNull() || numVertices > mVertexBuffer->mNumVerts )
- {
- mVertexBuffer.set( GFX, numVertices, GFXBufferTypeDynamic );
- }
- if ( mPrimitiveBuffer.isNull() || numTriangles > mPrimitiveBuffer->mPrimitiveCount )
- {
- mPrimitiveBuffer.set( GFX, numTriangles*3, numTriangles, GFXBufferTypeDynamic );
- }
- // Generate the grid.
- generateGrid( width, height, mSquareSize, min, max, mVertexBuffer, mPrimitiveBuffer );
- // Set up GFX primitive.
- mPrimitive.type = GFXTriangleList;
- mPrimitive.numPrimitives = numTriangles;
- mPrimitive.numVertices = numVertices;
- }
- /// Project the given frustum onto the ground plane and return the XY bounds in world space.
- void GroundPlane::projectFrustum( const Frustum& frustum, F32 squareSize, Point2F& outMin, Point2F& outMax )
- {
- // Get the frustum's min and max XY coordinates.
- const Box3F bounds = frustum.getBounds();
- Point2F minPt( bounds.minExtents.x, bounds.minExtents.y );
- Point2F maxPt( bounds.maxExtents.x, bounds.maxExtents.y );
- // Round the min and max coordinates so they align on the grid.
- minPt.x -= mFmod( minPt.x, squareSize );
- minPt.y -= mFmod( minPt.y, squareSize );
- F32 maxDeltaX = mFmod( maxPt.x, squareSize );
- F32 maxDeltaY = mFmod( maxPt.y, squareSize );
- if( maxDeltaX != 0.0f )
- maxPt.x += ( squareSize - maxDeltaX );
- if( maxDeltaY != 0.0f )
- maxPt.y += ( squareSize - maxDeltaY );
- // Add a safezone, so we don't touch the clipping planes.
- minPt.x -= squareSize; minPt.y -= squareSize;
- maxPt.x += squareSize; maxPt.y += squareSize;
- outMin = minPt;
- outMax = maxPt;
- }
- /// Generate a triangulated grid spanning the given bounds into the given buffers.
- void GroundPlane::generateGrid( U32 width, U32 height, F32 squareSize,
- const Point2F& min, const Point2F& max,
- GFXVertexBufferHandle< VertexType >& outVertices,
- GFXPrimitiveBufferHandle& outPrimitives )
- {
- // Generate the vertices.
- VertexType* vertices = outVertices.lock();
- for( F32 y = min.y; y <= max.y; y += squareSize )
- for( F32 x = min.x; x <= max.x; x += squareSize )
- {
- vertices->point.x = x;
- vertices->point.y = y;
- vertices->point.z = 0.0;
- vertices->texCoord.x = ( x / squareSize ) * mScaleU;
- vertices->texCoord.y = ( y / squareSize ) * -mScaleV;
- vertices->normal.x = 0.0f;
- vertices->normal.y = 0.0f;
- vertices->normal.z = 1.0f;
- vertices->tangent.x = 1.0f;
- vertices->tangent.y = 0.0f;
- vertices->tangent.z = 0.0f;
- vertices->binormal.x = 0.0f;
- vertices->binormal.y = 1.0f;
- vertices->binormal.z = 0.0f;
- vertices++;
- }
- outVertices.unlock();
- // Generate the indices.
- U16* indices;
- outPrimitives.lock( &indices );
-
- U16 corner1 = 0;
- U16 corner2 = 1;
- U16 corner3 = width + 1;
- U16 corner4 = width + 2;
-
- for( U32 y = 0; y < height; ++ y )
- {
- for( U32 x = 0; x < width; ++ x )
- {
- indices[ 0 ] = corner3;
- indices[ 1 ] = corner2;
- indices[ 2 ] = corner1;
- indices += 3;
- indices[ 0 ] = corner3;
- indices[ 1 ] = corner4;
- indices[ 2 ] = corner2;
- indices += 3;
- corner1 ++;
- corner2 ++;
- corner3 ++;
- corner4 ++;
- }
- corner1 ++;
- corner2 ++;
- corner3 ++;
- corner4 ++;
- }
- outPrimitives.unlock();
- }
- void GroundPlane::getUtilizedAssets(Vector<StringTableEntry>* usedAssetsList)
- {
- if (!mMaterialAsset.isNull() && mMaterialAsset->getAssetId() != MaterialAsset::smNoMaterialAssetFallback)
- usedAssetsList->push_back_unique(mMaterialAsset->getAssetId());
- }
- DefineEngineMethod( GroundPlane, postApply, void, (),,
- "Intended as a helper to developers and editor scripts.\n"
- "Force trigger an inspectPostApply. This will transmit "
- "material and other fields to client objects."
- )
- {
- object->inspectPostApply();
- }
|