mirror of
https://github.com/TorqueGameEngines/Torque3D.git
synced 2026-01-20 12:44:46 +00:00
357 lines
11 KiB
C++
357 lines
11 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.
|
|
//-----------------------------------------------------------------------------
|
|
|
|
#include "platform/platform.h"
|
|
#include "T3D/examples/renderMeshExample.h"
|
|
|
|
#include "math/mathIO.h"
|
|
#include "scene/sceneRenderState.h"
|
|
#include "console/consoleTypes.h"
|
|
#include "core/stream/bitStream.h"
|
|
#include "materials/materialManager.h"
|
|
#include "materials/baseMatInstance.h"
|
|
#include "renderInstance/renderPassManager.h"
|
|
#include "lighting/lightQuery.h"
|
|
#include "console/engineAPI.h"
|
|
|
|
IMPLEMENT_CO_NETOBJECT_V1(RenderMeshExample);
|
|
|
|
ConsoleDocClass( RenderMeshExample,
|
|
"@brief An example scene object which renders a mesh.\n\n"
|
|
"This class implements a basic SceneObject that can exist in the world at a "
|
|
"3D position and render itself. There are several valid ways to render an "
|
|
"object in Torque. This class implements the preferred rendering method which "
|
|
"is to submit a MeshRenderInst along with a Material, vertex buffer, "
|
|
"primitive buffer, and transform and allow the RenderMeshMgr handle the "
|
|
"actual setup and rendering for you.\n\n"
|
|
"See the C++ code for implementation details.\n\n"
|
|
"@ingroup Examples\n" );
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Object setup and teardown
|
|
//-----------------------------------------------------------------------------
|
|
RenderMeshExample::RenderMeshExample()
|
|
{
|
|
// Flag this object so that it will always
|
|
// be sent across the network to clients
|
|
mNetFlags.set( Ghostable | ScopeAlways );
|
|
|
|
// Set it as a "static" object that casts shadows
|
|
mTypeMask |= StaticObjectType | StaticShapeObjectType;
|
|
|
|
INIT_ASSET(Material);
|
|
mMaterialInst = NULL;
|
|
}
|
|
|
|
RenderMeshExample::~RenderMeshExample()
|
|
{
|
|
if ( mMaterialInst )
|
|
SAFE_DELETE( mMaterialInst );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Object Editing
|
|
//-----------------------------------------------------------------------------
|
|
void RenderMeshExample::initPersistFields()
|
|
{
|
|
docsURL;
|
|
addGroup( "Rendering" );
|
|
INITPERSISTFIELD_MATERIALASSET(Material, RenderMeshExample, "The material used to render the mesh.");
|
|
endGroup( "Rendering" );
|
|
|
|
// SceneObject already handles exposing the transform
|
|
Parent::initPersistFields();
|
|
}
|
|
|
|
void RenderMeshExample::inspectPostApply()
|
|
{
|
|
Parent::inspectPostApply();
|
|
|
|
// Flag the network mask to send the updates
|
|
// to the client object
|
|
setMaskBits( UpdateMask );
|
|
}
|
|
|
|
bool RenderMeshExample::onAdd()
|
|
{
|
|
if ( !Parent::onAdd() )
|
|
return false;
|
|
|
|
// Set up a 1x1x1 bounding box
|
|
mObjBox.set( Point3F( -0.5f, -0.5f, -0.5f ),
|
|
Point3F( 0.5f, 0.5f, 0.5f ) );
|
|
|
|
resetWorldBox();
|
|
|
|
// Add this object to the scene
|
|
addToScene();
|
|
|
|
// Refresh this object's material (if any)
|
|
updateMaterial();
|
|
|
|
return true;
|
|
}
|
|
|
|
void RenderMeshExample::onRemove()
|
|
{
|
|
// Remove this object from the scene
|
|
removeFromScene();
|
|
|
|
Parent::onRemove();
|
|
}
|
|
|
|
void RenderMeshExample::setTransform(const MatrixF & mat)
|
|
{
|
|
// Let SceneObject handle all of the matrix manipulation
|
|
Parent::setTransform( mat );
|
|
|
|
// Dirty our network mask so that the new transform gets
|
|
// transmitted to the client object
|
|
setMaskBits( TransformMask );
|
|
}
|
|
|
|
U32 RenderMeshExample::packUpdate( NetConnection *conn, U32 mask, BitStream *stream )
|
|
{
|
|
// Allow the Parent to get a crack at writing its info
|
|
U32 retMask = Parent::packUpdate( conn, mask, stream );
|
|
|
|
// Write our transform information
|
|
if ( stream->writeFlag( mask & TransformMask ) )
|
|
{
|
|
mathWrite(*stream, getTransform());
|
|
mathWrite(*stream, getScale());
|
|
}
|
|
|
|
// Write out any of the updated editable properties
|
|
if (stream->writeFlag(mask & UpdateMask))
|
|
{
|
|
PACK_ASSET(conn, Material);
|
|
}
|
|
|
|
return retMask;
|
|
}
|
|
|
|
void RenderMeshExample::unpackUpdate(NetConnection *conn, BitStream *stream)
|
|
{
|
|
// Let the Parent read any info it sent
|
|
Parent::unpackUpdate(conn, stream);
|
|
|
|
if ( stream->readFlag() ) // TransformMask
|
|
{
|
|
mathRead(*stream, &mObjToWorld);
|
|
mathRead(*stream, &mObjScale);
|
|
|
|
setTransform( mObjToWorld );
|
|
}
|
|
|
|
if ( stream->readFlag() ) // UpdateMask
|
|
{
|
|
UNPACK_ASSET(conn, Material);
|
|
|
|
if ( isProperlyAdded() )
|
|
updateMaterial();
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Object Rendering
|
|
//-----------------------------------------------------------------------------
|
|
void RenderMeshExample::createGeometry()
|
|
{
|
|
static const Point3F cubePoints[8] =
|
|
{
|
|
Point3F( 1, -1, -1), Point3F( 1, -1, 1), Point3F( 1, 1, -1), Point3F( 1, 1, 1),
|
|
Point3F(-1, -1, -1), Point3F(-1, 1, -1), Point3F(-1, -1, 1), Point3F(-1, 1, 1)
|
|
};
|
|
|
|
static const Point3F cubeNormals[6] =
|
|
{
|
|
Point3F( 1, 0, 0), Point3F(-1, 0, 0), Point3F( 0, 1, 0),
|
|
Point3F( 0, -1, 0), Point3F( 0, 0, 1), Point3F( 0, 0, -1)
|
|
};
|
|
|
|
static const Point2F cubeTexCoords[4] =
|
|
{
|
|
Point2F( 0, 0), Point2F( 0, -1),
|
|
Point2F( 1, 0), Point2F( 1, -1)
|
|
};
|
|
|
|
static const U32 cubeFaces[36][3] =
|
|
{
|
|
{ 3, 0, 3 }, { 0, 0, 0 }, { 1, 0, 1 },
|
|
{ 2, 0, 2 }, { 0, 0, 0 }, { 3, 0, 3 },
|
|
{ 7, 1, 1 }, { 4, 1, 2 }, { 5, 1, 0 },
|
|
{ 6, 1, 3 }, { 4, 1, 2 }, { 7, 1, 1 },
|
|
{ 3, 2, 1 }, { 5, 2, 2 }, { 2, 2, 0 },
|
|
{ 7, 2, 3 }, { 5, 2, 2 }, { 3, 2, 1 },
|
|
{ 1, 3, 3 }, { 4, 3, 0 }, { 6, 3, 1 },
|
|
{ 0, 3, 2 }, { 4, 3, 0 }, { 1, 3, 3 },
|
|
{ 3, 4, 3 }, { 6, 4, 0 }, { 7, 4, 1 },
|
|
{ 1, 4, 2 }, { 6, 4, 0 }, { 3, 4, 3 },
|
|
{ 2, 5, 1 }, { 4, 5, 2 }, { 0, 5, 0 },
|
|
{ 5, 5, 3 }, { 4, 5, 2 }, { 2, 5, 1 }
|
|
};
|
|
|
|
// Fill the vertex buffer
|
|
VertexType *pVert = NULL;
|
|
|
|
mVertexBuffer.set( GFX, 36, GFXBufferTypeStatic );
|
|
pVert = mVertexBuffer.lock();
|
|
|
|
Point3F halfSize = getObjBox().getExtents() * 0.5f;
|
|
|
|
for (U32 i = 0; i < 36; i++)
|
|
{
|
|
const U32& vdx = cubeFaces[i][0];
|
|
const U32& ndx = cubeFaces[i][1];
|
|
const U32& tdx = cubeFaces[i][2];
|
|
|
|
pVert[i].point = cubePoints[vdx] * halfSize;
|
|
pVert[i].normal = cubeNormals[ndx];
|
|
pVert[i].texCoord = cubeTexCoords[tdx];
|
|
}
|
|
|
|
mVertexBuffer.unlock();
|
|
|
|
// Fill the primitive buffer
|
|
U16 *pIdx = NULL;
|
|
|
|
mPrimitiveBuffer.set( GFX, 36, 12, GFXBufferTypeStatic );
|
|
|
|
mPrimitiveBuffer.lock(&pIdx);
|
|
|
|
for (U16 i = 0; i < 36; i++)
|
|
pIdx[i] = i;
|
|
|
|
mPrimitiveBuffer.unlock();
|
|
}
|
|
|
|
void RenderMeshExample::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("RenderMeshExample::updateMaterial - no Material called '%s'", mMaterialAsset->getMaterialDefinitionName());
|
|
}
|
|
}
|
|
|
|
void RenderMeshExample::prepRenderImage( SceneRenderState *state )
|
|
{
|
|
// Do a little prep work if needed
|
|
if ( mVertexBuffer.isNull() )
|
|
createGeometry();
|
|
|
|
// If we have no material then skip out.
|
|
if ( !mMaterialInst || !state)
|
|
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;
|
|
|
|
// Get a handy pointer to our RenderPassmanager
|
|
RenderPassManager *renderPass = state->getRenderPass();
|
|
|
|
// Allocate an MeshRenderInst so that we can submit it to the RenderPassManager
|
|
MeshRenderInst *ri = renderPass->allocInst<MeshRenderInst>();
|
|
|
|
// Set our RenderInst as a standard mesh render
|
|
ri->type = RenderPassManager::RIT_Mesh;
|
|
|
|
//If our material has transparency set on this will redirect it to proper render bin
|
|
if ( matInst->getMaterial()->isTranslucent() )
|
|
{
|
|
ri->type = RenderPassManager::RIT_Translucent;
|
|
ri->translucentSort = true;
|
|
}
|
|
|
|
// Calculate our sorting point
|
|
if ( state )
|
|
{
|
|
// Calculate our sort point manually.
|
|
const Box3F& rBox = getRenderWorldBox();
|
|
ri->sortDistSq = rBox.getSqDistanceToPoint( state->getCameraPosition() );
|
|
}
|
|
else
|
|
ri->sortDistSq = 0.0f;
|
|
|
|
// Set up our transforms
|
|
MatrixF objectToWorld = getRenderTransform();
|
|
objectToWorld.scale( getScale() );
|
|
|
|
ri->objectToWorld = renderPass->allocUniqueXform( objectToWorld );
|
|
ri->worldToCamera = renderPass->allocSharedXform(RenderPassManager::View);
|
|
ri->projection = renderPass->allocSharedXform(RenderPassManager::Projection);
|
|
|
|
// If our material needs lights then fill the RIs
|
|
// light vector with the best lights.
|
|
if ( matInst->isForwardLit() )
|
|
{
|
|
LightQuery query;
|
|
query.init( getWorldSphere() );
|
|
query.getLights( ri->lights, 8 );
|
|
}
|
|
|
|
// Make sure we have an up-to-date backbuffer in case
|
|
// our Material would like to make use of it
|
|
// NOTICE: SFXBB is removed and refraction is disabled!
|
|
//ri->backBuffTex = GFX->getSfxBackBuffer();
|
|
|
|
// Set our Material
|
|
ri->matInst = matInst;
|
|
|
|
// Set up our vertex buffer and primitive buffer
|
|
ri->vertBuff = &mVertexBuffer;
|
|
ri->primBuff = &mPrimitiveBuffer;
|
|
|
|
ri->prim = renderPass->allocPrim();
|
|
ri->prim->type = GFXTriangleList;
|
|
ri->prim->minIndex = 0;
|
|
ri->prim->startIndex = 0;
|
|
ri->prim->numPrimitives = 12;
|
|
ri->prim->startVertex = 0;
|
|
ri->prim->numVertices = 36;
|
|
|
|
// We sort by the material then vertex buffer
|
|
ri->defaultKey = matInst->getStateHint();
|
|
ri->defaultKey2 = (uintptr_t)ri->vertBuff; // Not 64bit safe!
|
|
|
|
// Submit our RenderInst to the RenderPassManager
|
|
state->getRenderPass()->addInst( ri );
|
|
}
|
|
|
|
DefineEngineMethod( RenderMeshExample, postApply, void, (),,
|
|
"A utility method for forcing a network update.\n")
|
|
{
|
|
object->inspectPostApply();
|
|
}
|