mirror of
https://github.com/TorqueGameEngines/Torque3D.git
synced 2026-01-19 20:24:49 +00:00
Adds handling for finding material asset akin to shape and image assets Updates the material asset inspector field to follow the current standard Updates prototyping material assets to correctly have materialDefinitionName assigned Fixed material asset import step to properly assign materialDefinitionName Fixed typo in material editor icon name Added logic to editAsset function call so it can also parse assetIds Changed DefaultEditorLevel to utilize FloorGray material Adds proper NoMaterial asset for fallback purposes
673 lines
20 KiB
C++
673 lines
20 KiB
C++
//-----------------------------------------------------------------------------
|
|
// 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 ),
|
|
mPhysicsRep( NULL ),
|
|
mMin( 0.0f, 0.0f ),
|
|
mMax( 0.0f, 0.0f )
|
|
{
|
|
mTypeMask |= StaticObjectType | StaticShapeObjectType;
|
|
mNetFlags.set( Ghostable | ScopeAlways );
|
|
|
|
mConvexList = new Convex;
|
|
mTypeMask |= TerrainLikeObjectType;
|
|
|
|
mMaterialAsset = StringTable->EmptyString();
|
|
mMaterialAssetId = StringTable->EmptyString();
|
|
}
|
|
|
|
GroundPlane::~GroundPlane()
|
|
{
|
|
if( mMaterial )
|
|
SAFE_DELETE( mMaterial );
|
|
|
|
mConvexList->nukeList();
|
|
SAFE_DELETE( mConvexList );
|
|
}
|
|
|
|
void GroundPlane::initPersistFields()
|
|
{
|
|
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." );
|
|
|
|
addProtectedField("materialAsset", TypeMaterialAssetId, Offset(mMaterialAssetId, GroundPlane),
|
|
&GroundPlane::_setMaterialAsset, &defaultProtectedGetFn,
|
|
"The material asset.");
|
|
|
|
addProtectedField("material", TypeMaterialName, Offset(mMaterialName, GroundPlane),
|
|
&GroundPlane::_setMaterialName, &defaultProtectedGetFn,
|
|
"The material name.");
|
|
|
|
endGroup( "Plane" );
|
|
|
|
Parent::initPersistFields();
|
|
|
|
removeField( "scale" );
|
|
removeField( "position" );
|
|
removeField( "rotation" );
|
|
}
|
|
|
|
bool GroundPlane::_setMaterialAsset(void* obj, const char* index, const char* data)
|
|
{
|
|
GroundPlane* gp = static_cast<GroundPlane*>(obj);// ->setFile(FileName(data));
|
|
|
|
gp->mMaterialAssetId = StringTable->insert(data);
|
|
|
|
return gp->setMaterialAsset(gp->mMaterialAssetId);
|
|
}
|
|
|
|
bool GroundPlane::_setMaterialName(void* obj, const char* index, const char* data)
|
|
{
|
|
GroundPlane* gp = static_cast<GroundPlane*>(obj);// ->setFile(FileName(data));
|
|
|
|
StringTableEntry assetId = MaterialAsset::getAssetIdByMaterialName(StringTable->insert(data));
|
|
if (assetId != StringTable->EmptyString())
|
|
{
|
|
//Special exception case. If we've defaulted to the 'no shape' mesh, don't save it out, we'll retain the original ids/paths so it doesn't break
|
|
//the TSStatic
|
|
if (gp->setMaterialAsset(assetId))
|
|
{
|
|
if (assetId == StringTable->insert("Core_Rendering:noMaterial"))
|
|
{
|
|
gp->mMaterialName = data;
|
|
gp->mMaterialAssetId = StringTable->EmptyString();
|
|
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
gp->mMaterialAssetId = assetId;
|
|
gp->mMaterialName = StringTable->EmptyString();
|
|
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
gp->mMaterialAsset = StringTable->EmptyString();
|
|
gp->mMaterialName = data;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool GroundPlane::setMaterialAsset(const StringTableEntry materialAssetId)
|
|
{
|
|
if (MaterialAsset::getAssetById(materialAssetId, &mMaterialAsset))
|
|
{
|
|
//Special exception case. If we've defaulted to the 'no shape' mesh, don't save it out, we'll retain the original ids/paths so it doesn't break
|
|
//the TSStatic
|
|
if (mMaterialAsset.getAssetId() != StringTable->insert("Core_Rendering:noMaterial"))
|
|
{
|
|
mMaterialName = StringTable->EmptyString();
|
|
}
|
|
|
|
_updateMaterial();
|
|
|
|
setMaskBits(-1);
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
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()
|
|
{
|
|
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 );
|
|
stream->writeString( mMaterialAsset.getAssetId() );
|
|
stream->write( mMaterialName );
|
|
|
|
return retMask;
|
|
}
|
|
|
|
void GroundPlane::unpackUpdate( NetConnection* connection, BitStream* stream )
|
|
{
|
|
Parent::unpackUpdate( connection, stream );
|
|
|
|
stream->read( &mSquareSize );
|
|
stream->read( &mScaleU );
|
|
stream->read( &mScaleV );
|
|
|
|
char buffer[256];
|
|
stream->readString(buffer);
|
|
setMaterialAsset(StringTable->insert(buffer));
|
|
|
|
stream->read( &mMaterialName );
|
|
|
|
// 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.isNull())
|
|
{
|
|
String matName = mMaterialAsset->getMaterialDefinitionName();
|
|
|
|
mMaterial = MATMGR->createMatInstance(matName, getGFXVertexFormat< VertexType >());
|
|
if (!mMaterial)
|
|
Con::errorf("GroundPlane::_updateMaterial - no material called '%s'", matName.c_str());
|
|
}
|
|
}
|
|
|
|
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 = mMaterial;
|
|
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, mMaterial );
|
|
|
|
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( mMaterial );
|
|
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() != StringTable->insert("Core_Rendering:noMaterial"))
|
|
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();
|
|
}
|