Torque3D/Engine/source/T3D/systems/render/meshRenderSystem.cpp
2019-02-06 15:03:06 -05:00

377 lines
14 KiB
C++

#include "T3D/systems/render/meshRenderSystem.h"
#include "gfx/gfxTransformSaver.h"
#include "lighting/lightQuery.h"
#include "renderInstance/renderPassManager.h"
#include "materials/materialManager.h"
#include "materials/baseMatInstance.h"
Vector<MeshRenderSystem::BufferMaterials> MeshRenderSystem::mBufferMaterials(0);
Vector<MeshRenderSystem::BufferSet> MeshRenderSystem::mStaticBuffers(0);
void MeshRenderSystem::render(SceneManager *sceneManager, SceneRenderState* state)
{
if (sceneManager == nullptr || state == nullptr)
return;
Frustum viewFrustum = state->getCullingFrustum();
MatrixF camTransform = state->getCameraTransform();
U32 count = MeshRenderSystemInterface::all.size();
for (U32 i = 0; i < count; i++)
{
//Server side items exist for data, but we don't actually render them
if (!MeshRenderSystemInterface::all[i]->mIsClient)
continue;
if (MeshRenderSystemInterface::all[i]->mStatic)
continue;
//First, do frustum culling
if (viewFrustum.isCulled(MeshRenderSystemInterface::all[i]->mBounds))
continue;
// Set the query box for the container query. Never
// make it larger than the frustum's AABB. In the editor,
// always query the full frustum as that gives objects
// the opportunity to render editor visualizations even if
// they are otherwise not in view.
if (!state->getCullingFrustum().getBounds().isOverlapped(state->getRenderArea()))
{
// This handles fringe cases like flying backwards into a zone where you
// end up pretty much standing on a zone border and looking directly into
// its "walls". In that case the traversal area will be behind the frustum
// (remember that the camera isn't where visibility starts, it's the near
// distance).
continue;
}
//We can then sort our objects by range since we have it already, so we can do occlusion culling be rendering front-to-back
//if we've made it this far, call down to the render function to actually display our stuff
renderInterface(i, state);
}
//Static Batch rendering
if ( /*!mMaterialInst ||*/ !state)
return;
BaseMatInstance *matInst = MATMGR->getWarningMatInstance();
// Get a handy pointer to our RenderPassmanager
RenderPassManager *renderPass = state->getRenderPass();
for (U32 i = 0; i < mStaticBuffers.size(); i++)
{
for (U32 b = 0; b < mStaticBuffers[i].buffers.size(); b++)
{
if (mStaticBuffers[i].buffers[b].vertData.empty())
continue;
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 = Box3F(1000);// getRenderWorldBox();
ri->sortDistSq = rBox.getSqDistanceToPoint(state->getCameraPosition());
}
else
ri->sortDistSq = 0.0f;
// Set up our transforms
MatrixF objectToWorld = MatrixF::Identity;//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 = &mStaticBuffers[i].buffers[b].vertexBuffer;
ri->primBuff = &mStaticBuffers[i].buffers[b].primitiveBuffer;
ri->prim = renderPass->allocPrim();
ri->prim->type = GFXTriangleList;
ri->prim->minIndex = 0;
ri->prim->startIndex = 0;
ri->prim->numPrimitives = mStaticBuffers[i].buffers[b].primData.size();
ri->prim->startVertex = 0;
ri->prim->numVertices = mStaticBuffers[i].buffers[b].vertData.size();
// We sort by the material then vertex buffer
ri->defaultKey = matInst->getStateHint();
ri->defaultKey2 = (uintptr_t)ri->vertBuff;
// Submit our RenderInst to the RenderPassManager
state->getRenderPass()->addInst(ri);
}
}
}
void MeshRenderSystem::renderInterface(U32 interfaceIndex, SceneRenderState* state)
{
//Fetch
MeshRenderSystemInterface* interface = MeshRenderSystemInterface::all[interfaceIndex];
if (interface->mShapeInstance == nullptr)
return;
Point3F cameraOffset;
interface->mTransform.getColumn(3, &cameraOffset);
cameraOffset -= state->getDiffuseCameraPosition();
F32 dist = cameraOffset.len();
if (dist < 0.01f)
dist = 0.01f;
Point3F objScale = interface->mScale;
F32 invScale = (1.0f / getMax(getMax(objScale.x, objScale.y), objScale.z));
interface->mShapeInstance->setDetailFromDistance(state, dist * invScale);
if (interface->mShapeInstance->getCurrentDetail() < 0)
return;
GFXTransformSaver saver;
// Set up our TS render state.
TSRenderState rdata;
rdata.setSceneState(state);
rdata.setFadeOverride(1.0f);
rdata.setOriginSort(false);
// We might have some forward lit materials
// so pass down a query to gather lights.
LightQuery query;
query.init(interface->mSphere);
rdata.setLightQuery(&query);
MatrixF mat = interface->mTransform;
mat.scale(objScale);
GFX->setWorldMatrix(mat);
interface->mShapeInstance->render(rdata);
}
void MeshRenderSystem::rebuildBuffers()
{
U32 BUFFER_SIZE = 65000;
Vector<U32> tempIndices;
tempIndices.reserve(4);
Box3F newBounds = Box3F::Zero;
mStaticBuffers.clear();
for (U32 i = 0; i < MeshRenderSystemInterface::all.size(); i++)
{
if (!MeshRenderSystemInterface::all[i]->mIsEnabled)
continue;
if (!MeshRenderSystemInterface::all[i]->mIsClient || !MeshRenderSystemInterface::all[i]->mStatic)
continue;
//TODO: Properly re-implement StaticElements to container owner interfaces and buffer sets
for (U32 j = 0; j < MeshRenderSystemInterface::all[i]->mGeometry.mPolyList.size(); j++)
{
const OptimizedPolyList::Poly& poly = MeshRenderSystemInterface::all[i]->mGeometry.mPolyList[j];
if (poly.vertexCount < 3)
continue;
tempIndices.setSize(poly.vertexCount);
dMemset(tempIndices.address(), 0, poly.vertexCount);
if (poly.type == OptimizedPolyList::TriangleStrip ||
poly.type == OptimizedPolyList::TriangleFan)
{
tempIndices[0] = 0;
U32 idx = 1;
for (U32 k = 1; k < poly.vertexCount; k += 2)
tempIndices[idx++] = k;
for (U32 k = ((poly.vertexCount - 1) & (~0x1)); k > 0; k -= 2)
tempIndices[idx++] = k;
}
else if (poly.type == OptimizedPolyList::TriangleList)
{
for (U32 k = 0; k < poly.vertexCount; k++)
tempIndices[k] = k;
}
else
continue;
//got our data, now insert it into the correct buffer!
S32 bufferId = findBufferSetByMaterial(poly.material);
if (bufferId == -1)
{
//add a new buffer set if we didn't get a match!
BufferSet newSet;
newSet.surfaceMaterialId = poly.material;
mStaticBuffers.push_back(newSet);
bufferId = mStaticBuffers.size() - 1;
}
//see if this would push us over our buffer size limit, if it is, make a new buffer for this set
if (mStaticBuffers[bufferId].buffers.last().vertData.size() + 3 > BUFFER_SIZE
|| mStaticBuffers[bufferId].buffers.last().primData.size() + 1 > BUFFER_SIZE)
{
//yep, we'll overstep with this, so spool up a new buffer in this set
BufferSet::Buffers newBuffer = BufferSet::Buffers();
mStaticBuffers[bufferId].buffers.push_back(newBuffer);
}
const U32& firstIdx = MeshRenderSystemInterface::all[i]->mGeometry.mIndexList[poly.vertexStart];
const OptimizedPolyList::VertIndex& firstVertIdx = MeshRenderSystemInterface::all[i]->mGeometry.mVertexList[firstIdx];
//Vector<Point3F> geomPoints = MeshRenderSystemInterface::all[i]->mGeometry.mPoints;
//Vector<Point3F> geomNormals = MeshRenderSystemInterface::all[i]->mGeometry.mNormals;
//Vector<Point2F> geoUVs = MeshRenderSystemInterface::all[i]->mGeometry.mUV0s;
for (U32 k = 1; k < poly.vertexCount - 1; k++)
{
const U32& secondIdx = MeshRenderSystemInterface::all[i]->mGeometry.mIndexList[poly.vertexStart + tempIndices[k]];
const U32& thirdIdx = MeshRenderSystemInterface::all[i]->mGeometry.mIndexList[poly.vertexStart + tempIndices[k + 1]];
const OptimizedPolyList::VertIndex& secondVertIdx = MeshRenderSystemInterface::all[i]->mGeometry.mVertexList[secondIdx];
const OptimizedPolyList::VertIndex& thirdVertIdx = MeshRenderSystemInterface::all[i]->mGeometry.mVertexList[thirdIdx];
Point3F points[3];
points[0] = MeshRenderSystemInterface::all[i]->mGeometry.mPoints[firstVertIdx.vertIdx];
points[1] = MeshRenderSystemInterface::all[i]->mGeometry.mPoints[secondVertIdx.vertIdx];
points[2] = MeshRenderSystemInterface::all[i]->mGeometry.mPoints[thirdVertIdx.vertIdx];
Point3F normals[3];
normals[0] = MeshRenderSystemInterface::all[i]->mGeometry.mNormals[firstVertIdx.normalIdx];
normals[1] = MeshRenderSystemInterface::all[i]->mGeometry.mNormals[secondVertIdx.normalIdx];
normals[2] = MeshRenderSystemInterface::all[i]->mGeometry.mNormals[thirdVertIdx.normalIdx];
Point3F tangents[3];
tangents[0] = mCross(points[1] - points[0], normals[0]);
tangents[1] = mCross(points[2] - points[1], normals[1]);
tangents[2] = mCross(points[0] - points[2], normals[2]);
Point2F uvs[3];
uvs[0] = MeshRenderSystemInterface::all[i]->mGeometry.mUV0s[firstVertIdx.uv0Idx];
uvs[1] = MeshRenderSystemInterface::all[i]->mGeometry.mUV0s[secondVertIdx.uv0Idx];
uvs[2] = MeshRenderSystemInterface::all[i]->mGeometry.mUV0s[thirdVertIdx.uv0Idx];
mStaticBuffers[bufferId].vertCount += 3;
mStaticBuffers[bufferId].primCount += 1;
for (U32 v = 0; v < 3; ++v)
{
//Build the vert and store it to the buffers!
GFXVertexPNTT bufVert;
bufVert.point = points[v];
bufVert.normal = normals[v];
bufVert.tangent = tangents[v];
bufVert.texCoord = uvs[v];
newBounds.extend(points[v]);
mStaticBuffers[bufferId].buffers.last().vertData.push_back(bufVert);
U32 vertPrimId = mStaticBuffers[bufferId].buffers.last().vertData.size() - 1;
mStaticBuffers[bufferId].buffers.last().primData.push_back(vertPrimId);
mStaticBuffers[bufferId].center += points[v];
}
}
}
}
//Now, iterate through the organized data and turn them into renderable buffers
for (U32 i = 0; i < mStaticBuffers.size(); i++)
{
for (U32 b = 0; b < mStaticBuffers[i].buffers.size(); b++)
{
BufferSet::Buffers& buffers = mStaticBuffers[i].buffers[b];
//if there's no data to be had in this buffer, just skip it
if (buffers.vertData.empty())
continue;
buffers.vertexBuffer.set(GFX, buffers.vertData.size(), GFXBufferTypeStatic);
GFXVertexPNTT *pVert = buffers.vertexBuffer.lock();
for (U32 v = 0; v < buffers.vertData.size(); v++)
{
pVert->normal = buffers.vertData[v].normal;
pVert->tangent = buffers.vertData[v].tangent;
//pVert->color = buffers.vertData[v].color;
pVert->point = buffers.vertData[v].point;
pVert->texCoord = buffers.vertData[v].texCoord;
pVert++;
}
buffers.vertexBuffer.unlock();
// Allocate PB
buffers.primitiveBuffer.set(GFX, buffers.primData.size(), buffers.primData.size() / 3, GFXBufferTypeStatic);
U16 *pIndex;
buffers.primitiveBuffer.lock(&pIndex);
for (U16 primDataIDx = 0; primDataIDx < buffers.primData.size(); primDataIDx++)
{
*pIndex = primDataIDx;
pIndex++;
}
buffers.primitiveBuffer.unlock();
}
mStaticBuffers[i].center /= mStaticBuffers[i].vertCount;
}
//mObjBox = newBounds;
//resetWorldBox();
}
U32 MeshRenderSystem::findBufferSetByMaterial(U32 matId)
{
for (U32 i = 0; i < mStaticBuffers.size(); i++)
{
if (mStaticBuffers[i].surfaceMaterialId == matId)
return i;
}
return -1;
}