diff --git a/Engine/source/environment/VolumetricFog.cpp b/Engine/source/environment/VolumetricFog.cpp index 9087effee..c4f895fe9 100644 --- a/Engine/source/environment/VolumetricFog.cpp +++ b/Engine/source/environment/VolumetricFog.cpp @@ -406,9 +406,10 @@ bool VolumetricFog::LoadShape() mIsVBDirty = true; for (U32 k = 0; k < numNrms; k++) { - Point3F norm = mesh->mVertexData[k].normal(); - Point3F vert = mesh->mVertexData[k].vert(); - Point2F uv = mesh->mVertexData[k].tvert(); + const TSMesh::__TSMeshVertexBase &vd = mesh->mVertexData.getBase(k); + Point3F norm = vd.normal(); + Point3F vert = vd.vert(); + Point2F uv = vd.tvert(); tmpVerts[k].point = vert; tmpVerts[k].texCoord = uv; tmpVerts[k].normal = norm; diff --git a/Engine/source/gfx/D3D11/gfxD3D11Device.cpp b/Engine/source/gfx/D3D11/gfxD3D11Device.cpp index e38ff546c..3f7cd44f8 100644 --- a/Engine/source/gfx/D3D11/gfxD3D11Device.cpp +++ b/Engine/source/gfx/D3D11/gfxD3D11Device.cpp @@ -1426,6 +1426,8 @@ String GFXD3D11Device::_createTempShaderInternal(const GFXVertexFormat *vertexFo StringBuilder mainBodyData; //make shader mainBodyData.append("VertOut main(VertIn IN){VertOut OUT;"); + + bool addedPadding = false; for (U32 i = 0; i < elemCount; i++) { const GFXVertexElement &element = vertexFormat->getElement(i); @@ -1433,6 +1435,8 @@ String GFXD3D11Device::_createTempShaderInternal(const GFXVertexFormat *vertexFo String semanticOut = semantic; String type; + AssertFatal(!(addedPadding && !element.isSemantic(GFXSemantic::PADDING)), "Padding added before data"); + if (element.isSemantic(GFXSemantic::POSITION)) { semantic = "POSITION"; @@ -1458,6 +1462,21 @@ String GFXD3D11Device::_createTempShaderInternal(const GFXVertexFormat *vertexFo semantic = "BINORMAL"; semanticOut = semantic; } + else if (element.isSemantic(GFXSemantic::BLENDINDICES)) + { + semantic = String::ToString("BLENDINDICES%d", element.getSemanticIndex()); + semanticOut = semantic; + } + else if (element.isSemantic(GFXSemantic::BLENDWEIGHT)) + { + semantic = String::ToString("BLENDWEIGHT%d", element.getSemanticIndex()); + semanticOut = semantic; + } + else if (element.isSemantic(GFXSemantic::PADDING)) + { + addedPadding = true; + continue; + } else { //Anything that falls thru to here will be a texture coord. @@ -1481,6 +1500,9 @@ String GFXD3D11Device::_createTempShaderInternal(const GFXVertexFormat *vertexFo case DXGI_FORMAT_R8G8B8A8_UNORM: type = "float4"; break; + case DXGI_FORMAT_R8G8B8A8_UINT: + type = "uint4"; + break; } StringBuilder in; @@ -1570,16 +1592,17 @@ GFXVertexDecl* GFXD3D11Device::allocVertexDecl( const GFXVertexFormat *vertexFor U32 stream; D3D11_INPUT_ELEMENT_DESC *vd = new D3D11_INPUT_ELEMENT_DESC[ elemCount]; - for ( U32 i=0; i < elemCount; i++ ) + S32 elemIndex = 0; + for (S32 i = 0; i < elemCount; i++, elemIndex++) { - - const GFXVertexElement &element = vertexFormat->getElement( i ); - + + const GFXVertexElement &element = vertexFormat->getElement(elemIndex); + stream = element.getStreamIndex(); vd[i].InputSlot = stream; - - vd[i].AlignedByteOffset = D3D11_APPEND_ALIGNED_ELEMENT; + + vd[i].AlignedByteOffset = D3D11_APPEND_ALIGNED_ELEMENT; vd[i].Format = GFXD3D11DeclType[element.getType()]; // If instancing is enabled, the per instance data is only used on stream 1. if (vertexFormat->hasInstancing() && stream == 1) @@ -1596,16 +1619,32 @@ GFXVertexDecl* GFXD3D11Device::allocVertexDecl( const GFXVertexFormat *vertexFor // texture coords for now... this may change later. vd[i].SemanticIndex = 0; - if ( element.isSemantic( GFXSemantic::POSITION ) ) + if (element.isSemantic(GFXSemantic::POSITION)) vd[i].SemanticName = "POSITION"; - else if ( element.isSemantic( GFXSemantic::NORMAL ) ) + else if (element.isSemantic(GFXSemantic::NORMAL)) vd[i].SemanticName = "NORMAL"; - else if ( element.isSemantic( GFXSemantic::COLOR ) ) + else if (element.isSemantic(GFXSemantic::COLOR)) vd[i].SemanticName = "COLOR"; - else if ( element.isSemantic( GFXSemantic::TANGENT ) ) + else if (element.isSemantic(GFXSemantic::TANGENT)) vd[i].SemanticName = "TANGENT"; - else if ( element.isSemantic( GFXSemantic::BINORMAL ) ) + else if (element.isSemantic(GFXSemantic::BINORMAL)) vd[i].SemanticName = "BINORMAL"; + else if (element.isSemantic(GFXSemantic::BLENDWEIGHT)) + { + vd[i].SemanticName = "BLENDWEIGHT"; + vd[i].SemanticIndex = element.getSemanticIndex(); + } + else if (element.isSemantic(GFXSemantic::BLENDINDICES)) + { + vd[i].SemanticName = "BLENDINDICES"; + vd[i].SemanticIndex = element.getSemanticIndex(); + } + else if (element.isSemantic(GFXSemantic::PADDING)) + { + i--; + elemCount--; + continue; + } else { //Anything that falls thru to here will be a texture coord. diff --git a/Engine/source/gfx/D3D11/gfxD3D11EnumTranslate.cpp b/Engine/source/gfx/D3D11/gfxD3D11EnumTranslate.cpp index 54c43fa38..b7a05acd4 100644 --- a/Engine/source/gfx/D3D11/gfxD3D11EnumTranslate.cpp +++ b/Engine/source/gfx/D3D11/gfxD3D11EnumTranslate.cpp @@ -152,5 +152,6 @@ void GFXD3D11EnumTranslate::init() GFXD3D11DeclType[GFXDeclType_Float3] = DXGI_FORMAT_R32G32B32_FLOAT; GFXD3D11DeclType[GFXDeclType_Float4] = DXGI_FORMAT_R32G32B32A32_FLOAT; GFXD3D11DeclType[GFXDeclType_Color] = DXGI_FORMAT_B8G8R8A8_UNORM; // DXGI_FORMAT_R8G8B8A8_UNORM; + GFXD3D11DeclType[GFXDeclType_UByte4] = DXGI_FORMAT_R8G8B8A8_UINT; } diff --git a/Engine/source/gfx/D3D11/gfxD3D11Shader.cpp b/Engine/source/gfx/D3D11/gfxD3D11Shader.cpp index e9dd75399..519c4645a 100644 --- a/Engine/source/gfx/D3D11/gfxD3D11Shader.cpp +++ b/Engine/source/gfx/D3D11/gfxD3D11Shader.cpp @@ -150,9 +150,11 @@ bool GFXD3D11ConstBufferLayout::set(const ParamDesc& pd, const GFXShaderConstTyp ( (pd.constType == GFXSCT_Float2x2 || pd.constType == GFXSCT_Float3x3 || + pd.constType == GFXSCT_Float4x3 || pd.constType == GFXSCT_Float4x4) && (constType == GFXSCT_Float2x2 || constType == GFXSCT_Float3x3 || + constType == GFXSCT_Float4x3 || constType == GFXSCT_Float4x4) ), "Mismatched const type!"); @@ -161,6 +163,7 @@ bool GFXD3D11ConstBufferLayout::set(const ParamDesc& pd, const GFXShaderConstTyp { case GFXSCT_Float2x2: case GFXSCT_Float3x3: + case GFXSCT_Float4x3: case GFXSCT_Float4x4: return setMatrix(pd, constType, size, data, basePointer); break; @@ -201,6 +204,40 @@ bool GFXD3D11ConstBufferLayout::setMatrix(const ParamDesc& pd, const GFXShaderCo return false; } + else if (pd.constType == GFXSCT_Float4x3) + { + F32 buffer[4 * 4]; + const U32 csize = 48; + + // Loop through and copy + bool ret = false; + U8* currDestPointer = basePointer + pd.offset; + const U8* currSourcePointer = static_cast(data); + const U8* endData = currSourcePointer + size; + while (currSourcePointer < endData) + { +#ifdef TORQUE_DOUBLE_CHECK_43MATS + Point4F col; + ((MatrixF*)currSourcePointer)->getRow(3, &col); + AssertFatal(col.x == 0.0f && col.y == 0.0f && col.z == 0.0f && col.w == 1.0f, "3rd row used"); +#endif + + if (dMemcmp(currDestPointer, currSourcePointer, csize) != 0) + { + dMemcpy(currDestPointer, currSourcePointer, csize); + ret = true; + } + else if (pd.constType == GFXSCT_Float4x3) + { + ret = true; + } + + currDestPointer += csize; + currSourcePointer += sizeof(MatrixF); + } + + return ret; + } else { PROFILE_SCOPE(GFXD3D11ConstBufferLayout_setMatrix_not4x4); @@ -480,8 +517,15 @@ void GFXD3D11ShaderConstBuffer::set(GFXShaderConstHandle* handle, const MatrixF& AssertFatal(!h->isSampler(), "Handle is sampler constant!" ); AssertFatal(h->mShader == mShader, "Mismatched shaders!"); - MatrixF transposed; - mat.transposeTo(transposed); + MatrixF transposed; + if (matrixType == GFXSCT_Float4x3) + { + transposed = mat; + } + else + { + mat.transposeTo(transposed); + } if (h->mInstancingConstant) { @@ -510,9 +554,17 @@ void GFXD3D11ShaderConstBuffer::set(GFXShaderConstHandle* handle, const MatrixF* static Vector transposed; if (arraySize > transposed.size()) - transposed.setSize(arraySize); - for (U32 i = 0; i < arraySize; i++) - mat[i].transposeTo(transposed[i]); + transposed.setSize(arraySize); + + if (matrixType == GFXSCT_Float4x3) + { + dMemcpy(transposed.address(), mat, arraySize * sizeof(MatrixF)); + } + else + { + for (U32 i = 0; i < arraySize; i++) + mat[i].transposeTo(transposed[i]); + } // TODO: Maybe support this in the future? if (h->mInstancingConstant) @@ -1190,19 +1242,13 @@ bool GFXD3D11Shader::_convertShaderVariable(const D3D11_SHADER_TYPE_DESC &typeDe case D3D_SVC_MATRIX_ROWS: case D3D_SVC_MATRIX_COLUMNS: { - switch (typeDesc.Columns) + switch (typeDesc.Rows) { case 3: - if (typeDesc.Rows == 3) - { - desc.constType = GFXSCT_Float3x3; - } + desc.constType = typeDesc.Columns == 4 ? GFXSCT_Float3x4 : GFXSCT_Float3x3; break; case 4: - if (typeDesc.Rows == 4) - { - desc.constType = GFXSCT_Float4x4; - } + desc.constType = typeDesc.Columns == 3 ? GFXSCT_Float4x3 : GFXSCT_Float4x4; break; } } @@ -1513,6 +1559,9 @@ U32 GFXD3D11Shader::getAlignmentValue(const GFXShaderConstType constType) const case GFXSCT_Float3x3 : return mRowSizeF * 3; break; + case GFXSCT_Float4x3: + return mRowSizeF * 3; + break; case GFXSCT_Float4x4 : return mRowSizeF * 4; break; diff --git a/Engine/source/gfx/D3D9/gfxD3D9Device.cpp b/Engine/source/gfx/D3D9/gfxD3D9Device.cpp index 12acfd43f..4f23d7a7d 100644 --- a/Engine/source/gfx/D3D9/gfxD3D9Device.cpp +++ b/Engine/source/gfx/D3D9/gfxD3D9Device.cpp @@ -629,6 +629,8 @@ void GFXD3D9Device::setVertexStream( U32 stream, GFXVertexBuffer *buffer ) mVolatileVB = NULL; } + U32 offset = d3dBuffer && stream != 0 ? d3dBuffer->mVolatileStart * d3dBuffer->mVertexSize : 0; + // NOTE: We do not use the stream offset here for stream 0 // as that feature is *supposedly* not as well supported as // using the start index in drawPrimitive. @@ -638,7 +640,7 @@ void GFXD3D9Device::setVertexStream( U32 stream, GFXVertexBuffer *buffer ) D3D9Assert( mD3DDevice->SetStreamSource( stream, d3dBuffer ? d3dBuffer->vb : NULL, - d3dBuffer && stream != 0 ? d3dBuffer->mVolatileStart * d3dBuffer->mVertexSize : 0, + offset, d3dBuffer ? d3dBuffer->mVertexSize : 0 ), "GFXD3D9Device::setVertexStream - Failed to set stream source." ); } @@ -929,10 +931,12 @@ GFXVertexDecl* GFXD3D9Device::allocVertexDecl( const GFXVertexFormat *vertexForm U32 elemCount = vertexFormat->getElementCount(); U32 offsets[4] = { 0 }; U32 stream; + S32 i = 0; + S32 elemIdx = 0; D3DVERTEXELEMENT9 *vd = new D3DVERTEXELEMENT9[ elemCount + 1 ]; - for ( U32 i=0; i < elemCount; i++ ) + for ( i=0; elemIdx < elemCount; i++, elemIdx++ ) { - const GFXVertexElement &element = vertexFormat->getElement( i ); + const GFXVertexElement &element = vertexFormat->getElement( elemIdx ); stream = element.getStreamIndex(); @@ -955,6 +959,18 @@ GFXVertexDecl* GFXD3D9Device::allocVertexDecl( const GFXVertexFormat *vertexForm vd[i].Usage = D3DDECLUSAGE_TANGENT; else if ( element.isSemantic( GFXSemantic::BINORMAL ) ) vd[i].Usage = D3DDECLUSAGE_BINORMAL; + else if ( element.isSemantic( GFXSemantic::BLENDINDICES ) ) + { + vd[i].Usage = D3DDECLUSAGE_BLENDINDICES; + vd[i].UsageIndex = element.getSemanticIndex(); + } + else if ( element.isSemantic( GFXSemantic::BLENDWEIGHT ) ) + { + vd[i].Usage = D3DDECLUSAGE_BLENDWEIGHT; + vd[i].UsageIndex = element.getSemanticIndex(); + } + else if ( element.isSemantic( GFXSemantic::PADDING ) ) + i--; else { // Anything that falls thru to here will be a texture coord. @@ -966,7 +982,7 @@ GFXVertexDecl* GFXD3D9Device::allocVertexDecl( const GFXVertexFormat *vertexForm } D3DVERTEXELEMENT9 declEnd = D3DDECL_END(); - vd[elemCount] = declEnd; + vd[i] = declEnd; decl = new D3D9VertexDecl(); D3D9Assert( mD3DDevice->CreateVertexDeclaration( vd, &decl->decl ), diff --git a/Engine/source/gfx/D3D9/gfxD3D9PrimitiveBuffer.cpp b/Engine/source/gfx/D3D9/gfxD3D9PrimitiveBuffer.cpp index 9588f798d..473694b5e 100644 --- a/Engine/source/gfx/D3D9/gfxD3D9PrimitiveBuffer.cpp +++ b/Engine/source/gfx/D3D9/gfxD3D9PrimitiveBuffer.cpp @@ -112,3 +112,4 @@ void GFXD3D9PrimitiveBuffer::resurrect() usage , GFXD3D9IndexFormat[GFXIndexFormat16], pool, &ib, 0), "GFXD3D9PrimitiveBuffer::resurrect - Failed to allocate an index buffer."); } + diff --git a/Engine/source/gfx/D3D9/gfxD3D9PrimitiveBuffer.h b/Engine/source/gfx/D3D9/gfxD3D9PrimitiveBuffer.h index 6dba3741c..34a1cd902 100644 --- a/Engine/source/gfx/D3D9/gfxD3D9PrimitiveBuffer.h +++ b/Engine/source/gfx/D3D9/gfxD3D9PrimitiveBuffer.h @@ -35,7 +35,6 @@ class GFXD3D9PrimitiveBuffer : public GFXPrimitiveBuffer public: IDirect3DIndexBuffer9 *ib; StrongRefPtr mVolatileBuffer; - U32 mVolatileStart; #ifdef TORQUE_DEBUG #define _PBGuardString "GFX_PRIMTIVE_BUFFER_GUARD_STRING" diff --git a/Engine/source/gfx/D3D9/gfxD3D9Shader.cpp b/Engine/source/gfx/D3D9/gfxD3D9Shader.cpp index 657797319..866c23274 100644 --- a/Engine/source/gfx/D3D9/gfxD3D9Shader.cpp +++ b/Engine/source/gfx/D3D9/gfxD3D9Shader.cpp @@ -35,6 +35,8 @@ #include "core/stream/fileStream.h" #include "core/util/safeDelete.h" #include "console/console.h" +#include "math/mMathFn.h" + using namespace Torque; @@ -172,6 +174,40 @@ bool GFXD3D9ShaderBufferLayout::setMatrix(const ParamDesc& pd, const GFXShaderCo return false; } + else if (pd.constType == GFXSCT_Float4x3) + { + F32 buffer[4*4]; + const U32 csize = 48; + + // Loop through and copy + bool ret = false; + U8* currDestPointer = basePointer + pd.offset; + const U8* currSourcePointer = static_cast(data); + const U8* endData = currSourcePointer + size; + while (currSourcePointer < endData) + { +#ifdef TORQUE_DOUBLE_CHECK_43MATS + Point4F col; + ((MatrixF*)currSourcePointer)->getRow(3, &col); + AssertFatal(col.x == 0.0f && col.y == 0.0f && col.z == 0.0f && col.w == 1.0f, "3rd row used"); +#endif + + if (dMemcmp(currDestPointer, currSourcePointer, csize) != 0) + { + dMemcpy(currDestPointer, currSourcePointer, csize); + ret = true; + } + else if (pd.constType == GFXSCT_Float4x3) + { + ret = true; + } + + currDestPointer += csize; + currSourcePointer += sizeof(MatrixF); + } + + return ret; + } else { PROFILE_SCOPE(GFXD3D9ShaderBufferLayout_setMatrix_not4x4); @@ -186,6 +222,9 @@ bool GFXD3D9ShaderBufferLayout::setMatrix(const ParamDesc& pd, const GFXShaderCo case GFXSCT_Float3x3 : csize = 48; break; + case GFXSCT_Float3x4 : + csize = 64; + break; default: AssertFatal(false, "Unhandled case!"); return false; @@ -204,6 +243,10 @@ bool GFXD3D9ShaderBufferLayout::setMatrix(const ParamDesc& pd, const GFXShaderCo dMemcpy(currDestPointer, currSourcePointer, csize); ret = true; } + else if (pd.constType == GFXSCT_Float4x3) + { + ret = true; + } currDestPointer += csize; currSourcePointer += sizeof(MatrixF); @@ -390,8 +433,15 @@ void GFXD3D9ShaderConstBuffer::set(GFXShaderConstHandle* handle, const MatrixF& AssertFatal(!h->isSampler(), "Handle is sampler constant!" ); AssertFatal(h->mShader == mShader, "Mismatched shaders!"); - MatrixF transposed; - mat.transposeTo(transposed); + MatrixF transposed; + if (matrixType == GFXSCT_Float4x3) + { + transposed = mat; + } + else + { + mat.transposeTo(transposed); + } if (h->mInstancingConstant) { @@ -420,9 +470,17 @@ void GFXD3D9ShaderConstBuffer::set(GFXShaderConstHandle* handle, const MatrixF* static Vector transposed; if (arraySize > transposed.size()) - transposed.setSize(arraySize); - for (U32 i = 0; i < arraySize; i++) - mat[i].transposeTo(transposed[i]); + transposed.setSize(arraySize); + + if (matrixType == GFXSCT_Float4x3) + { + dMemcpy(transposed.address(), mat, arraySize * sizeof(MatrixF)); + } + else + { + for (U32 i = 0; i < arraySize; i++) + mat[i].transposeTo(transposed[i]); + } // TODO: Maybe support this in the future? if (h->mInstancingConstant) @@ -1069,13 +1127,13 @@ void GFXD3D9Shader::_getShaderConstants( ID3DXConstantTable *table, case D3DXPC_MATRIX_ROWS : case D3DXPC_MATRIX_COLUMNS : { - switch (constantDesc.RegisterCount) + switch (constantDesc.Rows) { case 3 : - desc.constType = GFXSCT_Float3x3; + desc.constType = constantDesc.Columns == 4 ? GFXSCT_Float3x4 : GFXSCT_Float3x3; break; case 4 : - desc.constType = GFXSCT_Float4x4; + desc.constType = constantDesc.Columns == 3 ? GFXSCT_Float4x3 : GFXSCT_Float4x4; break; } } @@ -1436,9 +1494,15 @@ U32 GFXD3D9Shader::getAlignmentValue(const GFXShaderConstType constType) const case GFXSCT_Float3x3 : return mRowSizeF * 3; break; + case GFXSCT_Float3x4 : + return mRowSizeF * 4; + break; case GFXSCT_Float4x4 : return mRowSizeF * 4; break; + case GFXSCT_Float4x3 : + return mRowSizeF * 3; + break; //// Scalar case GFXSCT_Int : case GFXSCT_Int2 : diff --git a/Engine/source/gfx/D3D9/gfxD3D9VertexBuffer.cpp b/Engine/source/gfx/D3D9/gfxD3D9VertexBuffer.cpp index 0da7a3895..9bcea80c7 100644 --- a/Engine/source/gfx/D3D9/gfxD3D9VertexBuffer.cpp +++ b/Engine/source/gfx/D3D9/gfxD3D9VertexBuffer.cpp @@ -228,4 +228,3 @@ void GFXD3D9VertexBuffer::resurrect() "GFXD3D9VertexBuffer::resurrect - Failed to allocate VB" ); } } - diff --git a/Engine/source/gfx/D3D9/pc/gfxD3D9EnumTranslate.pc.cpp b/Engine/source/gfx/D3D9/pc/gfxD3D9EnumTranslate.pc.cpp index 93bc86ee9..a598c4999 100644 --- a/Engine/source/gfx/D3D9/pc/gfxD3D9EnumTranslate.pc.cpp +++ b/Engine/source/gfx/D3D9/pc/gfxD3D9EnumTranslate.pc.cpp @@ -372,6 +372,7 @@ void GFXD3D9EnumTranslate::init() GFXD3D9DeclType[GFXDeclType_Float3] = D3DDECLTYPE_FLOAT3; GFXD3D9DeclType[GFXDeclType_Float4] = D3DDECLTYPE_FLOAT4; GFXD3D9DeclType[GFXDeclType_Color] = D3DDECLTYPE_D3DCOLOR; + GFXD3D9DeclType[GFXDeclType_UByte4] = D3DDECLTYPE_UBYTE4; VALIDATE_LOOKUPTABLE( GFXD3D9DeclType, GFXDeclType ); } diff --git a/Engine/source/gfx/D3D9/pc/gfxD3D9PrimitiveBuffer.pc.cpp b/Engine/source/gfx/D3D9/pc/gfxD3D9PrimitiveBuffer.pc.cpp index 32251438d..c550d046a 100644 --- a/Engine/source/gfx/D3D9/pc/gfxD3D9PrimitiveBuffer.pc.cpp +++ b/Engine/source/gfx/D3D9/pc/gfxD3D9PrimitiveBuffer.pc.cpp @@ -27,6 +27,7 @@ void GFXD3D9PrimitiveBuffer::lock(U32 indexStart, U32 indexEnd, void **indexPtr) { AssertFatal(!mLocked, "GFXD3D9PrimitiveBuffer::lock - Can't lock a primitive buffer more than once!"); + mLocked = true; U32 flags=0; switch(mBufferType) diff --git a/Engine/source/gfx/genericConstBuffer.cpp b/Engine/source/gfx/genericConstBuffer.cpp index 011881ece..4a221eb7e 100644 --- a/Engine/source/gfx/genericConstBuffer.cpp +++ b/Engine/source/gfx/genericConstBuffer.cpp @@ -80,9 +80,13 @@ bool GenericConstBufferLayout::set(const ParamDesc& pd, const GFXShaderConstType ( ( pd.constType == GFXSCT_Float2x2 || pd.constType == GFXSCT_Float3x3 || + pd.constType == GFXSCT_Float3x4 || + pd.constType == GFXSCT_Float4x3 || pd.constType == GFXSCT_Float4x4 ) && ( constType == GFXSCT_Float2x2 || - constType == GFXSCT_Float3x3 || + constType == GFXSCT_Float3x3 || + constType == GFXSCT_Float3x4 || + constType == GFXSCT_Float4x3 || constType == GFXSCT_Float4x4 ) ), "Mismatched const type!" ); @@ -91,6 +95,7 @@ bool GenericConstBufferLayout::set(const ParamDesc& pd, const GFXShaderConstType { case GFXSCT_Float2x2 : case GFXSCT_Float3x3 : + case GFXSCT_Float4x3 : case GFXSCT_Float4x4 : return setMatrix(pd, constType, size, data, basePointer); break; diff --git a/Engine/source/gfx/genericConstBuffer.h b/Engine/source/gfx/genericConstBuffer.h index c38947541..1c3824bf8 100644 --- a/Engine/source/gfx/genericConstBuffer.h +++ b/Engine/source/gfx/genericConstBuffer.h @@ -190,6 +190,8 @@ public: { AssertFatal( matrixType == GFXSCT_Float2x2 || matrixType == GFXSCT_Float3x3 || + matrixType == GFXSCT_Float3x4 || + matrixType == GFXSCT_Float4x3 || matrixType == GFXSCT_Float4x4, "GenericConstBuffer::set() - Invalid matrix type!" ); @@ -200,6 +202,8 @@ public: { AssertFatal( matrixType == GFXSCT_Float2x2 || matrixType == GFXSCT_Float3x3 || + matrixType == GFXSCT_Float3x4 || + matrixType == GFXSCT_Float4x3 || matrixType == GFXSCT_Float4x4, "GenericConstBuffer::set() - Invalid matrix type!" ); diff --git a/Engine/source/gfx/gfxEnums.h b/Engine/source/gfx/gfxEnums.h index 238af2f94..cdb61b6a4 100644 --- a/Engine/source/gfx/gfxEnums.h +++ b/Engine/source/gfx/gfxEnums.h @@ -48,6 +48,7 @@ enum GFXBufferType ///< allowed. GFXBufferTypeVolatile, ///< Volatile vertex or index buffers are meant for vertices or indices that are essentially ///< only used once. They can be resized without any performance penalty. + GFXBufferTypeImmutable, ///< Immutable buffers must specify the data when creating the buffer. Cannot be modified. GFXBufferType_COUNT ///< Number of buffer types. @@ -581,7 +582,9 @@ enum GFXShaderConstType GFXSCT_Float4, // Matrices GFXSCT_Float2x2, - GFXSCT_Float3x3, + GFXSCT_Float3x3, + GFXSCT_Float3x4, + GFXSCT_Float4x3, GFXSCT_Float4x4, // Scalar GFXSCT_Int, @@ -621,6 +624,9 @@ enum GFXDeclType /// @see GFXVertexColor GFXDeclType_Color, + /// Four-component, packed, unsigned bytes ranged 0-255 + GFXDeclType_UByte4, + /// The count of total GFXDeclTypes. GFXDeclType_COUNT, }; diff --git a/Engine/source/gfx/gfxPrimitiveBuffer.h b/Engine/source/gfx/gfxPrimitiveBuffer.h index 9fa6ed14f..b7f202b5a 100644 --- a/Engine/source/gfx/gfxPrimitiveBuffer.h +++ b/Engine/source/gfx/gfxPrimitiveBuffer.h @@ -43,7 +43,9 @@ public: //protected: U32 mPrimitiveCount; GFXBufferType mBufferType; GFXPrimitive *mPrimitiveArray; - GFXDevice *mDevice; + GFXDevice *mDevice; + + U32 mVolatileStart; #ifdef TORQUE_DEBUG // In debug builds we provide a TOC leak tracking system. @@ -59,7 +61,8 @@ public: //protected: GFXPrimitiveBuffer( GFXDevice *device, U32 indexCount, U32 primitiveCount, - GFXBufferType bufferType ) + GFXBufferType bufferType ) : + mVolatileStart(0) { mDevice = device; mIndexCount = indexCount; @@ -122,7 +125,7 @@ public: //protected: // GFXResource interface /// The resource should put a description of itself (number of vertices, size/width of texture, etc.) in buffer - virtual const String describeSelf() const; + virtual const String describeSelf() const; }; class GFXPrimitiveBufferHandle : public StrongRefPtr diff --git a/Engine/source/gfx/gfxVertexBuffer.h b/Engine/source/gfx/gfxVertexBuffer.h index 329d0cf72..51f963852 100644 --- a/Engine/source/gfx/gfxVertexBuffer.h +++ b/Engine/source/gfx/gfxVertexBuffer.h @@ -64,11 +64,11 @@ public: const GFXVertexFormat *vertexFormat, U32 vertexSize, GFXBufferType bufferType ) - : mNumVerts( numVerts ), + : mDevice( device ), + mVolatileStart( 0 ), + mNumVerts( numVerts ), mVertexSize( vertexSize ), - mBufferType( bufferType ), - mDevice( device ), - mVolatileStart( 0 ) + mBufferType( bufferType ) { if ( vertexFormat ) { diff --git a/Engine/source/gfx/gfxVertexFormat.cpp b/Engine/source/gfx/gfxVertexFormat.cpp index 15f16a4d4..c1f7de9a7 100644 --- a/Engine/source/gfx/gfxVertexFormat.cpp +++ b/Engine/source/gfx/gfxVertexFormat.cpp @@ -37,6 +37,9 @@ namespace GFXSemantic const String TANGENTW = String( "TANGENTW" ).intern(); const String COLOR = String( "COLOR" ).intern(); const String TEXCOORD = String( "TEXCOORD" ).intern(); + const String BLENDINDICES = String( "BLENDINDICES" ).intern(); + const String BLENDWEIGHT = String( "BLENDWEIGHT" ).intern(); + const String PADDING = String( "PADDING" ).intern(); } @@ -59,6 +62,9 @@ U32 GFXVertexElement::getSizeInBytes() const case GFXDeclType_Color: return 4; + case GFXDeclType_UByte4: + return 4; + default: return 0; }; @@ -85,6 +91,7 @@ void GFXVertexFormat::copy( const GFXVertexFormat &format ) mHasTangent = format.mHasTangent; mHasColor = format.mHasColor; mHasInstancing = format.mHasInstancing; + mHasBlendIndices = format.mHasBlendIndices; mTexCoordCount = format.mTexCoordCount; mSizeInBytes = format.mSizeInBytes; mDescription = format.mDescription; @@ -171,6 +178,35 @@ bool GFXVertexFormat::hasInstancing() const return mHasInstancing; } +bool GFXVertexFormat::hasBlendIndices() const +{ + if ( mDirty ) + const_cast(this)->_updateDirty(); + + return mHasBlendIndices; +} + +U32 GFXVertexFormat::getNumBlendIndices() const +{ + if ( mDirty ) + const_cast(this)->_updateDirty(); + + if ( !mHasBlendIndices ) + return 0; + + U32 numIndices = 0; + + for ( U32 i=0; i < mElements.size(); i++ ) + { + const GFXVertexElement &element = mElements[i]; + + if ( element.isSemantic( GFXSemantic::BLENDINDICES ) ) + numIndices++; + } + + return numIndices; +} + U32 GFXVertexFormat::getTexCoordCount() const { if ( mDirty ) @@ -199,6 +235,7 @@ void GFXVertexFormat::_updateDirty() mTexCoordCount = 0; mHasColor = false; + mHasBlendIndices = false; mHasNormal = false; mHasTangent = false; mSizeInBytes = 0; @@ -222,6 +259,8 @@ void GFXVertexFormat::_updateDirty() mHasColor = true; else if ( element.isSemantic( GFXSemantic::TEXCOORD ) ) ++mTexCoordCount; + else if ( element.isSemantic( GFXSemantic::BLENDINDICES ) ) + mHasBlendIndices = true; mSizeInBytes += element.getSizeInBytes(); } diff --git a/Engine/source/gfx/gfxVertexFormat.h b/Engine/source/gfx/gfxVertexFormat.h index 09934e0df..b0457b957 100644 --- a/Engine/source/gfx/gfxVertexFormat.h +++ b/Engine/source/gfx/gfxVertexFormat.h @@ -45,6 +45,9 @@ namespace GFXSemantic extern const String TANGENTW; extern const String COLOR; extern const String TEXCOORD; + extern const String BLENDWEIGHT; + extern const String BLENDINDICES; + extern const String PADDING; } @@ -184,10 +187,16 @@ public: /// Returns true if there is a COLOR semantic in this vertex format. bool hasColor() const; + + /// Returns true if there is a BLENDWEIGHT or BLENDINDICES semantic in this vertex format. + bool hasBlendIndices() const; /// Return true if instancing is used with this vertex format. bool hasInstancing() const; + /// Returns number of blend indices + U32 getNumBlendIndices() const; + /// Returns the texture coordinate count by /// counting the number of TEXCOORD semantics. U32 getTexCoordCount() const; @@ -230,6 +239,9 @@ protected: /// Is true if there is a COLOR semantic in this vertex format. bool mHasColor; + + /// Is true if there is a BLENDWEIGHT or BLENDINDICES semantic in this vertex format. + bool mHasBlendIndices; /// Is instaning used with this vertex format. bool mHasInstancing; diff --git a/Engine/source/gfx/gfxVertexTypes.cpp b/Engine/source/gfx/gfxVertexTypes.cpp index dbee17550..bfc194104 100644 --- a/Engine/source/gfx/gfxVertexTypes.cpp +++ b/Engine/source/gfx/gfxVertexTypes.cpp @@ -29,6 +29,11 @@ GFXImplementVertexFormat( GFXVertexP ) addElement( "POSITION", GFXDeclType_Float3 ); } +GFXImplementVertexFormat( GFXVertexPad ) +{ + addElement("PADDING", GFXDeclType_UByte4); +} + GFXImplementVertexFormat( GFXVertexPT ) { addElement( "POSITION", GFXDeclType_Float3 ); diff --git a/Engine/source/gfx/gfxVertexTypes.h b/Engine/source/gfx/gfxVertexTypes.h index 8af285f5d..005aca02b 100644 --- a/Engine/source/gfx/gfxVertexTypes.h +++ b/Engine/source/gfx/gfxVertexTypes.h @@ -36,6 +36,10 @@ #include "math/mPoint3.h" #endif +GFXDeclareVertexFormat( GFXVertexPad ) +{ + U32 data; +}; GFXDeclareVertexFormat( GFXVertexP ) { diff --git a/Engine/source/gfx/gl/gfxGLDevice.cpp b/Engine/source/gfx/gl/gfxGLDevice.cpp index e3a1907bc..afa7ac4ec 100644 --- a/Engine/source/gfx/gl/gfxGLDevice.cpp +++ b/Engine/source/gfx/gl/gfxGLDevice.cpp @@ -608,13 +608,11 @@ void GFXGLDevice::drawIndexedPrimitive( GFXPrimitiveType primType, U32 startIndex, U32 primitiveCount ) { - AssertFatal( startVertex == 0, "GFXGLDevice::drawIndexedPrimitive() - Non-zero startVertex unsupported!" ); - preDrawPrimitive(); - U16* buf = (U16*)static_cast(mCurrentPrimitiveBuffer.getPointer())->getBuffer() + startIndex; + U16* buf = (U16*)static_cast(mCurrentPrimitiveBuffer.getPointer())->getBuffer() + startIndex + mCurrentPrimitiveBuffer->mVolatileStart; - const U32 baseVertex = mCurrentVB[0]->mBufferVertexOffset; + const U32 baseVertex = mCurrentVB[0]->mBufferVertexOffset + startVertex; if(mDrawInstancesCount) glDrawElementsInstancedBaseVertex(GFXGLPrimType[primType], primCountToIndexCount(primType, primitiveCount), GL_UNSIGNED_SHORT, buf, mDrawInstancesCount, baseVertex); diff --git a/Engine/source/gfx/gl/gfxGLShader.cpp b/Engine/source/gfx/gl/gfxGLShader.cpp index 890a6c2c5..6d641aa52 100644 --- a/Engine/source/gfx/gl/gfxGLShader.cpp +++ b/Engine/source/gfx/gl/gfxGLShader.cpp @@ -92,6 +92,8 @@ static U32 shaderConstTypeSize(GFXShaderConstType type) return 16; case GFXSCT_Float3x3: return 36; + case GFXSCT_Float4x3: + return 48; case GFXSCT_Float4x4: return 64; default: @@ -305,6 +307,9 @@ void GFXGLShaderConstBuffer::set(GFXShaderConstHandle* handle, const MatrixF& ma reinterpret_cast(mBuffer + _glHandle->mOffset)[7] = mat[9]; reinterpret_cast(mBuffer + _glHandle->mOffset)[8] = mat[10]; break; + case GFXSCT_Float4x3: + dMemcpy(mBuffer + _glHandle->mOffset, (const F32*)mat, (sizeof(F32) * 12));// matrix with end row chopped off + break; case GFXSCT_Float4x4: { if(_glHandle->mInstancingConstant) @@ -334,6 +339,13 @@ void GFXGLShaderConstBuffer::set(GFXShaderConstHandle* handle, const MatrixF* ma AssertFatal(!_glHandle->mInstancingConstant, "GFXGLShaderConstBuffer::set - Instancing not supported for matrix arrays"); switch (matrixType) { + case GFXSCT_Float4x3: + // Copy each item with the last row chopped off + for (int i = 0; imOffset + (i*(sizeof(F32) * 12)), (F32*)(mat + i), sizeof(F32) * 12); + } + break; case GFXSCT_Float4x4: dMemcpy(mBuffer + _glHandle->mOffset, (F32*)mat, _glHandle->getSize()); break; @@ -443,6 +455,14 @@ bool GFXGLShader::_init() glBindAttribLocation(mProgram, Torque::GL_VertexAttrib_Tangent, "vTangent"); glBindAttribLocation(mProgram, Torque::GL_VertexAttrib_TangentW, "vTangentW"); glBindAttribLocation(mProgram, Torque::GL_VertexAttrib_Binormal, "vBinormal"); + glBindAttribLocation(mProgram, Torque::GL_VertexAttrib_BlendIndex0, "vBlendIndex0"); + glBindAttribLocation(mProgram, Torque::GL_VertexAttrib_BlendIndex1, "vBlendIndex1"); + glBindAttribLocation(mProgram, Torque::GL_VertexAttrib_BlendIndex2, "vBlendIndex2"); + glBindAttribLocation(mProgram, Torque::GL_VertexAttrib_BlendIndex3, "vBlendIndex3"); + glBindAttribLocation(mProgram, Torque::GL_VertexAttrib_BlendWeight0, "vBlendWeight0"); + glBindAttribLocation(mProgram, Torque::GL_VertexAttrib_BlendWeight1, "vBlendWeight1"); + glBindAttribLocation(mProgram, Torque::GL_VertexAttrib_BlendWeight2, "vBlendWeight2"); + glBindAttribLocation(mProgram, Torque::GL_VertexAttrib_BlendWeight3, "vBlendWeight3"); glBindAttribLocation(mProgram, Torque::GL_VertexAttrib_TexCoord0, "vTexCoord0"); glBindAttribLocation(mProgram, Torque::GL_VertexAttrib_TexCoord1, "vTexCoord1"); glBindAttribLocation(mProgram, Torque::GL_VertexAttrib_TexCoord2, "vTexCoord2"); @@ -572,6 +592,9 @@ void GFXGLShader::initConstantDescs() case GL_FLOAT_MAT4: desc.constType = GFXSCT_Float4x4; break; + case GL_FLOAT_MAT4x3: // jamesu - columns, rows + desc.constType = GFXSCT_Float4x3; + break; case GL_SAMPLER_1D: case GL_SAMPLER_2D: case GL_SAMPLER_3D: @@ -805,6 +828,11 @@ void GFXGLShader::setConstantsFromBuffer(GFXGLShaderConstBuffer* buffer) case GFXSCT_Float3x3: glUniformMatrix3fv(handle->mLocation, handle->mDesc.arraySize, true, (GLfloat*)(mConstBuffer + handle->mOffset)); break; + case GFXSCT_Float4x3: + // NOTE: To save a transpose here we could store the matrix transposed (i.e. column major) in the constant buffer. + // See _mesa_uniform_matrix in the mesa source for the correct transpose algorithm for a 4x3 matrix. + glUniformMatrix4x3fv(handle->mLocation, handle->mDesc.arraySize, true, (GLfloat*)(mConstBuffer + handle->mOffset)); + break; case GFXSCT_Float4x4: glUniformMatrix4fv(handle->mLocation, handle->mDesc.arraySize, true, (GLfloat*)(mConstBuffer + handle->mOffset)); break; diff --git a/Engine/source/gfx/gl/gfxGLVertexAttribLocation.h b/Engine/source/gfx/gl/gfxGLVertexAttribLocation.h index 63582ecdf..aced0abe9 100644 --- a/Engine/source/gfx/gl/gfxGLVertexAttribLocation.h +++ b/Engine/source/gfx/gl/gfxGLVertexAttribLocation.h @@ -11,6 +11,14 @@ namespace Torque GL_VertexAttrib_Tangent, GL_VertexAttrib_TangentW, GL_VertexAttrib_Binormal, + GL_VertexAttrib_BlendIndex0, + GL_VertexAttrib_BlendIndex1, + GL_VertexAttrib_BlendIndex2, + GL_VertexAttrib_BlendIndex3, + GL_VertexAttrib_BlendWeight0, + GL_VertexAttrib_BlendWeight1, + GL_VertexAttrib_BlendWeight2, + GL_VertexAttrib_BlendWeight3, GL_VertexAttrib_TexCoord0, GL_VertexAttrib_TexCoord1, GL_VertexAttrib_TexCoord2, diff --git a/Engine/source/gfx/gl/gfxGLVertexDecl.cpp b/Engine/source/gfx/gl/gfxGLVertexDecl.cpp index 948a2b2de..2c19f756b 100644 --- a/Engine/source/gfx/gl/gfxGLVertexDecl.cpp +++ b/Engine/source/gfx/gl/gfxGLVertexDecl.cpp @@ -187,6 +187,28 @@ void GFXGLVertexDecl::_initVerticesFormat(U32 stream) buffer += element.getSizeInBytes(); } + else if ( element.isSemantic( GFXSemantic::BLENDWEIGHT ) ) + { + glElement.attrIndex = Torque::GL_VertexAttrib_BlendWeight0 + element.getSemanticIndex(); + glElement.elementCount = 4; + glElement.normalized = false; + glElement.type = GL_FLOAT; + glElement.stride = vertexSize; + glElement.pointerFirst = (void*)buffer; + + buffer += element.getSizeInBytes(); + } + else if ( element.isSemantic( GFXSemantic::BLENDINDICES ) ) + { + glElement.attrIndex = Torque::GL_VertexAttrib_BlendIndex0 + element.getSemanticIndex(); + glElement.elementCount = 4; + glElement.normalized = false; + glElement.type = GL_UNSIGNED_BYTE; + glElement.stride = vertexSize; + glElement.pointerFirst = (void*)buffer; + + buffer += element.getSizeInBytes(); + } else // Everything else is a texture coordinate. { String name = element.getSemantic(); diff --git a/Engine/source/materials/baseMatInstance.h b/Engine/source/materials/baseMatInstance.h index 4edd22664..4bc6a2ba2 100644 --- a/Engine/source/materials/baseMatInstance.h +++ b/Engine/source/materials/baseMatInstance.h @@ -80,6 +80,9 @@ protected: /// This is set by initialization and used by the prepass. bool mHasNormalMaps; + /// This material makes use of bone transforms + bool mUsesHardwareSkinning; + public: virtual ~BaseMatInstance(); @@ -149,6 +152,9 @@ public: /// @see setupPass virtual void setTransforms( const MatrixSet &matrixSet, SceneRenderState *state ) = 0; + /// Sets node transforms for the current stage. Used for hardware skinning. + virtual void setNodeTransforms( const MatrixF *address, const U32 numTransforms ) = 0; + /// This initializes various material scene state settings and /// should be called after setupPass() within the pass loop. /// @see setupPass @@ -214,6 +220,8 @@ public: /// Fast test for use of normal maps in this material. bool hasNormalMap() const { return mHasNormalMaps; } + bool usesHardwareSkinning() const { return mUsesHardwareSkinning; } + /// MatFeaturesDelegate& getFeaturesDelegate() { return mFeaturesDelegate; } diff --git a/Engine/source/materials/matInstance.cpp b/Engine/source/materials/matInstance.cpp index 273a4cb3d..bfc87054b 100644 --- a/Engine/source/materials/matInstance.cpp +++ b/Engine/source/materials/matInstance.cpp @@ -35,6 +35,7 @@ #include "gfx/sim/cubemapData.h" #include "gfx/gfxCubemap.h" #include "core/util/safeDelete.h" +#include "ts/tsShape.h" class MatInstParameters; @@ -248,8 +249,10 @@ void MatInstance::construct() mActiveParameters = NULL; mDefaultParameters = NULL; mHasNormalMaps = false; + mUsesHardwareSkinning = false; mIsForwardLit = false; mIsValid = false; + mIsHardwareSkinned = false; MATMGR->_track(this); } @@ -360,6 +363,11 @@ bool MatInstance::processMaterial() FeatureSet features( mFeatureList ); features.exclude( MATMGR->getExclusionFeatures() ); + + if (mVertexFormat->hasBlendIndices() && TSShape::smUseHardwareSkinning) + { + features.addFeature( MFT_HardwareSkinning ); + } if( !mProcessedMaterial->init(features, mVertexFormat, mFeaturesDelegate) ) { @@ -373,11 +381,14 @@ bool MatInstance::processMaterial() const FeatureSet &finalFeatures = mProcessedMaterial->getFeatures(); mHasNormalMaps = finalFeatures.hasFeature( MFT_NormalMap ); + mUsesHardwareSkinning = finalFeatures.hasFeature( MFT_HardwareSkinning ); mIsForwardLit = ( custMat && custMat->mForwardLit ) || ( !finalFeatures.hasFeature( MFT_IsEmissive ) && finalFeatures.hasFeature( MFT_ForwardShading ) ); + mIsHardwareSkinned = finalFeatures.hasFeature( MFT_HardwareSkinning ); + return true; } @@ -455,6 +466,12 @@ void MatInstance::setTransforms(const MatrixSet &matrixSet, SceneRenderState *st mProcessedMaterial->setTransforms(matrixSet, state, getCurPass()); } +void MatInstance::setNodeTransforms(const MatrixF *address, const U32 numTransforms) +{ + PROFILE_SCOPE(MatInstance_setNodeTransforms); + mProcessedMaterial->setNodeTransforms(address, numTransforms, getCurPass()); +} + void MatInstance::setSceneInfo(SceneRenderState * state, const SceneData& sgData) { PROFILE_SCOPE(MatInstance_setSceneInfo); diff --git a/Engine/source/materials/matInstance.h b/Engine/source/materials/matInstance.h index 290385242..ed498fa23 100644 --- a/Engine/source/materials/matInstance.h +++ b/Engine/source/materials/matInstance.h @@ -65,12 +65,14 @@ public: virtual MaterialParameterHandle* getMaterialParameterHandle(const String& name); virtual bool setupPass(SceneRenderState *, const SceneData &sgData ); virtual void setTransforms(const MatrixSet &matrixSet, SceneRenderState *state); + virtual void setNodeTransforms(const MatrixF *address, const U32 numTransforms); virtual void setSceneInfo(SceneRenderState *, const SceneData& sgData); virtual void setTextureStages(SceneRenderState * state, const SceneData &sgData ); virtual void setBuffers(GFXVertexBufferHandleBase* vertBuffer, GFXPrimitiveBufferHandle* primBuffer); virtual bool isInstanced() const; virtual bool stepInstance(); virtual bool isForwardLit() const { return mIsForwardLit; } + virtual bool isHardwareSkinned() const { return mIsHardwareSkinned; } virtual void setUserObject( SimObject *userObject ) { mUserObject = userObject; } virtual SimObject* getUserObject() const { return mUserObject; } virtual Material *getMaterial() { return mMaterial; } @@ -113,6 +115,9 @@ protected: /// If the processed material requires forward lighting or not. bool mIsForwardLit; + /// If the processed material requires bone transforms + bool mIsHardwareSkinned; + S32 mCurPass; U32 mMaxStages; diff --git a/Engine/source/materials/materialFeatureTypes.cpp b/Engine/source/materials/materialFeatureTypes.cpp index 513474c77..56dcbc8fe 100644 --- a/Engine/source/materials/materialFeatureTypes.cpp +++ b/Engine/source/materials/materialFeatureTypes.cpp @@ -103,3 +103,6 @@ ImplementFeatureType( MFT_DeferredEmptySpec, MFG_Texture, 8.01f, false ); ImplementFeatureType( MFT_DeferredSpecMap, MFG_Texture, 8.2f, false ); ImplementFeatureType( MFT_DeferredSpecVars, MFG_Texture, 8.5f, false ); ImplementFeatureType( MFT_DeferredMatInfoFlags, MFG_Texture, 8.7f, false ); + +ImplementFeatureType( MFT_HardwareSkinning, MFG_Transform,-2.0, false ); + diff --git a/Engine/source/materials/materialFeatureTypes.h b/Engine/source/materials/materialFeatureTypes.h index a002cd190..c820fec96 100644 --- a/Engine/source/materials/materialFeatureTypes.h +++ b/Engine/source/materials/materialFeatureTypes.h @@ -179,6 +179,8 @@ DeclareFeatureType( MFT_ForwardShading ); /// so that the rest of the material features can work on it. DeclareFeatureType( MFT_ImposterVert ); +DeclareFeatureType( MFT_HardwareSkinning ); + // Deferred Shading DeclareFeatureType( MFT_isDeferred ); diff --git a/Engine/source/materials/miscShdrDat.h b/Engine/source/materials/miscShdrDat.h index 9ebaa6f46..d8902e552 100644 --- a/Engine/source/materials/miscShdrDat.h +++ b/Engine/source/materials/miscShdrDat.h @@ -41,7 +41,9 @@ enum RegisterType RT_COLOR, RT_TEXCOORD, RT_VPOS, - RT_SVPOSITION + RT_SVPOSITION, + RT_BLENDINDICES, + RT_BLENDWEIGHT }; enum Components diff --git a/Engine/source/materials/processedFFMaterial.h b/Engine/source/materials/processedFFMaterial.h index 654653d6a..c65175381 100644 --- a/Engine/source/materials/processedFFMaterial.h +++ b/Engine/source/materials/processedFFMaterial.h @@ -53,6 +53,7 @@ public: virtual MaterialParameters* getDefaultMaterialParameters(); virtual void setTransforms(const MatrixSet &matrixSet, SceneRenderState *state, const U32 pass); + virtual void setNodeTransforms(const MatrixF *address, const U32 numTransforms, const U32 pass) {;} virtual void setSceneInfo(SceneRenderState *, const SceneData& sgData, U32 pass); diff --git a/Engine/source/materials/processedMaterial.h b/Engine/source/materials/processedMaterial.h index a77908959..3c3f97a88 100644 --- a/Engine/source/materials/processedMaterial.h +++ b/Engine/source/materials/processedMaterial.h @@ -142,6 +142,9 @@ public: /// Sets the transformation matrix, i.e. Model * View * Projection virtual void setTransforms(const MatrixSet &matrixSet, SceneRenderState *state, const U32 pass) = 0; + /// Sets the node transforms for HW Skinning + virtual void setNodeTransforms(const MatrixF *address, const U32 numTransforms, const U32 pass) = 0; + /// Sets the scene info like lights for the given pass. virtual void setSceneInfo(SceneRenderState *, const SceneData& sgData, U32 pass) = 0; diff --git a/Engine/source/materials/processedShaderMaterial.cpp b/Engine/source/materials/processedShaderMaterial.cpp index cd230ef53..aeecf3da3 100644 --- a/Engine/source/materials/processedShaderMaterial.cpp +++ b/Engine/source/materials/processedShaderMaterial.cpp @@ -44,6 +44,9 @@ // We need to include customMaterialDefinition for ShaderConstHandles::init #include "materials/customMaterialDefinition.h" + +#include "ts/tsShape.h" + /// /// ShaderConstHandles /// @@ -99,6 +102,9 @@ void ShaderConstHandles::init( GFXShader *shader, CustomMaterial* mat /*=NULL*/ for (S32 i = 0; i < TEXTURE_STAGE_COUNT; ++i) mRTParamsSC[i] = shader->getShaderConstHandle( String::ToString( "$rtParams%d", i ) ); + // MFT_HardwareSkinning + mNodeTransforms = shader->getShaderConstHandle( "$nodeTransforms" ); + // Clear any existing texture handles. dMemset( mTexHandlesSC, 0, sizeof( mTexHandlesSC ) ); if(mat) @@ -491,6 +497,12 @@ void ProcessedShaderMaterial::_determineFeatures( U32 stageNum, &fd ); } + // Need to add the Hardware Skinning feature if its used + if ( features.hasFeature( MFT_HardwareSkinning ) ) + { + fd.features.addFeature( MFT_HardwareSkinning ); + } + // Now disable any features that were // not part of the input feature handle. fd.features.filter( features ); @@ -1217,6 +1229,20 @@ void ProcessedShaderMaterial::setTransforms(const MatrixSet &matrixSet, SceneRen shaderConsts->set( handles->m_vEyeSC, state->getVectorEye() ); } +void ProcessedShaderMaterial::setNodeTransforms(const MatrixF *transforms, const U32 transformCount, const U32 pass) +{ + PROFILE_SCOPE( ProcessedShaderMaterial_setNodeTransforms ); + + GFXShaderConstBuffer* shaderConsts = _getShaderConstBuffer(pass); + ShaderConstHandles* handles = _getShaderConstHandles(pass); + + if ( handles->mNodeTransforms->isValid() ) + { + S32 realTransformCount = getMin( transformCount, TSShape::smMaxSkinBones ); + shaderConsts->set( handles->mNodeTransforms, transforms, realTransformCount, GFXSCT_Float4x3 ); + } +} + void ProcessedShaderMaterial::setSceneInfo(SceneRenderState * state, const SceneData& sgData, U32 pass) { PROFILE_SCOPE( ProcessedShaderMaterial_setSceneInfo ); diff --git a/Engine/source/materials/processedShaderMaterial.h b/Engine/source/materials/processedShaderMaterial.h index 4f7c67023..b107a52e8 100644 --- a/Engine/source/materials/processedShaderMaterial.h +++ b/Engine/source/materials/processedShaderMaterial.h @@ -93,6 +93,8 @@ public: GFXShaderConstHandle* mTexHandlesSC[Material::MAX_TEX_PER_PASS]; GFXShaderConstHandle* mRTParamsSC[TEXTURE_STAGE_COUNT]; + GFXShaderConstHandle* mNodeTransforms; + void init( GFXShader* shader, CustomMaterial* mat = NULL ); }; @@ -128,6 +130,7 @@ public: virtual bool setupPass(SceneRenderState *, const SceneData& sgData, U32 pass); virtual void setTextureStages(SceneRenderState *, const SceneData &sgData, U32 pass ); virtual void setTransforms(const MatrixSet &matrixSet, SceneRenderState *state, const U32 pass); + virtual void setNodeTransforms(const MatrixF *address, const U32 numTransforms, const U32 pass); virtual void setSceneInfo(SceneRenderState *, const SceneData& sgData, U32 pass); virtual void setBuffers(GFXVertexBufferHandleBase* vertBuffer, GFXPrimitiveBufferHandle* primBuffer); virtual bool stepInstance(); diff --git a/Engine/source/renderInstance/renderGlowMgr.cpp b/Engine/source/renderInstance/renderGlowMgr.cpp index a5c31c5d6..1ce49149d 100644 --- a/Engine/source/renderInstance/renderGlowMgr.cpp +++ b/Engine/source/renderInstance/renderGlowMgr.cpp @@ -245,6 +245,12 @@ void RenderGlowMgr::render( SceneRenderState *state ) matrixSet.setProjection(*passRI->projection); glowMat->setTransforms(matrixSet, state); + // Setup HW skinning transforms if applicable + if (glowMat->usesHardwareSkinning()) + { + glowMat->setNodeTransforms(passRI->mNodeTransforms, passRI->mNodeTransformCount); + } + glowMat->setSceneInfo(state, sgData); glowMat->setBuffers(passRI->vertBuff, passRI->primBuff); diff --git a/Engine/source/renderInstance/renderMeshMgr.cpp b/Engine/source/renderInstance/renderMeshMgr.cpp index b224e5469..476c5e9b8 100644 --- a/Engine/source/renderInstance/renderMeshMgr.cpp +++ b/Engine/source/renderInstance/renderMeshMgr.cpp @@ -176,6 +176,12 @@ void RenderMeshMgr::render(SceneRenderState * state) matrixSet.setProjection(*passRI->projection); mat->setTransforms(matrixSet, state); + // Setup HW skinning transforms if applicable + if (mat->usesHardwareSkinning()) + { + mat->setNodeTransforms(passRI->mNodeTransforms, passRI->mNodeTransformCount); + } + setupSGData( passRI, sgData ); mat->setSceneInfo( state, sgData ); diff --git a/Engine/source/renderInstance/renderPassManager.h b/Engine/source/renderInstance/renderPassManager.h index 2aa1e37ee..98080365b 100644 --- a/Engine/source/renderInstance/renderPassManager.h +++ b/Engine/source/renderInstance/renderPassManager.h @@ -371,6 +371,17 @@ struct MeshRenderInst : public RenderInst GFXTextureObject *accuTex; GFXCubemap *cubemap; + /// @name Hardware Skinning + /// { + MatrixF *mNodeTransforms; + U32 mNodeTransformCount; + /// } + +#ifdef TORQUE_ENABLE_GFXDEBUGEVENTS + const char *meshName; + const char *objectName; +#endif + void clear(); }; diff --git a/Engine/source/renderInstance/renderPrePassMgr.cpp b/Engine/source/renderInstance/renderPrePassMgr.cpp index 66232374b..2c3f2c4c2 100644 --- a/Engine/source/renderInstance/renderPrePassMgr.cpp +++ b/Engine/source/renderInstance/renderPrePassMgr.cpp @@ -430,6 +430,12 @@ void RenderPrePassMgr::render( SceneRenderState *state ) matrixSet.setProjection(*passRI->projection); mat->setTransforms(matrixSet, state); + // Setup HW skinning transforms if applicable + if (mat->usesHardwareSkinning()) + { + mat->setNodeTransforms(passRI->mNodeTransforms, passRI->mNodeTransformCount); + } + // If we're instanced then don't render yet. if ( mat->isInstanced() ) { diff --git a/Engine/source/renderInstance/renderTranslucentMgr.cpp b/Engine/source/renderInstance/renderTranslucentMgr.cpp index 7ad324a26..e0c3e6bb4 100644 --- a/Engine/source/renderInstance/renderTranslucentMgr.cpp +++ b/Engine/source/renderInstance/renderTranslucentMgr.cpp @@ -243,6 +243,12 @@ void RenderTranslucentMgr::render( SceneRenderState *state ) matrixSet.setProjection(*passRI->projection); mat->setTransforms(matrixSet, state); + // Setup HW skinning transforms if applicable + if (mat->usesHardwareSkinning()) + { + mat->setNodeTransforms(passRI->mNodeTransforms, passRI->mNodeTransformCount); + } + // If we're instanced then don't render yet. if ( mat->isInstanced() ) { diff --git a/Engine/source/shaderGen/GLSL/shaderCompGLSL.cpp b/Engine/source/shaderGen/GLSL/shaderCompGLSL.cpp index 576f12ef5..a6e55271b 100644 --- a/Engine/source/shaderGen/GLSL/shaderCompGLSL.cpp +++ b/Engine/source/shaderGen/GLSL/shaderCompGLSL.cpp @@ -101,6 +101,30 @@ Var * AppVertConnectorGLSL::getElement( RegisterType type, return newVar; } + case RT_BLENDINDICES: + { + Var *newVar = new Var; + newVar->constNum = mCurBlendIndicesElem; + mElementList.push_back(newVar); + char out[32]; + dSprintf((char*)out, sizeof(out), "vBlendIndex%d", mCurBlendIndicesElem); + mCurBlendIndicesElem += 1; + newVar->setConnectName(out); + return newVar; + } + + case RT_BLENDWEIGHT: + { + Var *newVar = new Var; + newVar->constNum = mCurBlendWeightsElem; + mElementList.push_back(newVar); + char out[32]; + dSprintf((char*)out, sizeof(out), "vBlendWeight%d", mCurBlendWeightsElem); + mCurBlendWeightsElem += 1; + newVar->setConnectName(out); + return newVar; + } + default: break; } diff --git a/Engine/source/shaderGen/GLSL/shaderFeatureGLSL.cpp b/Engine/source/shaderGen/GLSL/shaderFeatureGLSL.cpp index e927deb06..cabedc14c 100644 --- a/Engine/source/shaderGen/GLSL/shaderFeatureGLSL.cpp +++ b/Engine/source/shaderGen/GLSL/shaderFeatureGLSL.cpp @@ -33,6 +33,7 @@ #include "core/util/autoPtr.h" #include "lighting/advanced/advancedLightBinManager.h" +#include "ts/tsShape.h" LangElement * ShaderFeatureGLSL::setupTexSpaceMat( Vector &, // componentList Var **texSpaceMat ) @@ -2795,4 +2796,68 @@ void ImposterVertFeatureGLSL::determineFeature( Material *material, { if ( features.hasFeature( MFT_ImposterVert ) ) outFeatureData->features.addFeature( MFT_ImposterVert ); -} \ No newline at end of file +} + +//**************************************************************************** +// HardwareSkinningFeatureGLSL +//**************************************************************************** + +void HardwareSkinningFeatureGLSL::processVert(Vector &componentList, + const MaterialFeatureData &fd) +{ + MultiLine *meta = new MultiLine; + + Var *inPosition = (Var*)LangElement::find("inPosition"); + Var *inNormal = (Var*)LangElement::find("inNormal"); + + if (!inPosition) + inPosition = (Var*)LangElement::find("position"); + + if (!inNormal) + inNormal = (Var*)LangElement::find("normal"); + + Var* posePos = new Var("posePos", "vec3"); + Var* poseNormal = new Var("poseNormal", "vec3"); + Var* poseMat = new Var("poseMat", "mat4x3"); + Var* poseRotMat = new Var("poseRotMat", "mat3x3"); + Var* nodeTransforms = (Var*)LangElement::find("nodeTransforms"); + + if (!nodeTransforms) + { + nodeTransforms = new Var("nodeTransforms", "mat4x3"); + nodeTransforms->uniform = true; + nodeTransforms->arraySize = TSShape::smMaxSkinBones; + nodeTransforms->constSortPos = cspPrimitive; + } + + U32 numIndices = mVertexFormat->getNumBlendIndices(); + meta->addStatement(new GenOp(" @ = vec3(0.0);\r\n", new DecOp(posePos))); + meta->addStatement(new GenOp(" @ = vec3(0.0);\r\n", new DecOp(poseNormal))); + meta->addStatement(new GenOp(" @;\r\n", new DecOp(poseMat))); + meta->addStatement(new GenOp(" @;\r\n int i;\r\n", new DecOp(poseRotMat))); + + for (U32 i = 0; iaddStatement(new GenOp(" for (i=0; i<4; i++) {\r\n")); + meta->addStatement(new GenOp(" int poseIdx = int(@[i]);\r\n", inIndices)); + meta->addStatement(new GenOp(" float poseWeight = @[i];\r\n", inWeights)); + meta->addStatement(new GenOp(" @ = @[poseIdx];\r\n", poseMat, nodeTransforms)); + meta->addStatement(new GenOp(" @ = mat3x3(@);\r\n", poseRotMat, poseMat)); + meta->addStatement(new GenOp(" @ += (@ * vec4(@, 1)).xyz * poseWeight;\r\n", posePos, poseMat, inPosition)); + meta->addStatement(new GenOp(" @ += ((@ * @) * poseWeight);\r\n", poseNormal, poseRotMat, inNormal)); + meta->addStatement(new GenOp(" }\r\n")); + } + + // Assign new position and normal + meta->addStatement(new GenOp(" @ = @;\r\n", inPosition, posePos)); + meta->addStatement(new GenOp(" @ = normalize(@);\r\n", inNormal, poseNormal)); + + output = meta; +} diff --git a/Engine/source/shaderGen/GLSL/shaderFeatureGLSL.h b/Engine/source/shaderGen/GLSL/shaderFeatureGLSL.h index 984e66092..f890ec046 100644 --- a/Engine/source/shaderGen/GLSL/shaderFeatureGLSL.h +++ b/Engine/source/shaderGen/GLSL/shaderFeatureGLSL.h @@ -659,4 +659,17 @@ public: MaterialFeatureData *outFeatureData ); }; +/// Hardware Skinning +class HardwareSkinningFeatureGLSL : public ShaderFeatureGLSL +{ +protected: + +public: + + virtual void processVert(Vector &componentList, + const MaterialFeatureData &fd); + + virtual String getName() { return "Hardware Skinning"; } +}; + #endif // _SHADERGEN_GLSL_SHADERFEATUREGLSL_H_ diff --git a/Engine/source/shaderGen/GLSL/shaderGenGLSL.cpp b/Engine/source/shaderGen/GLSL/shaderGenGLSL.cpp index d89682951..6e068c7cf 100644 --- a/Engine/source/shaderGen/GLSL/shaderGenGLSL.cpp +++ b/Engine/source/shaderGen/GLSL/shaderGenGLSL.cpp @@ -113,6 +113,9 @@ const char* ShaderGenComponentFactoryGLSL::typeToString( GFXDeclType type ) case GFXDeclType_Float3: return "vec3"; + case GFXDeclType_UByte4: + return "vec4"; + case GFXDeclType_Float4: case GFXDeclType_Color: return "vec4"; @@ -160,6 +163,16 @@ ShaderComponent* ShaderGenComponentFactoryGLSL::createVertexInputConnector( cons var = vertComp->getElement( RT_COLOR ); var->setName( "diffuse" ); } + else if (element.isSemantic(GFXSemantic::BLENDINDICES)) + { + var = vertComp->getElement(RT_BLENDINDICES); + var->setName(String::ToString("vBlendIndex%d", element.getSemanticIndex())); + } + else if (element.isSemantic(GFXSemantic::BLENDWEIGHT)) + { + var = vertComp->getElement(RT_BLENDWEIGHT); + var->setName(String::ToString("vBlendWeight%d", element.getSemanticIndex())); + } else if ( element.isSemantic( GFXSemantic::TEXCOORD ) ) { var = vertComp->getElement( RT_TEXCOORD ); diff --git a/Engine/source/shaderGen/GLSL/shaderGenGLSLInit.cpp b/Engine/source/shaderGen/GLSL/shaderGenGLSLInit.cpp index 0a14af629..28e3cd00c 100644 --- a/Engine/source/shaderGen/GLSL/shaderGenGLSLInit.cpp +++ b/Engine/source/shaderGen/GLSL/shaderGenGLSLInit.cpp @@ -105,6 +105,7 @@ void _initShaderGenGLSL( ShaderGen *shaderGen ) FEATUREMGR->registerFeature( MFT_DeferredMatInfoFlags, new DeferredMatInfoFlagsGLSL ); FEATUREMGR->registerFeature( MFT_DeferredEmptySpec, new DeferredEmptySpecGLSL ); FEATUREMGR->registerFeature( MFT_SkyBox, new NamedFeatureGLSL( "skybox" ) ); + FEATUREMGR->registerFeature( MFT_HardwareSkinning, new HardwareSkinningFeatureGLSL ); } MODULE_BEGIN( ShaderGenGLSL ) diff --git a/Engine/source/shaderGen/HLSL/shaderCompHLSL.cpp b/Engine/source/shaderGen/HLSL/shaderCompHLSL.cpp index 6aece4ef0..b01419a8b 100644 --- a/Engine/source/shaderGen/HLSL/shaderCompHLSL.cpp +++ b/Engine/source/shaderGen/HLSL/shaderCompHLSL.cpp @@ -32,7 +32,20 @@ Var * ShaderConnectorHLSL::getElement( RegisterType type, U32 numElements, U32 numRegisters ) { - Var *ret = getIndexedElement( mCurTexElem, type, numElements, numRegisters ); + Var *ret = NULL; + + if ( type == RT_BLENDINDICES ) + { + ret = getIndexedElement( mCurBlendIndicesElem, type, numElements, numRegisters ); + } + else if ( type == RT_BLENDWEIGHT ) + { + ret = getIndexedElement( mCurBlendWeightsElem, type, numElements, numRegisters ); + } + else + { + ret = getIndexedElement( mCurTexElem, type, numElements, numRegisters ); + } // Adjust texture offset if this is a texcoord type if( type == RT_TEXCOORD ) @@ -42,6 +55,20 @@ Var * ShaderConnectorHLSL::getElement( RegisterType type, else mCurTexElem += numElements; } + else if ( type == RT_BLENDINDICES ) + { + if ( numRegisters != -1 ) + mCurBlendIndicesElem += numRegisters; + else + mCurBlendIndicesElem += numElements; + } + else if ( type == RT_BLENDWEIGHT ) + { + if ( numRegisters != -1 ) + mCurBlendWeightsElem += numRegisters; + else + mCurBlendWeightsElem += numElements; + } return ret; } @@ -133,6 +160,46 @@ Var * ShaderConnectorHLSL::getIndexedElement( U32 index, RegisterType type, U32 return newVar; } + case RT_BLENDINDICES: + { + Var *newVar = new Var; + mElementList.push_back( newVar ); + + // This was needed for hardware instancing, but + // i don't really remember why right now. + if ( index > mCurBlendIndicesElem ) + mCurBlendIndicesElem = index + 1; + + char out[32]; + dSprintf( (char*)out, sizeof(out), "BLENDINDICES%d", index ); + newVar->setConnectName( out ); + newVar->constNum = index; + newVar->arraySize = numElements; + + return newVar; + } + + case RT_BLENDWEIGHT: + { + Var *newVar = new Var; + mElementList.push_back( newVar ); + + // This was needed for hardware instancing, but + // i don't really remember why right now. + if ( index > mCurBlendWeightsElem ) + mCurBlendWeightsElem = index + 1; + + char out[32]; + dSprintf( (char*)out, sizeof(out), "BLENDWEIGHT%d", index ); + newVar->setConnectName( out ); + newVar->constNum = index; + newVar->arraySize = numElements; + + return newVar; + } + + + default: break; } @@ -177,6 +244,8 @@ void ShaderConnectorHLSL::reset() mElementList.setSize( 0 ); mCurTexElem = 0; + mCurBlendIndicesElem = 0; + mCurBlendWeightsElem = 0; } void ShaderConnectorHLSL::print( Stream &stream, bool isVertexShader ) @@ -231,12 +300,23 @@ void ParamsDefHLSL::assignConstantNumbers() if (dStrcmp((const char*)var->type, "float4x4") == 0) { mCurrConst += (4 * var->arraySize); - } else { + } + else + { if (dStrcmp((const char*)var->type, "float3x3") == 0) { mCurrConst += (3 * var->arraySize); - } else { - mCurrConst += var->arraySize; + } + else + { + if (dStrcmp((const char*)var->type, "float4x3") == 0) + { + mCurrConst += (3 * var->arraySize); + } + else + { + mCurrConst += var->arraySize; + } } } } diff --git a/Engine/source/shaderGen/HLSL/shaderFeatureHLSL.cpp b/Engine/source/shaderGen/HLSL/shaderFeatureHLSL.cpp index 22c48ecc5..e37b23a9b 100644 --- a/Engine/source/shaderGen/HLSL/shaderFeatureHLSL.cpp +++ b/Engine/source/shaderGen/HLSL/shaderFeatureHLSL.cpp @@ -33,6 +33,7 @@ #include "core/util/autoPtr.h" #include "lighting/advanced/advancedLightBinManager.h" +#include "ts/tsShape.h" LangElement * ShaderFeatureHLSL::setupTexSpaceMat( Vector &, // componentList Var **texSpaceMat ) @@ -2991,3 +2992,66 @@ void ImposterVertFeatureHLSL::determineFeature( Material *material, outFeatureData->features.addFeature( MFT_ImposterVert ); } +//**************************************************************************** +// HardwareSkinningFeatureHLSL +//**************************************************************************** + +void HardwareSkinningFeatureHLSL::processVert( Vector &componentList, + const MaterialFeatureData &fd ) +{ + MultiLine *meta = new MultiLine; + + Var *inPosition = (Var*)LangElement::find( "inPosition" ); + Var *inNormal = (Var*)LangElement::find( "inNormal" ); + + if ( !inPosition ) + inPosition = (Var*)LangElement::find( "position" ); + + if ( !inNormal ) + inNormal = (Var*)LangElement::find( "normal" ); + + Var* posePos = new Var("posePos", "float3"); + Var* poseNormal = new Var("poseNormal", "float3"); + Var* poseMat = new Var("poseMat", "float4x3"); + Var* poseRotMat = new Var("poseRotMat", "float3x3"); + Var* nodeTransforms = (Var*)LangElement::find("nodeTransforms"); + + if (!nodeTransforms) + { + nodeTransforms = new Var("nodeTransforms", "float4x3"); + nodeTransforms->uniform = true; + nodeTransforms->arraySize = TSShape::smMaxSkinBones; + nodeTransforms->constSortPos = cspPotentialPrimitive; + } + + U32 numIndices = mVertexFormat->getNumBlendIndices(); + meta->addStatement( new GenOp( " @ = 0.0;\r\n", new DecOp( posePos ) ) ); + meta->addStatement( new GenOp( " @ = 0.0;\r\n", new DecOp( poseNormal ) ) ); + meta->addStatement( new GenOp( " @;\r\n", new DecOp( poseMat ) ) ); + meta->addStatement(new GenOp(" @;\r\n int i;\r\n", new DecOp(poseRotMat))); + + for (U32 i=0; iaddStatement( new GenOp( " for (i=0; i<4; i++) {\r\n" ) ); + meta->addStatement( new GenOp( " int poseIdx = int(@[i]);\r\n", inIndices ) ); + meta->addStatement( new GenOp( " float poseWeight = @[i];\r\n", inWeights) ); + meta->addStatement( new GenOp( " @ = @[poseIdx];\r\n", poseMat, nodeTransforms) ); + meta->addStatement( new GenOp( " @ = (float3x3)@;\r\n", poseRotMat, poseMat) ); + meta->addStatement( new GenOp( " @ += (mul(float4(@, 1), @)).xyz * poseWeight;\r\n", posePos, inPosition, poseMat) ); + meta->addStatement( new GenOp( " @ += (mul(@,@) * poseWeight);\r\n", poseNormal, inNormal, poseRotMat) ); + meta->addStatement( new GenOp( " }\r\n" ) ); + } + + // Assign new position and normal + meta->addStatement( new GenOp( " @ = @;\r\n", inPosition, posePos ) ); + meta->addStatement( new GenOp( " @ = normalize(@);\r\n", inNormal, poseNormal ) ); + + output = meta; +} diff --git a/Engine/source/shaderGen/HLSL/shaderFeatureHLSL.h b/Engine/source/shaderGen/HLSL/shaderFeatureHLSL.h index 8be2400f5..960b9b491 100644 --- a/Engine/source/shaderGen/HLSL/shaderFeatureHLSL.h +++ b/Engine/source/shaderGen/HLSL/shaderFeatureHLSL.h @@ -663,4 +663,17 @@ public: MaterialFeatureData *outFeatureData ); }; +/// Hardware Skinning +class HardwareSkinningFeatureHLSL : public ShaderFeatureHLSL +{ +protected: + +public: + + virtual void processVert( Vector &componentList, + const MaterialFeatureData &fd ); + + virtual String getName() { return "Hardware Skinning"; } +}; + #endif // _SHADERGEN_HLSL_SHADERFEATUREHLSL_H_ diff --git a/Engine/source/shaderGen/HLSL/shaderGenHLSL.cpp b/Engine/source/shaderGen/HLSL/shaderGenHLSL.cpp index 49487624f..0e9a09eb2 100644 --- a/Engine/source/shaderGen/HLSL/shaderGenHLSL.cpp +++ b/Engine/source/shaderGen/HLSL/shaderGenHLSL.cpp @@ -121,6 +121,9 @@ const char* ShaderGenComponentFactoryHLSL::typeToString( GFXDeclType type ) case GFXDeclType_Float4: case GFXDeclType_Color: return "float4"; + + case GFXDeclType_UByte4: + return "uint4"; } } @@ -174,6 +177,22 @@ ShaderComponent* ShaderGenComponentFactoryHLSL::createVertexInputConnector( cons else var->setName( String::ToString( "texCoord%d", element.getSemanticIndex() + 1 ) ); } + else if ( element.isSemantic( GFXSemantic::BLENDINDICES ) ) + { + var = vertComp->getIndexedElement( element.getSemanticIndex(), RT_BLENDINDICES ); + var->setName( String::ToString( "blendIndices%d", element.getSemanticIndex() ) ); + } + else if ( element.isSemantic( GFXSemantic::BLENDWEIGHT ) ) + { + var = vertComp->getIndexedElement( element.getSemanticIndex(), RT_BLENDWEIGHT ); + var->setName( String::ToString( "blendWeight%d", element.getSemanticIndex() ) ); + } + else if ( element.isSemantic( GFXSemantic::PADDING ) ) + { + var = NULL; + //var = vertComp->getIndexedElement( vertComp->getCurTexElem() + element.getSemanticIndex(), RT_TEXCOORD ); + //var->setName( String::ToString( "pad%d", element.getSemanticIndex() + 1 ) ); + } else { // Everything else is a texcoord! diff --git a/Engine/source/shaderGen/HLSL/shaderGenHLSLInit.cpp b/Engine/source/shaderGen/HLSL/shaderGenHLSLInit.cpp index 167b4eafe..8ea9bbf0f 100644 --- a/Engine/source/shaderGen/HLSL/shaderGenHLSLInit.cpp +++ b/Engine/source/shaderGen/HLSL/shaderGenHLSLInit.cpp @@ -100,13 +100,13 @@ void _initShaderGenHLSL( ShaderGen *shaderGen ) FEATUREMGR->registerFeature( MFT_ImposterVert, new ImposterVertFeatureHLSL ); - // Deferred Shading FEATUREMGR->registerFeature( MFT_isDeferred, new NamedFeatureHLSL( "Deferred Material" ) ); FEATUREMGR->registerFeature( MFT_DeferredSpecMap, new DeferredSpecMapHLSL ); FEATUREMGR->registerFeature( MFT_DeferredSpecVars, new DeferredSpecVarsHLSL ); FEATUREMGR->registerFeature( MFT_DeferredMatInfoFlags, new DeferredMatInfoFlagsHLSL ); FEATUREMGR->registerFeature( MFT_DeferredEmptySpec, new DeferredEmptySpecHLSL ); FEATUREMGR->registerFeature( MFT_SkyBox, new NamedFeatureHLSL( "skybox" ) ); + FEATUREMGR->registerFeature( MFT_HardwareSkinning, new HardwareSkinningFeatureHLSL ); } MODULE_BEGIN( ShaderGenHLSL ) diff --git a/Engine/source/shaderGen/shaderComp.cpp b/Engine/source/shaderGen/shaderComp.cpp index 2110c209f..f55ddf161 100644 --- a/Engine/source/shaderGen/shaderComp.cpp +++ b/Engine/source/shaderGen/shaderComp.cpp @@ -32,6 +32,8 @@ ShaderConnector::ShaderConnector() { mCurTexElem = 0; mName[0] = '\0'; + mCurBlendIndicesElem = 0; + mCurBlendWeightsElem = 0; } //---------------------------------------------------------------------------- diff --git a/Engine/source/shaderGen/shaderComp.h b/Engine/source/shaderGen/shaderComp.h index e8903e613..ac6d9c539 100644 --- a/Engine/source/shaderGen/shaderComp.h +++ b/Engine/source/shaderGen/shaderComp.h @@ -71,6 +71,8 @@ protected: Vector mElementList; U32 mCurTexElem; + U32 mCurBlendIndicesElem; + U32 mCurBlendWeightsElem; U8 mName[32]; public: @@ -78,6 +80,8 @@ public: ShaderConnector(); virtual ~ShaderConnector(); + U32 getCurTexElem() { return mCurTexElem; } + /// virtual Var* getElement( RegisterType type, U32 numElements = 1, diff --git a/Engine/source/shaderGen/shaderFeature.h b/Engine/source/shaderGen/shaderFeature.h index 03426b733..96f8912ee 100644 --- a/Engine/source/shaderGen/shaderFeature.h +++ b/Engine/source/shaderGen/shaderFeature.h @@ -99,6 +99,12 @@ protected: /// S32 mProcessIndex; +public: + + // TODO: Make this protected and give it a proper API. + const GFXVertexFormat *mVertexFormat; + + // TODO: Make this protected and give it a proper API. GFXVertexFormat *mInstancingFormat; public: @@ -139,7 +145,8 @@ public: ShaderFeature() : output( NULL ), mProcessIndex( 0 ), - mInstancingFormat( NULL ) + mInstancingFormat( NULL ), + mVertexFormat( NULL ) { } @@ -285,7 +292,7 @@ public: /// Called after processing the vertex and processing the pixel /// to cleanup any temporary structures stored in the feature. - virtual void reset() { output = NULL; mProcessIndex = 0; mInstancingFormat = NULL; } + virtual void reset() { output = NULL; mProcessIndex = 0; mInstancingFormat = NULL; mVertexFormat = NULL; } /// A simpler helper function which either finds /// the existing local var or creates one. diff --git a/Engine/source/shaderGen/shaderGen.cpp b/Engine/source/shaderGen/shaderGen.cpp index eb7685b39..e2c342a86 100644 --- a/Engine/source/shaderGen/shaderGen.cpp +++ b/Engine/source/shaderGen/shaderGen.cpp @@ -265,6 +265,9 @@ void ShaderGen::_processVertFeatures( Vector ¯os, bool macro continue; feature->setInstancingFormat( &mInstancingFormat ); + + feature->mVertexFormat = mVertexFormat; + feature->processVert( mComponents, mFeatureData ); String line; diff --git a/Engine/source/ts/arch/tsMeshIntrinsics.arch.h b/Engine/source/ts/arch/tsMeshIntrinsics.arch.h index ee3373c61..f6fe7b2c7 100644 --- a/Engine/source/ts/arch/tsMeshIntrinsics.arch.h +++ b/Engine/source/ts/arch/tsMeshIntrinsics.arch.h @@ -26,19 +26,13 @@ #if defined(TORQUE_CPU_X86) # // x86 CPU family implementations extern void zero_vert_normal_bulk_SSE(const dsize_t count, U8 * __restrict const outPtr, const dsize_t outStride); -extern void m_matF_x_BatchedVertWeightList_SSE(const MatrixF &mat, const dsize_t count, const TSSkinMesh::BatchData::BatchedVertWeight * __restrict batch, U8 * const __restrict outPtr, const dsize_t outStride); -#if (_MSC_VER >= 1500) -extern void m_matF_x_BatchedVertWeightList_SSE4(const MatrixF &mat, const dsize_t count, const TSSkinMesh::BatchData::BatchedVertWeight * __restrict batch, U8 * const __restrict outPtr, const dsize_t outStride); -#endif # #elif defined(TORQUE_CPU_PPC) # // PPC CPU family implementations # if defined(TORQUE_OS_XENON) extern void zero_vert_normal_bulk_X360(const dsize_t count, U8 * __restrict const outPtr, const dsize_t outStride); -extern void m_matF_x_BatchedVertWeightList_X360(const MatrixF &mat, const dsize_t count, const TSSkinMesh::BatchData::BatchedVertWeight * __restrict batch, U8 * const __restrict outPtr, const dsize_t outStride); # else extern void zero_vert_normal_bulk_gccvec(const dsize_t count, U8 * __restrict const outPtr, const dsize_t outStride); -extern void m_matF_x_BatchedVertWeightList_gccvec(const MatrixF &mat, const dsize_t count, const TSSkinMesh::BatchData::BatchedVertWeight * __restrict batch, U8 * const __restrict outPtr, const dsize_t outStride); # endif # #else diff --git a/Engine/source/ts/arch/tsMeshIntrinsics.sse.cpp b/Engine/source/ts/arch/tsMeshIntrinsics.sse.cpp index 1e343ac23..0a68353c4 100644 --- a/Engine/source/ts/arch/tsMeshIntrinsics.sse.cpp +++ b/Engine/source/ts/arch/tsMeshIntrinsics.sse.cpp @@ -67,117 +67,4 @@ void zero_vert_normal_bulk_SSE(const dsize_t count, U8 * __restrict const outPtr //------------------------------------------------------------------------------ -void m_matF_x_BatchedVertWeightList_SSE(const MatrixF &mat, - const dsize_t count, - const TSSkinMesh::BatchData::BatchedVertWeight * __restrict batch, - U8 * const __restrict outPtr, - const dsize_t outStride) -{ - const char * __restrict iPtr = reinterpret_cast(batch); - const dsize_t inStride = sizeof(TSSkinMesh::BatchData::BatchedVertWeight); - - // SSE intrinsic version - // Based on: http://www.cortstratton.org/articles/HugiCode.html - - // Load matrix, transposed, into registers - MatrixF transMat; - mat.transposeTo(transMat); - register __m128 sseMat[4]; - - sseMat[0] = _mm_loadu_ps(&transMat[0]); - sseMat[1] = _mm_loadu_ps(&transMat[4]); - sseMat[2] = _mm_loadu_ps(&transMat[8]); - sseMat[3] = _mm_loadu_ps(&transMat[12]); - - // mask - const __m128 _w_mask = { 1.0f, 1.0f, 1.0f, 0.0f }; - - // temp registers - register __m128 tempPos; - register __m128 tempNrm; - register __m128 scratch0; - register __m128 scratch1; - register __m128 inPos; - register __m128 inNrm; - - // pre-populate cache - const TSSkinMesh::BatchData::BatchedVertWeight &firstElem = batch[0]; - for(S32 i = 0; i < 8; i++) - { - _mm_prefetch(reinterpret_cast(iPtr + inStride * i), _MM_HINT_T0); - _mm_prefetch(reinterpret_cast(outPtr + outStride * (i + firstElem.vidx)), _MM_HINT_T0); - } - - for(register S32 i = 0; i < count; i++) - { - const TSSkinMesh::BatchData::BatchedVertWeight &inElem = batch[i]; - TSMesh::__TSMeshVertexBase *outElem = reinterpret_cast(outPtr + inElem.vidx * outStride); - - // process x (hiding the prefetches in the delays) - inPos = _mm_load_ps(inElem.vert); - inNrm = _mm_load_ps(inElem.normal); - - // prefetch input -#define INPUT_PREFETCH_LOOKAHEAD 64 - const char *prefetchInput = reinterpret_cast(batch) + inStride * (i + INPUT_PREFETCH_LOOKAHEAD); - _mm_prefetch(prefetchInput, _MM_HINT_T0); - - // propagate the .x elements across the vectors - tempPos = _mm_shuffle_ps(inPos, inPos, _MM_SHUFFLE(0, 0, 0, 0)); - tempNrm = _mm_shuffle_ps(inNrm, inNrm, _MM_SHUFFLE(0, 0, 0, 0)); - - // prefetch ouput with half the lookahead distance of the input -#define OUTPUT_PREFETCH_LOOKAHEAD (INPUT_PREFETCH_LOOKAHEAD >> 1) - const char *outPrefetch = reinterpret_cast(outPtr) + outStride * (inElem.vidx + OUTPUT_PREFETCH_LOOKAHEAD); - _mm_prefetch(outPrefetch, _MM_HINT_T0); - - // mul by column 0 - tempPos = _mm_mul_ps(tempPos, sseMat[0]); - tempNrm = _mm_mul_ps(tempNrm, sseMat[0]); - - // process y - scratch0 = _mm_shuffle_ps(inPos, inPos, _MM_SHUFFLE(1, 1, 1, 1)); - scratch1 = _mm_shuffle_ps(inNrm, inNrm, _MM_SHUFFLE(1, 1, 1, 1)); - - scratch0 = _mm_mul_ps(scratch0, sseMat[1]); - scratch1 = _mm_mul_ps(scratch1, sseMat[1]); - - tempPos = _mm_add_ps(tempPos, scratch0); - tempNrm = _mm_add_ps(tempNrm, scratch1); - - - // process z - scratch0 = _mm_shuffle_ps(inPos, inPos, _MM_SHUFFLE(2, 2, 2, 2)); - scratch1 = _mm_shuffle_ps(inNrm, inNrm, _MM_SHUFFLE(2, 2, 2, 2)); - - scratch0 = _mm_mul_ps(scratch0, sseMat[2]); - scratch1 = _mm_mul_ps(scratch1, sseMat[2]); - - tempPos = _mm_add_ps(tempPos, scratch0); - - inNrm = _mm_load_ps(outElem->_normal); //< load normal for accumulation - scratch0 = _mm_shuffle_ps(inPos, inPos, _MM_SHUFFLE(3, 3, 3, 3));//< load bone weight across all elements of scratch0 - - tempNrm = _mm_add_ps(tempNrm, scratch1); - - scratch0 = _mm_mul_ps(scratch0, _w_mask); //< mask off last - - // Translate the position by adding the 4th column of the matrix to it - tempPos = _mm_add_ps(tempPos, sseMat[3]); - - // now multiply by the blend weight, and mask out the W component of both vectors - tempPos = _mm_mul_ps(tempPos, scratch0); - tempNrm = _mm_mul_ps(tempNrm, scratch0); - - inPos = _mm_load_ps(outElem->_vert); //< load position for accumulation - - // accumulate with previous values - tempNrm = _mm_add_ps(tempNrm, inNrm); - tempPos = _mm_add_ps(tempPos, inPos); - - _mm_store_ps(outElem->_vert, tempPos); //< output position - _mm_store_ps(outElem->_normal, tempNrm); //< output normal - } -} - #endif // TORQUE_CPU_X86 \ No newline at end of file diff --git a/Engine/source/ts/arch/tsMeshIntrinsics.sse4.cpp b/Engine/source/ts/arch/tsMeshIntrinsics.sse4.cpp index 9aba0f027..76dac3fd2 100644 --- a/Engine/source/ts/arch/tsMeshIntrinsics.sse4.cpp +++ b/Engine/source/ts/arch/tsMeshIntrinsics.sse4.cpp @@ -25,84 +25,4 @@ #include "ts/tsMeshIntrinsics.h" #include -void m_matF_x_BatchedVertWeightList_SSE4(const MatrixF &mat, - const dsize_t count, - const TSSkinMesh::BatchData::BatchedVertWeight * __restrict batch, - U8 * const __restrict outPtr, - const dsize_t outStride) -{ - const char * __restrict iPtr = reinterpret_cast(batch); - const dsize_t inStride = sizeof(TSSkinMesh::BatchData::BatchedVertWeight); - - __m128 sseMat[3]; - sseMat[0] = _mm_loadu_ps(&mat[0]); - sseMat[1] = _mm_loadu_ps(&mat[4]); - sseMat[2] = _mm_loadu_ps(&mat[8]); - - // temp registers - __m128 inPos, tempPos; - __m128 inNrm, tempNrm; - __m128 temp0, temp1, temp2, temp3; - - // pre-populate cache - const TSSkinMesh::BatchData::BatchedVertWeight &firstElem = batch[0]; - for(S32 i = 0; i < 8; i++) - { - _mm_prefetch(reinterpret_cast(iPtr + inStride * i), _MM_HINT_T0); - _mm_prefetch(reinterpret_cast(outPtr + outStride * (i + firstElem.vidx)), _MM_HINT_T0); - } - - for(S32 i = 0; i < count; i++) - { - const TSSkinMesh::BatchData::BatchedVertWeight &inElem = batch[i]; - TSMesh::__TSMeshVertexBase *outElem = reinterpret_cast(outPtr + inElem.vidx * outStride); - - // process x (hiding the prefetches in the delays) - inPos = _mm_load_ps(inElem.vert); - inNrm = _mm_load_ps(inElem.normal); - - // prefetch input -#define INPUT_PREFETCH_LOOKAHEAD 64 - const char *prefetchInput = reinterpret_cast(batch) + inStride * (i + INPUT_PREFETCH_LOOKAHEAD); - _mm_prefetch(prefetchInput, _MM_HINT_T0); - - // prefetch ouput with half the lookahead distance of the input -#define OUTPUT_PREFETCH_LOOKAHEAD (INPUT_PREFETCH_LOOKAHEAD >> 1) - const char *outPrefetch = reinterpret_cast(outPtr) + outStride * (inElem.vidx + OUTPUT_PREFETCH_LOOKAHEAD); - _mm_prefetch(outPrefetch, _MM_HINT_T0); - - // Multiply position - tempPos = _mm_dp_ps(inPos, sseMat[0], 0xF1); - temp0 = _mm_dp_ps(inPos, sseMat[1], 0xF2); - temp1 = _mm_dp_ps(inPos, sseMat[2], 0xF4); - - temp0 = _mm_or_ps(temp0, temp1); - tempPos = _mm_or_ps(tempPos, temp0); - - // Multiply normal - tempNrm = _mm_dp_ps(inNrm, sseMat[0], 0x71); - temp2 = _mm_dp_ps(inNrm, sseMat[1], 0x72); - temp3 = _mm_dp_ps(inNrm, sseMat[2], 0x74); - - temp2 = _mm_or_ps(temp2, temp3); - tempNrm = _mm_or_ps(tempNrm, temp2); - - // Load bone weight and multiply - temp3 = _mm_shuffle_ps(inPos, inPos, _MM_SHUFFLE(3, 3, 3, 3)); - - tempPos = _mm_mul_ps(tempPos, temp3); - tempNrm = _mm_mul_ps(tempNrm, temp3); - - inPos = _mm_load_ps(outElem->_vert); //< load position for accumulation - inNrm = _mm_load_ps(outElem->_normal); //< load normal for accumulation - - // accumulate with previous values - tempNrm = _mm_add_ps(tempNrm, inNrm); - tempPos = _mm_add_ps(tempPos, inPos); - - _mm_store_ps(outElem->_vert, tempPos); //< output position - _mm_store_ps(outElem->_normal, tempNrm); //< output normal - } -} - #endif // TORQUE_CPU_X86 \ No newline at end of file diff --git a/Engine/source/ts/loader/appMesh.cpp b/Engine/source/ts/loader/appMesh.cpp index 8db105689..9d44be6cf 100644 --- a/Engine/source/ts/loader/appMesh.cpp +++ b/Engine/source/ts/loader/appMesh.cpp @@ -157,6 +157,7 @@ TSMesh* AppMesh::constructTSMesh() // Finish initializing the shape tsmesh->setFlags(flags); + tsmesh->updateMeshFlags(); tsmesh->computeBounds(); tsmesh->numFrames = numFrames; tsmesh->numMatFrames = numMatFrames; diff --git a/Engine/source/ts/loader/tsShapeLoader.cpp b/Engine/source/ts/loader/tsShapeLoader.cpp index 62c4dfb36..61c183d27 100644 --- a/Engine/source/ts/loader/tsShapeLoader.cpp +++ b/Engine/source/ts/loader/tsShapeLoader.cpp @@ -1229,6 +1229,7 @@ void TSShapeLoader::install() shape->tubeRadius = shape->radius; shape->init(); + shape->finalizeEditable(); } void TSShapeLoader::computeBounds(Box3F& bounds) diff --git a/Engine/source/ts/tsCollision.cpp b/Engine/source/ts/tsCollision.cpp index 8ab250509..1d04b409e 100644 --- a/Engine/source/ts/tsCollision.cpp +++ b/Engine/source/ts/tsCollision.cpp @@ -1459,10 +1459,17 @@ void TSMesh::prepOpcodeCollision() AssertFatal( (curIts - its) == mi->GetNbTriangles(), "Triangle count mismatch!" ); for( S32 i = 0; i < mi->GetNbVertices(); i++ ) + { if( mVertexData.isReady() ) - pts[i].Set( mVertexData[i].vert().x, mVertexData[i].vert().y, mVertexData[i].vert().z ); + { + const __TSMeshVertexBase &vertData = mVertexData.getBase(i); + pts[i].Set( vertData.vert().x, vertData.vert().y, vertData.vert().z ); + } else + { pts[i].Set( verts[i].x, verts[i].y, verts[i].z ); + } + } mi->SetPointers( its, pts ); diff --git a/Engine/source/ts/tsMesh.cpp b/Engine/source/ts/tsMesh.cpp index 47a60124d..39ccc72b9 100644 --- a/Engine/source/ts/tsMesh.cpp +++ b/Engine/source/ts/tsMesh.cpp @@ -78,6 +78,8 @@ Vector TSSkinMesh::smBoneIndexList; Vector TSSkinMesh::smWeightList; Vector TSSkinMesh::smNodeIndexList; +bool TSSkinMesh::smDebugSkinVerts = false; + Vector gNormalStore; bool TSMesh::smUseTriangles = false; // convert all primitives to triangle lists on load @@ -88,6 +90,7 @@ bool TSMesh::smUseEncodedNormals = false; const F32 TSMesh::VISIBILITY_EPSILON = 0.0001f; S32 TSMesh::smMaxInstancingVerts = 200; +MatrixF TSMesh::smDummyNodeTransform(1); // quick function to force object to face camera -- currently throws out roll :( void tsForceFaceCamera( MatrixF *mat, const Point3F *objScale ) @@ -109,13 +112,9 @@ void tsForceFaceCamera( MatrixF *mat, const Point3F *objScale ) // TSMesh render methods //----------------------------------------------------- -void TSMesh::render( TSVertexBufferHandle &instanceVB, GFXPrimitiveBufferHandle &instancePB ) +void TSMesh::render( TSVertexBufferHandle &instanceVB ) { - // A TSMesh never uses the instanceVB. - TORQUE_UNUSED( instanceVB ); - TORQUE_UNUSED( instancePB ); - - innerRender( mVB, mPB ); + innerRender(instanceVB, mPB); } void TSMesh::innerRender( TSVertexBufferHandle &vb, GFXPrimitiveBufferHandle &pb ) @@ -136,19 +135,17 @@ void TSMesh::render( TSMaterialList *materials, bool isSkinDirty, const Vector &transforms, TSVertexBufferHandle &vertexBuffer, - GFXPrimitiveBufferHandle &primitiveBuffer ) + const char *meshName) { // These are only used by TSSkinMesh. TORQUE_UNUSED( isSkinDirty ); TORQUE_UNUSED( transforms ); - TORQUE_UNUSED( vertexBuffer ); - TORQUE_UNUSED( primitiveBuffer ); // Pass our shared VB. - innerRender( materials, rdata, mVB, mPB ); + innerRender(materials, rdata, vertexBuffer, mPB, meshName); } -void TSMesh::innerRender( TSMaterialList *materials, const TSRenderState &rdata, TSVertexBufferHandle &vb, GFXPrimitiveBufferHandle &pb ) +void TSMesh::innerRender( TSMaterialList *materials, const TSRenderState &rdata, TSVertexBufferHandle &vb, GFXPrimitiveBufferHandle &pb, const char *meshName ) { PROFILE_SCOPE( TSMesh_InnerRender ); @@ -164,6 +161,9 @@ void TSMesh::innerRender( TSMaterialList *materials, const TSRenderState &rdata, MeshRenderInst *coreRI = renderPass->allocInst(); coreRI->type = RenderPassManager::RIT_Mesh; +#ifdef TORQUE_ENABLE_GFXDEBUGEVENTS + coreRI->meshName = meshName; +#endif // Pass accumulation texture along. coreRI->accuTex = rdata.getAccuTex(); @@ -213,6 +213,16 @@ void TSMesh::innerRender( TSMaterialList *materials, const TSRenderState &rdata, coreRI->visibility = meshVisibility; coreRI->cubemap = rdata.getCubemap(); + if ( getMeshType() == TSMesh::SkinMeshType ) + { + rdata.getNodeTransforms(&coreRI->mNodeTransforms, &coreRI->mNodeTransformCount); + } + else + { + coreRI->mNodeTransforms = &TSMesh::smDummyNodeTransform; + coreRI->mNodeTransformCount = 1; + } + // NOTICE: SFXBB is removed and refraction is disabled! //coreRI->backBuffTex = GFX->getSfxBackBuffer(); @@ -267,7 +277,7 @@ void TSMesh::innerRender( TSMaterialList *materials, const TSRenderState &rdata, ri->matInst = matInst; ri->defaultKey = matInst->getStateHint(); - ri->primBuffIndex = i; + ri->primBuffIndex = mPrimBufferOffset + i; // Translucent materials need the translucent type. if ( matInst->getMaterial()->isTranslucent() ) @@ -301,6 +311,7 @@ const Point3F * TSMesh::getNormals( S32 firstVert ) bool TSMesh::buildPolyList( S32 frame, AbstractPolyList *polyList, U32 &surfaceKey, TSMaterialList *materials ) { S32 firstVert = vertsPerFrame * frame, i, base = 0; + bool hasTVert2 = getHasTVert2(); // add the verts... if ( vertsPerFrame ) @@ -315,21 +326,21 @@ bool TSMesh::buildPolyList( S32 frame, AbstractPolyList *polyList, U32 &surfaceK { // Don't use vertex() method as we want to retain the original indices OptimizedPolyList::VertIndex vert; - vert.vertIdx = opList->insertPoint( mVertexData[ i + firstVert ].vert() ); - vert.normalIdx = opList->insertNormal( mVertexData[ i + firstVert ].normal() ); - vert.uv0Idx = opList->insertUV0( mVertexData[ i + firstVert ].tvert() ); - if ( mHasTVert2 ) - vert.uv1Idx = opList->insertUV1( mVertexData[ i + firstVert ].tvert2() ); + vert.vertIdx = opList->insertPoint( mVertexData.getBase( i + firstVert ).vert() ); + vert.normalIdx = opList->insertNormal( mVertexData.getBase( i + firstVert ).normal() ); + vert.uv0Idx = opList->insertUV0( mVertexData.getBase( i + firstVert ).tvert() ); + if ( hasTVert2 ) + vert.uv1Idx = opList->insertUV1( mVertexData.getColor( i + firstVert ).tvert2() ); opList->mVertexList.push_back( vert ); } } else { - base = polyList->addPointAndNormal( mVertexData[firstVert].vert(), mVertexData[firstVert].normal() ); + base = polyList->addPointAndNormal( mVertexData.getBase( firstVert ).vert(), mVertexData.getBase( firstVert ).normal() ); for ( i = 1; i < vertsPerFrame; i++ ) { - polyList->addPointAndNormal( mVertexData[ i + firstVert ].vert(), mVertexData[ i + firstVert ].normal() ); + polyList->addPointAndNormal( mVertexData.getBase( i + firstVert ).vert(), mVertexData.getBase( i + firstVert ).normal() ); } } } @@ -346,7 +357,7 @@ bool TSMesh::buildPolyList( S32 frame, AbstractPolyList *polyList, U32 &surfaceK vert.vertIdx = opList->insertPoint( verts[ i + firstVert ] ); vert.normalIdx = opList->insertNormal( norms[ i + firstVert ] ); vert.uv0Idx = opList->insertUV0( tverts[ i + firstVert ] ); - if ( mHasTVert2 ) + if ( hasTVert2 ) vert.uv1Idx = opList->insertUV1( tverts2[ i + firstVert ] ); opList->mVertexList.push_back( vert ); @@ -427,7 +438,7 @@ bool TSMesh::getFeatures( S32 frame, const MatrixF& mat, const VectorF&, ConvexF for ( i = 0; i < vertsPerFrame; i++ ) { cf->mVertexList.increment(); - mat.mulP( mVertexData[firstVert + i].vert(), &cf->mVertexList.last() ); + mat.mulP( mVertexData.getBase(firstVert + i).vert(), &cf->mVertexList.last() ); } // add the polys... @@ -590,7 +601,7 @@ void TSMesh::support( S32 frame, const Point3F &v, F32 *currMaxDP, Point3F *curr S32 firstVert = vertsPerFrame * frame; m_point3F_bulk_dot( &v.x, - &mVertexData[firstVert].vert().x, + &mVertexData.getBase(firstVert).vert().x, vertsPerFrame, mVertexData.vertSize(), pDots ); @@ -612,7 +623,7 @@ void TSMesh::support( S32 frame, const Point3F &v, F32 *currMaxDP, Point3F *curr if ( index != -1 ) { *currMaxDP = localdp; - *currSupport = mVertexData[index + firstVert].vert(); + *currSupport = mVertexData.getBase(index + firstVert).vert(); } } @@ -800,8 +811,8 @@ bool TSMesh::castRayRendered( S32 frame, const Point3F & start, const Point3F & F32 cur_t = 0; Point2F b; - if(castRayTriangle(start, dir, mVertexData[firstVert + idx0].vert(), - mVertexData[firstVert + idx1].vert(), mVertexData[firstVert + idx2].vert(), cur_t, b)) + if(castRayTriangle(start, dir, mVertexData.getBase(firstVert + idx0).vert(), + mVertexData.getBase(firstVert + idx1).vert(), mVertexData.getBase(firstVert + idx2).vert(), cur_t, b)) { if(cur_t < best_t) { @@ -834,8 +845,8 @@ bool TSMesh::castRayRendered( S32 frame, const Point3F & start, const Point3F & F32 cur_t = 0; Point2F b; - if(castRayTriangle(start, dir, mVertexData[firstVert + idx0].vert(), - mVertexData[firstVert + idx1].vert(), mVertexData[firstVert + idx2].vert(), cur_t, b)) + if(castRayTriangle(start, dir, mVertexData.getBase(firstVert + idx0).vert(), + mVertexData.getBase(firstVert + idx1).vert(), mVertexData.getBase(firstVert + idx2).vert(), cur_t, b)) { if(cur_t < best_t) { @@ -857,13 +868,13 @@ bool TSMesh::castRayRendered( S32 frame, const Point3F & start, const Point3F & rayInfo->t = best_t; Point3F normal; - mCross(mVertexData[bestIdx2].vert()-mVertexData[bestIdx0].vert(),mVertexData[bestIdx1].vert()-mVertexData[bestIdx0].vert(),&normal); + mCross(mVertexData.getBase(bestIdx2).vert()-mVertexData.getBase(bestIdx0).vert(),mVertexData.getBase(bestIdx1).vert()-mVertexData.getBase(bestIdx0).vert(),&normal); if ( mDot( normal, normal ) < 0.001f ) { - mCross( mVertexData[bestIdx0].vert() - mVertexData[bestIdx1].vert(), mVertexData[bestIdx2].vert() - mVertexData[bestIdx1].vert(), &normal ); + mCross( mVertexData.getBase(bestIdx0).vert() - mVertexData.getBase(bestIdx1).vert(), mVertexData.getBase(bestIdx2).vert() - mVertexData.getBase(bestIdx1).vert(), &normal ); if ( mDot( normal, normal ) < 0.001f ) { - mCross( mVertexData[bestIdx1].vert() - mVertexData[bestIdx2].vert(), mVertexData[bestIdx0].vert() - mVertexData[bestIdx2].vert(), &normal ); + mCross( mVertexData.getBase(bestIdx1).vert() - mVertexData.getBase(bestIdx2).vert(), mVertexData.getBase(bestIdx0).vert() - mVertexData.getBase(bestIdx2).vert(), &normal ); } } normal.normalize(); @@ -890,9 +901,15 @@ bool TSMesh::addToHull( U32 idx0, U32 idx1, U32 idx2 ) // ways and take the one that gives us the largest vector before we // normalize. Point3F normal1, normal2, normal3; - mCross(mVertexData[idx2].vert()-mVertexData[idx0].vert(),mVertexData[idx1].vert()-mVertexData[idx0].vert(),&normal1); - mCross(mVertexData[idx0].vert()-mVertexData[idx1].vert(),mVertexData[idx2].vert()-mVertexData[idx1].vert(),&normal2); - mCross(mVertexData[idx1].vert()-mVertexData[idx2].vert(),mVertexData[idx0].vert()-mVertexData[idx2].vert(),&normal3); + + const Point3F& vertex0Data = mVertexData.getBase(idx0).vert(); + const Point3F& vertex1Data = mVertexData.getBase(idx1).vert(); + const Point3F& vertex2Data = mVertexData.getBase(idx2).vert(); + + mCross(vertex2Data-vertex0Data,vertex1Data-vertex0Data,&normal1); + mCross(vertex0Data-vertex1Data,vertex2Data-vertex1Data,&normal2); + mCross(vertex1Data-vertex2Data,vertex0Data-vertex2Data,&normal3); + Point3F normal = normal1; F32 greatestMagSquared = mDot(normal1, normal1); F32 magSquared = mDot(normal2, normal2); @@ -911,7 +928,7 @@ bool TSMesh::addToHull( U32 idx0, U32 idx1, U32 idx2 ) return false; normal.normalize(); - F32 k = mDot( normal, mVertexData[idx0].vert() ); + F32 k = mDot( normal, mVertexData.getBase(idx0).vert() ); for ( S32 i = 0; i < planeNormals.size(); i++ ) { if ( mDot( planeNormals[i], normal ) > 0.99f && mFabs( k-planeConstants[i] ) < 0.01f ) @@ -976,7 +993,7 @@ bool TSMesh::buildConvexHull() // make sure all the verts on this frame are inside all the planes for ( i = 0; i < vertsPerFrame; i++ ) for ( j = firstPlane; j < planeNormals.size(); j++ ) - if ( mDot( mVertexData[firstVert + i].vert(), planeNormals[j] ) - planeConstants[j] < 0.01 ) // .01 == a little slack + if ( mDot( mVertexData.getBase(firstVert + i).vert(), planeNormals[j] ) - planeConstants[j] < 0.01 ) // .01 == a little slack error = true; if ( frame == 0 ) @@ -1034,16 +1051,18 @@ void TSMesh::computeBounds( const MatrixF &transform, Box3F &bounds, S32 frame, S32 stride = 0; S32 numVerts = 0; - if(mVertexData.isReady()) + AssertFatal(!mVertexData.isReady() || (mVertexData.isReady() && mNumVerts == mVertexData.size() && mNumVerts == vertsPerFrame), "vertex number mismatch"); + + if(verts.size() == 0 && mVertexData.isReady() && mVertexData.size() > 0) { - baseVert = &mVertexData[0].vert(); + baseVert = &mVertexData.getBase(0).vert(); stride = mVertexData.vertSize(); if ( frame < 0 ) numVerts = mNumVerts; else { - baseVert = &mVertexData[frame * vertsPerFrame].vert(); + baseVert = &mVertexData.getBase(frame * vertsPerFrame).vert(); numVerts = vertsPerFrame; } } @@ -1157,12 +1176,14 @@ TSMesh::TSMesh() : meshType( StandardMeshType ) mOpTris = NULL; mOpPoints = NULL; - mDynamic = false; mVisibility = 1.0f; - mHasTVert2 = false; - mHasColor = false; + mNumVerts = 0; + mVertSize = 0; + mVertOffset = 0; + + parentMeshObject = NULL; } //----------------------------------------------------- @@ -1183,151 +1204,134 @@ TSMesh::~TSMesh() // TSSkinMesh methods //----------------------------------------------------- -void TSSkinMesh::updateSkin( const Vector &transforms, TSVertexBufferHandle &instanceVB, GFXPrimitiveBufferHandle &instancePB ) +void TSSkinMesh::updateSkinBuffer( const Vector &transforms, U8* buffer ) { - PROFILE_SCOPE( TSSkinMesh_UpdateSkin ); + PROFILE_SCOPE(TSSkinMesh_UpdateSkinBuffer); - AssertFatal(batchDataInitialized, "Batch data not initialized. Call createBatchData() before any skin update is called."); + AssertFatal(batchData.initialized, "Batch data not initialized. Call createSkinBatchData() before any skin update is called."); - // set arrays -#if defined(TORQUE_MAX_LIB) - verts.setSize(batchData.initialVerts.size()); - norms.setSize(batchData.initialNorms.size()); -#else - if ( !batchDataInitialized && encodedNorms.size() ) - { - // we co-opt responsibility for updating encoded normals from mesh - gNormalStore.setSize( vertsPerFrame ); - for ( S32 i = 0; i < vertsPerFrame; i++ ) - gNormalStore[i] = decodeNormal( encodedNorms[i] ); + if (TSShape::smUseHardwareSkinning || mNumVerts == 0) + return; - batchData.initialNorms.set( gNormalStore.address(), vertsPerFrame ); - } -#endif + const MatrixF *matrices = NULL; static Vector sBoneTransforms; - sBoneTransforms.setSize( batchData.nodeIndex.size() ); + sBoneTransforms.setSize(batchData.nodeIndex.size()); // set up bone transforms PROFILE_START(TSSkinMesh_UpdateTransforms); - for( S32 i=0; i::const_iterator itr = batchData.vertexBatchOperations.begin(); - itr != batchData.vertexBatchOperations.end(); itr++ ) - { - const BatchData::BatchedVertex &curVert = *itr; - - skinnedVert.zero(); - skinnedNorm.zero(); - - for( S32 tOp = 0; tOp < curVert.transformCount; tOp++ ) - { - const BatchData::TransformOp &transformOp = curVert.transform[tOp]; - - const MatrixF& deltaTransform = matrices[transformOp.transformIndex]; - - deltaTransform.mulP( inVerts[curVert.vertexIndex], &srcVtx ); - skinnedVert += ( srcVtx * transformOp.weight ); - - deltaTransform.mulV( inNorms[curVert.vertexIndex], &srcNrm ); - skinnedNorm += srcNrm * transformOp.weight; - } - - // Assign results - __TSMeshVertexBase &dest = mVertexData[curVert.vertexIndex]; - dest.vert(skinnedVert); - dest.normal(skinnedNorm); - } - } - else // Batch by transform - { - U8 *outPtr = reinterpret_cast(mVertexData.address()); - dsize_t outStride = mVertexData.vertSize(); - -#if defined(USE_MEM_VERTEX_BUFFERS) - // Initialize it if NULL. - // Skinning includes readbacks from memory (argh) so don't allocate with PAGE_WRITECOMBINE - if( instanceVB.isNull() ) - instanceVB.set( GFX, outStride, mVertexFormat, mNumVerts, GFXBufferTypeDynamic ); - - // Grow if needed - if( instanceVB.getPointer()->mNumVerts < mNumVerts ) - instanceVB.resize( mNumVerts ); - - // Lock, and skin directly into the final memory destination - outPtr = (U8 *)instanceVB.lock(); - if(!outPtr) return; -#endif - // Set position/normal to zero so we can accumulate - zero_vert_normal_bulk(mNumVerts, outPtr, outStride); - - // Iterate over transforms, and perform batch transform x skin_vert - for(Vector::const_iterator itr = batchData.transformKeys.begin(); - itr != batchData.transformKeys.end(); itr++) - { - const S32 boneXfmIdx = *itr; - const BatchData::BatchedTransform &curTransform = *batchData.transformBatchOperations.retreive(boneXfmIdx); - const MatrixF &curBoneMat = matrices[boneXfmIdx]; - const S32 numVerts = curTransform.numElements; - - // Bulk transform points/normals by this transform - m_matF_x_BatchedVertWeightList(curBoneMat, numVerts, curTransform.alignedMem, - outPtr, outStride); - } -#if defined(USE_MEM_VERTEX_BUFFERS) - instanceVB.unlock(); -#endif - } -} - -S32 QSORT_CALLBACK _sort_BatchedVertWeight( const void *a, const void *b ) -{ - // Sort by vertex index - const TSSkinMesh::BatchData::BatchedVertWeight &_a = *reinterpret_cast(a); - const TSSkinMesh::BatchData::BatchedVertWeight &_b = *reinterpret_cast(b); - return ( _a.vidx - _b.vidx ); -} - -// Batch by vertex is useful to emulate the old skinning, or to build batch data -// sutable for GPU skinning. -//#define _BATCH_BY_VERTEX - -void TSSkinMesh::createBatchData() -{ - if(batchDataInitialized) + U8 *dest = buffer + mVertOffset; + if (!dest) return; - batchDataInitialized = true; + Point3F srcVtx, srcNrm; + + AssertFatal(batchData.vertexBatchOperations.size() == batchData.initialVerts.size(), "Assumption failed!"); + + register Point3F skinnedVert; + register Point3F skinnedNorm; + + for (Vector::const_iterator itr = batchData.vertexBatchOperations.begin(); + itr != batchData.vertexBatchOperations.end(); itr++) + { + const BatchData::BatchedVertex &curVert = *itr; + + skinnedVert.zero(); + skinnedNorm.zero(); + + for (S32 tOp = 0; tOp < curVert.transformCount; tOp++) + { + const BatchData::TransformOp &transformOp = curVert.transform[tOp]; + + const MatrixF& deltaTransform = matrices[transformOp.transformIndex]; + + deltaTransform.mulP(inVerts[curVert.vertexIndex], &srcVtx); + skinnedVert += (srcVtx * transformOp.weight); + + deltaTransform.mulV(inNorms[curVert.vertexIndex], &srcNrm); + skinnedNorm += srcNrm * transformOp.weight; + } + + // Assign results + __TSMeshVertexBase *dvert = (__TSMeshVertexBase*)(dest + (mVertSize * curVert.vertexIndex)); + dvert->vert(skinnedVert); + dvert->normal(skinnedNorm); + } +} + +void TSSkinMesh::updateSkinBones( const Vector &transforms, Vector& destTransforms ) +{ + // Update transforms for current mesh + destTransforms.setSize(batchData.nodeIndex.size()); + + for (int i = 0; i= transforms.size()) + continue; // jamesu - ignore obviously invalid data + destTransforms[i].mul(transforms[node], batchData.initialTransforms[i]); + } +} + +void TSSkinMesh::createSkinBatchData() +{ + if(batchData.initialized) + return; + + batchData.initialized = true; S32 * curVtx = vertexIndex.begin(); S32 * curBone = boneIndex.begin(); F32 * curWeight = weight.begin(); const S32 * endVtx = vertexIndex.end(); + AssertFatal(batchData.nodeIndex.size() <= TSShape::smMaxSkinBones, "Too many bones are here!!!"); + // Temp vector to build batch operations Vector batchOperations; bool issuedWeightWarning = false; + if (mVertexData.isReady()) + { + batchData.initialVerts.setSize(mNumVerts); + batchData.initialNorms.setSize(mNumVerts); + + // Fill arrays + for (U32 i = 0; i < mNumVerts; i++) + { + const __TSMeshVertexBase &cv = mVertexData.getBase(i); + batchData.initialVerts[i] = cv.vert(); + batchData.initialNorms[i] = cv.normal(); + } + + addWeightsFromVertexBuffer(); + + curVtx = vertexIndex.begin(); + curBone = boneIndex.begin(); + curWeight = weight.begin(); + endVtx = vertexIndex.end(); + } + else + { + batchData.initialNorms = norms; + batchData.initialVerts = verts; + } + // Build the batch operations while( curVtx != endVtx ) { @@ -1413,75 +1417,91 @@ void TSSkinMesh::createBatchData() } } -#ifdef _BATCH_BY_VERTEX - // Copy data to member, and be done batchData.vertexBatchOperations.set(batchOperations.address(), batchOperations.size()); - // Convert to batch-by-transform, which is better for CPU skinning, - // where-as GPU skinning would data for batch-by-vertex operation -#else - // Iterate the batch-by-vertex, and populate the batch-by-transform structs - for( Vector::const_iterator itr = batchOperations.begin(); - itr != batchOperations.end(); itr++ ) + U32 maxValue = 0; + for (U32 i = 0; i_tmpVec = new Vector; - batchData.transformKeys.push_back(transformOp.transformIndex); - } - - bt->_tmpVec->increment(); - - BatchData::BatchedVertWeight& tempLast = bt->_tmpVec->last(); - tempLast.vert = batchData.initialVerts[curTransform.vertexIndex]; - tempLast.normal = batchData.initialNorms[curTransform.vertexIndex]; - tempLast.weight = transformOp.weight; - tempLast.vidx = curTransform.vertexIndex; - } + maxValue = batchData.vertexBatchOperations[i].transformCount > maxValue ? batchData.vertexBatchOperations[i].transformCount : maxValue; } - - // Now iterate the resulting operations and convert the vectors to aligned - // memory locations - const S32 numBatchOps = batchData.transformKeys.size(); - for(S32 i = 0; i < numBatchOps; i++) - { - BatchData::BatchedTransform &curTransform = *batchData.transformBatchOperations.retreive(batchData.transformKeys[i]); - const S32 numVerts = curTransform._tmpVec->size(); - - // Allocate a chunk of aligned memory and copy in values - curTransform.numElements = numVerts; - curTransform.alignedMem = reinterpret_cast(dMalloc_aligned(sizeof(BatchData::BatchedVertWeight) * numVerts, 16)); - AssertFatal(curTransform.alignedMem, "Aligned malloc failed! Debug!"); - constructArrayInPlace(curTransform.alignedMem, numVerts); - dMemcpy(curTransform.alignedMem, curTransform._tmpVec->address(), numVerts * sizeof(BatchData::BatchedVertWeight)); - - // Now free the vector memory - delete curTransform._tmpVec; - curTransform._tmpVec = NULL; - } - - // Now sort the batch data so that the skin function writes close to linear output - for(S32 i = 0; i < numBatchOps; i++) - { - BatchData::BatchedTransform &curTransform = *batchData.transformBatchOperations.retreive(batchData.transformKeys[i]); - dQsort(curTransform.alignedMem, curTransform.numElements, sizeof(BatchData::BatchedVertWeight), _sort_BatchedVertWeight); - } -#endif + maxBones = maxValue; } -void TSSkinMesh::render( TSVertexBufferHandle &instanceVB, GFXPrimitiveBufferHandle &instancePB ) +void TSSkinMesh::setupVertexTransforms() { - innerRender( instanceVB, instancePB ); + AssertFatal(mVertexData.vertSize() == mVertSize, "vert size mismatch"); + + // Generate the bone transforms for the verts + for( Vector::const_iterator itr = batchData.vertexBatchOperations.begin(); + itr != batchData.vertexBatchOperations.end(); itr++ ) + { + const BatchData::BatchedVertex &curTransform = *itr; + S32 i=0; + S32 j=0; + S32 transformsLeft = curTransform.transformCount; + + // Set weights and indices in batches of 4 + for( i = 0, j = 0; i < curTransform.transformCount; i += 4, j += 1 ) + { + __TSMeshVertex_BoneData &v = mVertexData.getBone(curTransform.vertexIndex, j); + const BatchData::TransformOp &transformOp = curTransform.transform[i]; + S32 vertsSet = transformsLeft > 4 ? 4 : transformsLeft; + + __TSMeshIndex_List indices; + Point4F weights; + dMemset(&indices, '\0', sizeof(indices)); + dMemset(&weights, '\0', sizeof(weights)); + + switch (vertsSet) + { + case 1: + indices.x = curTransform.transform[i+0].transformIndex; + weights.x = curTransform.transform[i+0].weight; + break; + case 2: + indices.x = curTransform.transform[i+0].transformIndex; + weights.x = curTransform.transform[i+0].weight; + indices.y = curTransform.transform[i+1].transformIndex; + weights.y = curTransform.transform[i+1].weight; + break; + case 3: + indices.x = curTransform.transform[i+0].transformIndex; + weights.x = curTransform.transform[i+0].weight; + indices.y = curTransform.transform[i+1].transformIndex; + weights.y = curTransform.transform[i+1].weight; + indices.z = curTransform.transform[i+2].transformIndex; + weights.z = curTransform.transform[i+2].weight; + break; + case 4: + indices.x = curTransform.transform[i+0].transformIndex; + weights.x = curTransform.transform[i+0].weight; + indices.y = curTransform.transform[i+1].transformIndex; + weights.y = curTransform.transform[i+1].weight; + indices.z = curTransform.transform[i+2].transformIndex; + weights.z = curTransform.transform[i+2].weight; + indices.w = curTransform.transform[i+3].transformIndex; + weights.w = curTransform.transform[i+3].weight; + break; + case 0: + default: + break; + } + + v.index(indices); + v.weight(weights); + transformsLeft -= 4; + } + } +} + +U32 TSSkinMesh::getMaxBonesPerVert() +{ + return maxBones >= 0 ? maxBones : 0; +} + +void TSSkinMesh::render( TSVertexBufferHandle &instanceVB ) +{ + innerRender(instanceVB, mPB); } void TSSkinMesh::render( TSMaterialList *materials, @@ -1489,50 +1509,23 @@ void TSSkinMesh::render( TSMaterialList *materials, bool isSkinDirty, const Vector &transforms, TSVertexBufferHandle &vertexBuffer, - GFXPrimitiveBufferHandle &primitiveBuffer ) + const char *meshName ) { PROFILE_SCOPE(TSSkinMesh_render); - if( mNumVerts == 0 ) + if (mNumVerts == 0) return; - // Initialize the vertex data if it needs it - if(!mVertexData.isReady() ) - _convertToAlignedMeshData(mVertexData, batchData.initialVerts, batchData.initialNorms); + // verify stuff first AssertFatal(mVertexData.size() == mNumVerts, "Vert # mismatch"); - - // Initialize the skin batch if that isn't ready - if(!batchDataInitialized) - createBatchData(); - - const bool vertsChanged = vertexBuffer.isNull() || vertexBuffer->mNumVerts != mNumVerts; - const bool primsChanged = primitiveBuffer.isNull() || primitiveBuffer->mIndexCount != indices.size(); - - if ( primsChanged || vertsChanged || isSkinDirty ) - { - // Perform skinning - updateSkin( transforms, vertexBuffer, primitiveBuffer ); - - // Update GFX vertex buffer - _createVBIB( vertexBuffer, primitiveBuffer ); - } + AssertFatal((TSShape::smUseHardwareSkinning && vertexBuffer == mVB) || (!TSShape::smUseHardwareSkinning), "Vertex buffer mismatch"); // render... - innerRender( materials, rdata, vertexBuffer, primitiveBuffer ); + innerRender(materials, rdata, vertexBuffer, mPB, meshName); } bool TSSkinMesh::buildPolyList( S32 frame, AbstractPolyList *polyList, U32 &surfaceKey, TSMaterialList *materials ) { - // UpdateSkin() here may not be needed... - // we don't capture skinned - // verts in the polylist. - - // update verts and normals... - //if( !smGlowPass && !smRefractPass ) - // updateSkin(); - - // render... - //Parent::buildPolyList( frame,polyList,surfaceKey, materials ); return false; } @@ -1556,10 +1549,15 @@ void TSSkinMesh::computeBounds( const MatrixF &transform, Box3F &bounds, S32 fra { TORQUE_UNUSED(frame); - if (frame < 0) + if (verts.size() != 0) { // Use unskinned verts - TSMesh::computeBounds( batchData.initialVerts.address(), batchData.initialVerts.size(), sizeof(Point3F), transform, bounds, center, radius ); + TSMesh::computeBounds( verts.address(), verts.size(), sizeof(Point3F), transform, bounds, center, radius ); + } + else if (frame <= 0 && batchData.initialVerts.size() > 0) + { + // Use unskinned verts + TSMesh::computeBounds(batchData.initialVerts.address(), batchData.initialVerts.size(), sizeof(Point3F), transform, bounds, center, radius); } else { @@ -2368,107 +2366,50 @@ S8 * TSMesh::getSharedData8( S32 parentMesh, S32 size, S8 **source, bool skip ) return ptr; } -void TSMesh::createVBIB() +void TSMesh::dumpPrimitives(U32 startVertex, U32 startIndex, GFXPrimitive *piArray, U16* ibIndices) { - AssertFatal( getMeshType() != SkinMeshType, "TSMesh::createVBIB() - Invalid call for skinned mesh type!" ); - _createVBIB( mVB, mPB ); -} + // go through and create PrimitiveInfo array + GFXPrimitive pInfo; -void TSMesh::_createVBIB( TSVertexBufferHandle &vb, GFXPrimitiveBufferHandle &pb ) -{ - AssertFatal(mVertexData.isReady(), "Call convertToAlignedMeshData() before calling _createVBIB()"); - - if ( mNumVerts == 0 || !GFXDevice::devicePresent() ) - return; - - PROFILE_SCOPE( TSMesh_CreateVBIB ); - - // Number of verts can change in LOD skinned mesh - const bool vertsChanged = ( vb && vb->mNumVerts < mNumVerts ); - -#if defined(USE_MEM_VERTEX_BUFFERS) - if(!mDynamic) + U32 primitivesSize = primitives.size(); + for (U32 i = 0; i < primitivesSize; i++) { -#endif - // Create the vertex buffer - if( vertsChanged || vb == NULL ) - vb.set( GFX, mVertSize, mVertexFormat, mNumVerts, mDynamic ? -#if defined(TORQUE_OS_XENON) - // Skinned meshes still will occasionally re-skin more than once per frame. - // This cannot happen on the Xbox360. Until this issue is resolved, use - // type volatile instead. [1/27/2010 Pat] - GFXBufferTypeVolatile : GFXBufferTypeStatic ); -#else - GFXBufferTypeDynamic : GFXBufferTypeStatic ); -#endif + const TSDrawPrimitive & draw = primitives[i]; - // Copy from aligned memory right into GPU memory - U8 *vertData = (U8*)vb.lock(); - if(!vertData) return; -#if defined(TORQUE_OS_XENON) - XMemCpyStreaming_WriteCombined( vertData, mVertexData.address(), mVertexData.mem_size() ); -#else - dMemcpy( vertData, mVertexData.address(), mVertexData.mem_size() ); -#endif - vb.unlock(); -#if defined(USE_MEM_VERTEX_BUFFERS) - } -#endif + GFXPrimitiveType drawType = getDrawType(draw.matIndex >> 30); - const bool primsChanged = ( pb.isValid() && pb->mIndexCount != indices.size() ); - if( primsChanged || pb.isNull() ) - { - // go through and create PrimitiveInfo array - Vector piArray; - GFXPrimitive pInfo; - - U32 primitivesSize = primitives.size(); - for ( U32 i = 0; i < primitivesSize; i++ ) + switch (drawType) { - const TSDrawPrimitive & draw = primitives[i]; + case GFXTriangleList: + pInfo.type = drawType; + pInfo.numPrimitives = draw.numElements / 3; + pInfo.startIndex = startIndex + draw.start; + // Use the first index to determine which 16-bit address space we are operating in + pInfo.startVertex = (indices[draw.start] & 0xFFFF0000); // TODO: figure out a good solution for this + pInfo.minIndex = 0; // minIndex are zero based index relative to startVertex. See @GFXDevice + pInfo.numVertices = getMin((U32)0x10000, mNumVerts - pInfo.startVertex); + pInfo.startVertex += startVertex; + break; - GFXPrimitiveType drawType = getDrawType( draw.matIndex >> 30 ); + case GFXTriangleStrip: + pInfo.type = drawType; + pInfo.numPrimitives = draw.numElements - 2; + pInfo.startIndex = startIndex + draw.start; + // Use the first index to determine which 16-bit address space we are operating in + pInfo.startVertex = (indices[draw.start] & 0xFFFF0000); // TODO: figure out a good solution for this + pInfo.minIndex = 0; // minIndex are zero based index relative to startVertex. See @GFXDevice + pInfo.numVertices = getMin((U32)0x10000, mNumVerts - pInfo.startVertex); + pInfo.startVertex += startVertex; + break; - switch( drawType ) - { - case GFXTriangleList: - pInfo.type = drawType; - pInfo.numPrimitives = draw.numElements / 3; - pInfo.startIndex = draw.start; - // Use the first index to determine which 16-bit address space we are operating in - pInfo.startVertex = indices[draw.start] & 0xFFFF0000; - pInfo.minIndex = 0; // minIndex are zero based index relative to startVertex. See @GFXDevice - pInfo.numVertices = getMin((U32)0x10000, mNumVerts - pInfo.startVertex); - break; - - case GFXTriangleStrip: - pInfo.type = drawType; - pInfo.numPrimitives = draw.numElements - 2; - pInfo.startIndex = draw.start; - // Use the first index to determine which 16-bit address space we are operating in - pInfo.startVertex = indices[draw.start] & 0xFFFF0000; - pInfo.minIndex = 0; // minIndex are zero based index relative to startVertex. See @GFXDevice - pInfo.numVertices = getMin((U32)0x10000, mNumVerts - pInfo.startVertex); - break; - - default: - AssertFatal( false, "WTF?!" ); - } - - piArray.push_back( pInfo ); + default: + AssertFatal(false, "WTF?!"); } - pb.set( GFX, indices.size(), piArray.size(), GFXBufferTypeStatic ); - - U16 *ibIndices = NULL; - GFXPrimitive *piInput = NULL; - pb.lock( &ibIndices, &piInput ); - - dCopyArray( ibIndices, indices.address(), indices.size() ); - dMemcpy( piInput, piArray.address(), piArray.size() * sizeof(GFXPrimitive) ); - - pb.unlock(); + *piArray++ = pInfo; } + + dCopyArray(ibIndices, indices.address(), indices.size()); } void TSMesh::assemble( bool skip ) @@ -2482,6 +2423,20 @@ void TSMesh::assemble( bool skip ) tsalloc.get32( (S32*)&mCenter, 3 ); mRadius = (F32)tsalloc.get32(); + if (TSShape::smReadVersion >= 27) + { + // Offsetted + mVertOffset = tsalloc.get32(); + mNumVerts = tsalloc.get32(); + mVertSize = tsalloc.get32(); + } + else + { + mVertOffset = 0; + mNumVerts = 0; + mVertSize = 0; + } + S32 numVerts = tsalloc.get32(); S32 *ptr32 = getSharedData32( parentMesh, 3 * numVerts, (S32**)smVertsList.address(), skip ); verts.set( (Point3F*)ptr32, numVerts ); @@ -2506,7 +2461,7 @@ void TSMesh::assemble( bool skip ) { // we have encoded normals and we want to use them... if ( parentMesh < 0 ) - tsalloc.getPointer32( numVerts * 3 ); // advance past norms, don't use + tsalloc.getPointer32( numVerts * 3 ); // adva nce past norms, don't use norms.set( NULL, 0 ); ptr8 = getSharedData8( parentMesh, numVerts, (S8**)smEncodedNormsList.address(), skip ); @@ -2622,13 +2577,20 @@ void TSMesh::assemble( bool skip ) setFlags( flags ); + // Set color & tvert2 flags if we have an old version + if (TSShape::smReadVersion < 27) + { + if (colors.size() > 0) setFlags(HasColor); + if (tverts2.size() > 0) setFlags(HasTVert2); + mNumVerts = verts.size(); + } + tsalloc.checkGuard(); if ( tsalloc.allocShape32( 0 ) && TSShape::smReadVersion < 19 ) computeBounds(); // only do this if we copied the data... - if(getMeshType() != SkinMeshType) - createTangents(verts, norms); + createTangents(verts, norms); } void TSMesh::disassemble() @@ -2642,68 +2604,76 @@ void TSMesh::disassemble() tsalloc.copyToBuffer32( (S32*)&mCenter, 3 ); tsalloc.set32( (S32)mRadius ); + bool shouldMakeEditable = TSShape::smVersion < 27; + // Re-create the vectors - if(mVertexData.isReady()) + if (shouldMakeEditable) { - verts.setSize(mNumVerts); - tverts.setSize(mNumVerts); - norms.setSize(mNumVerts); + makeEditable(); - if(mHasColor) - colors.setSize(mNumVerts); - if(mHasTVert2) - tverts2.setSize(mNumVerts); - - // Fill arrays - for(U32 i = 0; i < mNumVerts; i++) + // No Offset + if (TSShape::smVersion >= 27) { - const __TSMeshVertexBase &cv = mVertexData[i]; - verts[i] = cv.vert(); - tverts[i] = cv.tvert(); - norms[i] = cv.normal(); - - if(mHasColor) - cv.color().getColor(&colors[i]); - if(mHasTVert2) - tverts2[i] = cv.tvert2(); + tsalloc.set32(0); + tsalloc.set32(0); + tsalloc.set32(0); } } - - // verts... - tsalloc.set32( verts.size() ); - if ( parentMesh < 0 ) - tsalloc.copyToBuffer32( (S32*)verts.address(), 3 * verts.size() ); // if no parent mesh, then save off our verts - - // tverts... - tsalloc.set32( tverts.size() ); - if ( parentMesh < 0 ) - tsalloc.copyToBuffer32( (S32*)tverts.address(), 2 * tverts.size() ); // if no parent mesh, then save off our tverts - - if (TSShape::smVersion > 25) + else { - // tverts2... - tsalloc.set32( tverts2.size() ); - if ( parentMesh < 0 ) - tsalloc.copyToBuffer32( (S32*)tverts2.address(), 2 * tverts2.size() ); // if no parent mesh, then save off our tverts - - // colors - tsalloc.set32( colors.size() ); - if ( parentMesh < 0 ) - tsalloc.copyToBuffer32( (S32*)colors.address(), colors.size() ); // if no parent mesh, then save off our tverts + // Offsetted + tsalloc.set32(mVertOffset); + tsalloc.set32(mNumVerts); + tsalloc.set32(mVertSize); + AssertFatal(mNumVerts >= vertsPerFrame, "invalid mNumVerts"); } - // norms... - if ( parentMesh < 0 ) // if no parent mesh, then save off our norms - tsalloc.copyToBuffer32( (S32*)norms.address(), 3 * norms.size() ); // norms.size()==verts.size() or error... - - // encoded norms... - if ( parentMesh < 0 ) + if (TSShape::smVersion >= 27 && mVertexData.isReady()) { - // if no parent mesh, compute encoded normals and copy over - for ( S32 i = 0; i < norms.size(); i++ ) + // If not editable all arrays are effectively 0. + tsalloc.set32(0); // verts + tsalloc.set32(0); // tverts + tsalloc.set32(0); // tverts2 + tsalloc.set32(0); // colors + } + else + { + // verts... + tsalloc.set32(verts.size()); + if (parentMesh < 0) + tsalloc.copyToBuffer32((S32*)verts.address(), 3 * verts.size()); // if no parent mesh, then save off our verts + + // tverts... + tsalloc.set32(tverts.size()); + if (parentMesh < 0) + tsalloc.copyToBuffer32((S32*)tverts.address(), 2 * tverts.size()); // if no parent mesh, then save off our tverts + + if (TSShape::smVersion > 25) { - U8 normIdx = encodedNorms.size() ? encodedNorms[i] : encodeNormal( norms[i] ); - tsalloc.copyToBuffer8( (S8*)&normIdx, 1 ); + // tverts2... + tsalloc.set32(tverts2.size()); + if (parentMesh < 0) + tsalloc.copyToBuffer32((S32*)tverts2.address(), 2 * tverts2.size()); // if no parent mesh, then save off our tverts + + // colors + tsalloc.set32(colors.size()); + if (parentMesh < 0) + tsalloc.copyToBuffer32((S32*)colors.address(), colors.size()); // if no parent mesh, then save off our tverts + } + + // norms... + if (parentMesh < 0) // if no parent mesh, then save off our norms + tsalloc.copyToBuffer32((S32*)norms.address(), 3 * norms.size()); // norms.size()==verts.size() or error... + + // encoded norms... + if (parentMesh < 0) + { + // if no parent mesh, compute encoded normals and copy over + for (S32 i = 0; i < norms.size(); i++) + { + U8 normIdx = encodedNorms.size() ? encodedNorms[i] : encodeNormal(norms[i]); + tsalloc.copyToBuffer8((S8*)&normIdx, 1); + } } } @@ -2777,41 +2747,62 @@ void TSSkinMesh::assemble( bool skip ) TSMesh::assemble( skip ); - S32 sz = tsalloc.get32(); - S32 numVerts = sz; - S32 * ptr32 = getSharedData32( parentMesh, 3 * numVerts, (S32**)smVertsList.address(), skip ); - batchData.initialVerts.set( (Point3F*)ptr32, sz ); - - S8 * ptr8; - if ( TSShape::smReadVersion>21 && TSMesh::smUseEncodedNormals ) + if (TSShape::smReadVersion >= 27) { - // we have encoded normals and we want to use them... - if ( parentMesh < 0 ) - tsalloc.getPointer32( numVerts * 3 ); // advance past norms, don't use - batchData.initialNorms.set( NULL, 0 ); - - ptr8 = getSharedData8( parentMesh, numVerts, (S8**)smEncodedNormsList.address(), skip ); - encodedNorms.set( ptr8, numVerts ); - // Note: we don't set the encoded normals flag because we handle them in updateSkin and - // hide the fact that we are using them from base class (TSMesh) - } - else if ( TSShape::smReadVersion > 21 ) - { - // we have encoded normals but we don't want to use them... - ptr32 = getSharedData32( parentMesh, 3 * numVerts, (S32**)smNormsList.address(), skip ); - batchData.initialNorms.set( (Point3F*)ptr32, numVerts ); - - if ( parentMesh < 0 ) - tsalloc.getPointer8( numVerts ); // advance past encoded normls, don't use - - encodedNorms.set( NULL, 0 ); + maxBones = tsalloc.get32(); } else { - // no encoded normals... - ptr32 = getSharedData32( parentMesh, 3 * numVerts, (S32**)smNormsList.address(), skip ); - batchData.initialNorms.set( (Point3F*)ptr32, numVerts ); - encodedNorms.set( NULL, 0 ); + maxBones = -1; + } + + S32 sz; + S32 * ptr32; + + if (TSShape::smReadVersion < 27) + { + sz = tsalloc.get32(); + S32 numVerts = sz; + ptr32 = getSharedData32(parentMesh, 3 * numVerts, (S32**)smVertsList.address(), skip); + batchData.initialVerts.set((Point3F*)ptr32, sz); + + S8 * ptr8; + if (TSShape::smReadVersion > 21 && TSMesh::smUseEncodedNormals) + { + // we have encoded normals and we want to use them... + if (parentMesh < 0) + tsalloc.getPointer32(numVerts * 3); // advance past norms, don't use + batchData.initialNorms.set(NULL, 0); + + ptr8 = getSharedData8(parentMesh, numVerts, (S8**)smEncodedNormsList.address(), skip); + encodedNorms.set(ptr8, numVerts); + // Note: we don't set the encoded normals flag because we handle them in updateSkin and + // hide the fact that we are using them from base class (TSMesh) + } + else if (TSShape::smReadVersion > 21) + { + // we have encoded normals but we don't want to use them... + ptr32 = getSharedData32(parentMesh, 3 * numVerts, (S32**)smNormsList.address(), skip); + batchData.initialNorms.set((Point3F*)ptr32, numVerts); + + if (parentMesh < 0) + tsalloc.getPointer8(numVerts); // advance past encoded normls, don't use + + encodedNorms.set(NULL, 0); + } + else + { + // no encoded normals... + ptr32 = getSharedData32(parentMesh, 3 * numVerts, (S32**)smNormsList.address(), skip); + batchData.initialNorms.set((Point3F*)ptr32, numVerts); + encodedNorms.set(NULL, 0); + } + } + else + { + // Set from the mesh data + batchData.initialVerts = verts; + batchData.initialNorms = norms; } sz = tsalloc.get32(); @@ -2834,10 +2825,36 @@ void TSSkinMesh::assemble( bool skip ) tsalloc.checkGuard(); - if ( tsalloc.allocShape32( 0 ) && TSShape::smReadVersion < 19 ) - TSMesh::computeBounds(); // only do this if we copied the data... + if (smDebugSkinVerts && ptr32 != NULL) + { + Con::printf("Loaded skin verts..."); + for (U32 i = 0; i < vertexIndex.size(); i++) + { + Con::printf("vi[%i] == %i", i, vertexIndex[i]); + } + for (U32 i = 0; i < boneIndex.size(); i++) + { + Con::printf("bi[%i] == %i", i, boneIndex[i]); + } + for (U32 i = 0; i < batchData.nodeIndex.size(); i++) + { + Con::printf("ni[%i] == %i", i, batchData.nodeIndex[i]); + } + for (U32 i = 0; i < boneIndex.size(); i++) + { + Con::printf("we[%i] == %f", i, weight[i]); + } - createTangents(batchData.initialVerts, batchData.initialNorms); + if (mNumVerts != 0) + { + AssertFatal(batchData.initialVerts.size() == mNumVerts, "err WTF"); + } + + Con::printf("---"); + } + + if ( tsalloc.allocShape32( 0 ) && TSShape::smReadVersion < 19 ) + TSMesh::computeBounds(); // only do this if we copied the data...c } //----------------------------------------------------------------------------- @@ -2847,20 +2864,29 @@ void TSSkinMesh::disassemble() { TSMesh::disassemble(); - tsalloc.set32( batchData.initialVerts.size() ); - // if we have no parent mesh, then save off our verts & norms - if ( parentMesh < 0 ) + if (TSShape::smVersion >= 27) { - tsalloc.copyToBuffer32( (S32*)batchData.initialVerts.address(), 3 * batchData.initialVerts.size() ); + AssertFatal(maxBones != 0, "Skin mesh with no bones? No way!"); + tsalloc.set32(maxBones); + } - // no longer do this here...let tsmesh handle this - tsalloc.copyToBuffer32( (S32*)batchData.initialNorms.address(), 3 * batchData.initialNorms.size() ); - - // if no parent mesh, compute encoded normals and copy over - for ( S32 i = 0; i < batchData.initialNorms.size(); i++ ) + if (TSShape::smVersion < 27) + { + tsalloc.set32(batchData.initialVerts.size()); + // if we have no parent mesh, then save off our verts & norms + if (parentMesh < 0) { - U8 normIdx = encodedNorms.size() ? encodedNorms[i] : encodeNormal( batchData.initialNorms[i] ); - tsalloc.copyToBuffer8( (S8*)&normIdx, 1 ); + tsalloc.copyToBuffer32((S32*)verts.address(), 3 * verts.size()); + + // no longer do this here...let tsmesh handle this + tsalloc.copyToBuffer32((S32*)norms.address(), 3 * norms.size()); + + // if no parent mesh, compute encoded normals and copy over + for (S32 i = 0; i < norms.size(); i++) + { + U8 normIdx = encodedNorms.size() ? encodedNorms[i] : encodeNormal(norms[i]); + tsalloc.copyToBuffer8((S8*)&normIdx, 1); + } } } @@ -2868,14 +2894,31 @@ void TSSkinMesh::disassemble() if ( parentMesh < 0 ) tsalloc.copyToBuffer32( (S32*)batchData.initialTransforms.address(), batchData.initialTransforms.size() * 16 ); - tsalloc.set32( vertexIndex.size() ); - if ( parentMesh < 0 ) + if (!mVertexData.isReady()) { - tsalloc.copyToBuffer32( (S32*)vertexIndex.address(), vertexIndex.size() ); + tsalloc.set32(vertexIndex.size()); - tsalloc.copyToBuffer32( (S32*)boneIndex.address(), boneIndex.size() ); + tsalloc.copyToBuffer32((S32*)vertexIndex.address(), vertexIndex.size()); - tsalloc.copyToBuffer32( (S32*)weight.address(), weight.size() ); + tsalloc.copyToBuffer32((S32*)boneIndex.address(), boneIndex.size()); + + tsalloc.copyToBuffer32((S32*)weight.address(), weight.size()); + } + else + { + tsalloc.set32(0); + } + + if (TSShape::smVersion < 27) + { + if (parentMesh < 0) + { + tsalloc.copyToBuffer32((S32*)vertexIndex.address(), vertexIndex.size()); + + tsalloc.copyToBuffer32((S32*)boneIndex.address(), boneIndex.size()); + + tsalloc.copyToBuffer32((S32*)weight.address(), weight.size()); + } } tsalloc.set32( batchData.nodeIndex.size() ); @@ -2888,8 +2931,8 @@ void TSSkinMesh::disassemble() TSSkinMesh::TSSkinMesh() { meshType = SkinMeshType; - mDynamic = true; - batchDataInitialized = false; + batchData.initialized = false; + maxBones = -1; } //----------------------------------------------------------------------------- @@ -2956,6 +2999,9 @@ inline void TSMesh::findTangent( U32 index1, //----------------------------------------------------------------------------- void TSMesh::createTangents(const Vector &_verts, const Vector &_norms) { + if (_verts.size() == 0) // can only be done in editable mode + return; + U32 numVerts = _verts.size(); U32 numNorms = _norms.size(); if ( numVerts <= 0 || numNorms <= 0 ) @@ -3031,91 +3077,397 @@ void TSMesh::createTangents(const Vector &_verts, const Vector } } -void TSMesh::convertToAlignedMeshData() +void TSMesh::convertToVertexData() { - if(!mVertexData.isReady()) - _convertToAlignedMeshData(mVertexData, verts, norms); -} - - -void TSSkinMesh::convertToAlignedMeshData() -{ - if(!mVertexData.isReady()) - _convertToAlignedMeshData(mVertexData, batchData.initialVerts, batchData.initialNorms); -} - -void TSMesh::_convertToAlignedMeshData( TSMeshVertexArray &vertexData, const Vector &_verts, const Vector &_norms ) -{ - // If mVertexData is ready, and the input array is different than mVertexData - // use mVertexData to quickly initialize the input array - if(mVertexData.isReady() && vertexData.address() != mVertexData.address()) + if (!mVertexData.isReady()) { - AssertFatal(mVertexData.size() == mNumVerts, "Vertex data length mismatch; no idea how this happened."); - - // There doesn't seem to be an _mm_realloc, even though there is an _aligned_realloc - // We really shouldn't be re-allocating anyway. Should TSShapeInstance be - // storing an array of the data structures? That would certainly bloat memory. - void *aligned_mem = dMalloc_aligned(mVertSize * mNumVerts, 16); - AssertFatal(aligned_mem, "Aligned malloc failed! Debug!"); - - vertexData.set(aligned_mem, mVertSize, mNumVerts); - vertexData.setReady(true); - -#if defined(TORQUE_OS_XENON) - XMemCpyStreaming(vertexData.address(), mVertexData.address(), vertexData.mem_size() ); -#else - dMemcpy(vertexData.address(), mVertexData.address(), vertexData.mem_size()); -#endif - return; + _convertToVertexData(mVertexData, verts, norms); } +} +void TSSkinMesh::convertToVertexData() +{ + if (!mVertexData.isReady()) + { + // Batch data required here + createSkinBatchData(); - AssertFatal(!vertexData.isReady(), "Mesh already converted to aligned data! Re-check code!"); + // Dump verts to buffer + _convertToVertexData(mVertexData, batchData.initialVerts, batchData.initialNorms); + + // Setup bones too + setupVertexTransforms(); + } +} + +void TSMesh::copySourceVertexDataFrom(const TSMesh* srcMesh) +{ + verts = srcMesh->verts; + tverts = srcMesh->tverts; + norms = srcMesh->norms; + colors = srcMesh->colors; + tverts2 = srcMesh->tverts2; + + if (verts.size() == 0) + { + bool hasTVert2 = srcMesh->getHasTVert2(); + bool hasColor = srcMesh->getHasColor(); + + verts.setSize(srcMesh->mNumVerts); + tverts.setSize(srcMesh->mNumVerts); + norms.setSize(srcMesh->mNumVerts); + + if (hasTVert2) + colors.setSize(mNumVerts); + if (hasColor) + tverts2.setSize(mNumVerts); + + // Fill arrays + for (U32 i = 0; i < mNumVerts; i++) + { + const __TSMeshVertexBase &cv = srcMesh->mVertexData.getBase(i); + const __TSMeshVertex_3xUVColor &cvc = srcMesh->mVertexData.getColor(i); + verts[i] = cv.vert(); + tverts[i] = cv.tvert(); + norms[i] = cv.normal(); + + if (hasColor) + cvc.color().getColor(&colors[i]); + if (hasTVert2) + tverts2[i] = cvc.tvert2(); + } + } +} + +void TSSkinMesh::copySourceVertexDataFrom(const TSMesh* srcMesh) +{ + TSMesh::copySourceVertexDataFrom(srcMesh); + + if (srcMesh->getMeshType() == TSMesh::SkinMeshType) + { + const TSSkinMesh* srcSkinMesh = static_cast(srcMesh); + + weight = srcSkinMesh->weight; + boneIndex = srcSkinMesh->boneIndex; + vertexIndex = srcSkinMesh->vertexIndex; + maxBones = srcSkinMesh->maxBones; + + // Extract from vertex data + if (srcSkinMesh->vertexIndex.size() == 0) + { + mVertexData = srcMesh->mVertexData; + addWeightsFromVertexBuffer(); + mVertexData.setReady(false); + } + } +} + +U32 TSMesh::getNumVerts() +{ + return mVertexData.isReady() ? mNumVerts : verts.size(); +} + +void TSMesh::_convertToVertexData(TSMeshVertexArray &outArray, const Vector &_verts, const Vector &_norms) +{ + U32 colorOffset = 0; + U32 boneOffset = 0; + + // Update tangents list + createTangents(verts, norms); + + AssertFatal(_verts.size() == mNumVerts, "vert count mismatch"); + AssertFatal(!getHasColor() || colors.size() == _verts.size(), "Vector of color elements should be the same size as other vectors"); + AssertFatal(!getHasTVert2() || tverts2.size() == _verts.size(), "Vector of tvert2 elements should be the same size as other vectors"); + + AssertFatal(!outArray.isReady(), "Mesh already converted to aligned data! Re-check code!"); AssertFatal(_verts.size() == _norms.size() && - _verts.size() == tangents.size(), - "Vectors: verts, norms, tangents must all be the same size"); - mNumVerts = _verts.size(); + _verts.size() == tangents.size(), + "Vectors: verts, norms, tangents must all be the same size"); + AssertFatal(mVertSize == outArray.vertSize(), "Size inconsistency"); - // Initialize the vertex data - vertexData.set(NULL, 0, 0); - vertexData.setReady(true); - - if(mNumVerts == 0) + if (mNumVerts == 0) return; - mHasColor = !colors.empty(); - AssertFatal(!mHasColor || colors.size() == _verts.size(), "Vector of color elements should be the same size as other vectors"); + bool needsSkin = mVertexFormat->hasBlendIndices(); + bool needWeightSet = outArray.getBoneOffset() != 0; - mHasTVert2 = !tverts2.empty(); - AssertFatal(!mHasTVert2 || tverts2.size() == _verts.size(), "Vector of tvert2 elements should be the same size as other vectors"); + bool hasColor = getHasColor(); + bool hasTVert2 = getHasTVert2(); - // Create the proper array type - void *aligned_mem = dMalloc_aligned(mVertSize * mNumVerts, 16); - AssertFatal(aligned_mem, "Aligned malloc failed! Debug!"); + dMemset(&outArray.getBase(0), '\0', mVertSize * mNumVerts); - dMemset(aligned_mem, 0, mNumVerts * mVertSize); - vertexData.set(aligned_mem, mVertSize, mNumVerts); - - for(U32 i = 0; i < mNumVerts; i++) + for (U32 i = 0; i < mNumVerts; i++) { - __TSMeshVertexBase &v = vertexData[i]; + __TSMeshVertexBase &v = outArray.getBase(i); v.vert(_verts[i]); v.normal(_norms[i]); v.tangent(tangents[i]); - if(i < tverts.size()) + if (i < tverts.size()) v.tvert(tverts[i]); - if(mHasTVert2 && i < tverts2.size()) - v.tvert2(tverts2[i]); - if(mHasColor && i < colors.size()) - v.color(colors[i]); + + if (hasTVert2 || hasColor) + { + __TSMeshVertex_3xUVColor &vc = outArray.getColor(i); + if (hasTVert2 && i < tverts2.size()) + vc.tvert2(tverts2[i]); + if (hasColor && i < colors.size()) + vc.color(colors[i]); + } + + // NOTE: skin verts are set later on for the skinned mesh, otherwise we'll set the default (i.e. 0) if we need one for a rigid mesh + if (needWeightSet) + { + const Point4F wt(1.0f, 0.0f, 0.0f, 0.0f); + outArray.getBone(i, 0).weight(wt); + } + } +} + +void TSMesh::makeEditable() +{ + bool hasTVert2 = getHasTVert2(); + bool hasColor = getHasColor(); + bool hasVerts = verts.size() != 0; + + if(mVertexData.isReady() && !hasVerts) + { + copySourceVertexDataFrom(this); } - // Now that the data is in the aligned struct, free the Vector memory + mVertexData.setReady(false); + + mVertSize = 0; + mNumVerts = 0; + mVertOffset = 0; + + updateMeshFlags(); +} + +void TSSkinMesh::addWeightsFromVertexBuffer() +{ + weight.setSize(0); + boneIndex.setSize(0); + vertexIndex.setSize(0); + + U32 numBoneBlocks = maxBones >= 0 ? (maxBones + 3) / 4 : 0; + for (U32 i = 0; i < mNumVerts; i++) + { + for (U32 j = 0; j < numBoneBlocks; j++) + { + const __TSMeshVertex_BoneData &cv = mVertexData.getBone(i, j); + + if (cv._weights.x != 0.0f) + { + addWeightForVert(i, cv._indexes.x, cv._weights.x); + } + if (cv._weights.y != 0.0f) + { + addWeightForVert(i, cv._indexes.y, cv._weights.y); + } + if (cv._weights.z != 0.0f) + { + addWeightForVert(i, cv._indexes.z, cv._weights.z); + } + if (cv._weights.w != 0.0f) + { + addWeightForVert(i, cv._indexes.w, cv._weights.w); + } + } + } +} + +void TSSkinMesh::makeEditable() +{ + bool hasTVert2 = getHasTVert2(); + bool hasColor = getHasColor(); + bool hasVerts = verts.size() != 0; + + // Reconstruct bone mapping + if (mVertexData.isReady() && !hasVerts) + { + copySourceVertexDataFrom(this); + + weight.setSize(0); + boneIndex.setSize(0); + vertexIndex.setSize(0); + + addWeightsFromVertexBuffer(); + } + + mVertexData.setReady(false); + + mVertSize = 0; + mNumVerts = 0; + + updateMeshFlags(); + batchData.initialized = false; +} + +void TSMesh::clearEditable() +{ + if (verts.size() == 0) + return; + + if (colors.empty()) + clearFlags(HasColor); + else + setFlags(HasColor); + + if (tverts2.empty()) + clearFlags(HasTVert2); + else + setFlags(HasTVert2); + verts.free_memory(); norms.free_memory(); tangents.free_memory(); tverts.free_memory(); tverts2.free_memory(); colors.free_memory(); +} + +void TSMesh::updateMeshFlags() +{ + // Make sure flags are correct + if (colors.empty()) + clearFlags(HasColor); + else + setFlags(HasColor); + + if (tverts2.empty()) + clearFlags(HasTVert2); + else + setFlags(HasTVert2); +} + +void TSSkinMesh::clearEditable() +{ + TSMesh::clearEditable(); + + weight.free_memory(); + boneIndex.free_memory(); + vertexIndex.free_memory(); +} + +TSBasicVertexFormat::TSBasicVertexFormat() : + texCoordOffset(-1), + boneOffset(-1), + colorOffset(-1), + numBones(0), + vertexSize(-1) +{ +} + +TSBasicVertexFormat::TSBasicVertexFormat(TSMesh *mesh) +{ + texCoordOffset = -1; + boneOffset = -1; + colorOffset = -1; + numBones = 0; + vertexSize = -1; + + addMeshRequirements(mesh); +} + +void TSBasicVertexFormat::getFormat(GFXVertexFormat &fmt) +{ + // NOTE: previously the vertex data was padded to allow for verts to be skinned via SSE. + // since we now prefer to skin on the GPU and use a basic non-SSE fallback for software + // skinning, adding in padding via GFXSemantic::PADDING or dummy fields is no longer required. + fmt.addElement(GFXSemantic::POSITION, GFXDeclType_Float3); + fmt.addElement(GFXSemantic::TANGENTW, GFXDeclType_Float, 3); + fmt.addElement(GFXSemantic::NORMAL, GFXDeclType_Float3); + fmt.addElement(GFXSemantic::TANGENT, GFXDeclType_Float3); + + fmt.addElement(GFXSemantic::TEXCOORD, GFXDeclType_Float2, 0); + + if (texCoordOffset >= 0 || colorOffset >= 0) + { + fmt.addElement(GFXSemantic::TEXCOORD, GFXDeclType_Float2, 1); + fmt.addElement(GFXSemantic::COLOR, GFXDeclType_Color); + } + + for (U32 i=0; iset16(texCoordOffset); + alloc->set16(boneOffset); + alloc->set16(colorOffset); + alloc->set16(numBones); + alloc->set16(vertexSize); +} + +void TSBasicVertexFormat::readAlloc(TSShapeAlloc* alloc) +{ + texCoordOffset = alloc->get16(); + boneOffset = alloc->get16(); + colorOffset = alloc->get16(); + numBones = alloc->get16(); + vertexSize = alloc->get16(); +} + +void TSBasicVertexFormat::addMeshRequirements(TSMesh *mesh) +{ + bool hasColors = false; + bool hasTexcoord2 = false; + bool hasSkin = false; + + hasColors = mesh->getHasColor() || (texCoordOffset != -1); + hasTexcoord2 = mesh->getHasTVert2() || (colorOffset != -1); + hasSkin = (mesh->getMeshType() == TSMesh::SkinMeshType) || (boneOffset != -1); + + S32 offset = sizeof(TSMesh::__TSMeshVertexBase); + + if ((hasTexcoord2 || hasColors)) + { + if (texCoordOffset == -1 || colorOffset == -1) + { + texCoordOffset = offset; + colorOffset = offset + (sizeof(float) * 2); + } + + offset += sizeof(TSMesh::__TSMeshVertex_3xUVColor); + } + + if (hasSkin) + { + boneOffset = offset; + + U32 numMeshBones = mesh->getMaxBonesPerVert(); + U32 boneBlocks = numMeshBones / 4; + U32 extraBlocks = numMeshBones % 4 != 0 ? 1 : 0; + U32 neededBones = boneBlocks + extraBlocks; + numBones = MAX(neededBones, numBones); + } +} + +void TSSkinMesh::printVerts() +{ + for (U32 i = 0; i < mNumVerts; i++) + { + TSMesh::__TSMeshVertexBase &vb = mVertexData.getBase(i); + TSMesh::__TSMeshVertex_BoneData &bw = mVertexData.getBone(i, 0); + + Point3F vert = batchData.initialVerts[i]; + Con::printf("v[%i] == %f,%f,%f; iv == %f,%f,%f. bo=%i,%i,%i,%i bw=%f,%f,%f,%f", + i, vb._vert.x, vb._vert.y, vb._vert.z, + vert.x, vert.y, vert.z, + bw._indexes.x, bw._indexes.y, bw._indexes.z, bw._indexes.w, + bw._weights.x, bw._weights.y, bw._weights.z, bw._weights.w); + } } \ No newline at end of file diff --git a/Engine/source/ts/tsMesh.h b/Engine/source/ts/tsMesh.h index d2ac79087..d39dc96a3 100644 --- a/Engine/source/ts/tsMesh.h +++ b/Engine/source/ts/tsMesh.h @@ -96,65 +96,53 @@ typedef GFX360MemVertexBufferHandle<__NullVertexStruct> TSVertexBufferHandle; typedef GFXVertexBufferDataHandle TSVertexBufferHandle; #endif +class TSMesh; +class TSShapeAlloc; + +/// @name Vertex format serialization +/// { +struct TSBasicVertexFormat +{ + S16 texCoordOffset; + S16 boneOffset; + S16 colorOffset; + S16 numBones; + S16 vertexSize; + + TSBasicVertexFormat(); + TSBasicVertexFormat(TSMesh *mesh); + void getFormat(GFXVertexFormat &fmt); + void calculateSize(); + + void writeAlloc(TSShapeAlloc* alloc); + void readAlloc(TSShapeAlloc* alloc); + + void addMeshRequirements(TSMesh *mesh); +}; +/// } /// class TSMesh { friend class TSShape; - public: - struct TSMeshVertexArray; - protected: +public: - U32 meshType; - Box3F mBounds; - Point3F mCenter; - F32 mRadius; - F32 mVisibility; - bool mDynamic; - - const GFXVertexFormat *mVertexFormat; - - U32 mVertSize; - - TSVertexBufferHandle mVB; - GFXPrimitiveBufferHandle mPB; - - void _convertToAlignedMeshData( TSMeshVertexArray &vertexData, const Vector &_verts, const Vector &_norms ); - void _createVBIB( TSVertexBufferHandle &vb, GFXPrimitiveBufferHandle &pb ); - - public: - - enum + /// Helper class for a freeable vector + template + class FreeableVector : public Vector { - /// types... - StandardMeshType = 0, - SkinMeshType = 1, - DecalMeshType = 2, - SortedMeshType = 3, - NullMeshType = 4, - TypeMask = StandardMeshType|SkinMeshType|DecalMeshType|SortedMeshType|NullMeshType, + public: + bool free_memory() { return Vector::resize(0); } - /// flags (stored with meshType)... - Billboard = BIT(31), HasDetailTexture = BIT(30), - BillboardZAxis = BIT(29), UseEncodedNormals = BIT(28), - FlagMask = Billboard|BillboardZAxis|HasDetailTexture|UseEncodedNormals + FreeableVector& operator=(const Vector& p) { Vector::operator=(p); return *this; } + FreeableVector& operator=(const FreeableVector& p) { Vector::operator=(p); return *this; } }; - U32 getMeshType() const { return meshType & TypeMask; } - void setFlags(U32 flag) { meshType |= flag; } - void clearFlags(U32 flag) { meshType &= ~flag; } - U32 getFlags( U32 flag = 0xFFFFFFFF ) const { return meshType & flag; } - - const Point3F* getNormals( S32 firstVert ); - - S32 parentMesh; ///< index into shapes mesh list - S32 numFrames; - S32 numMatFrames; - S32 vertsPerFrame; - /// @name Aligned Vertex Data - /// @{ - #pragma pack(1) + /// { + +#pragma pack(1) + struct __TSMeshVertexBase { Point3F _vert; @@ -173,23 +161,40 @@ class TSMesh void tangent(const Point4F &t) { _tangent = t.asPoint3F(); _tangentW = t.w; } const Point2F &tvert() const { return _tvert; } - void tvert(const Point2F &tv) { _tvert = tv;} - - // Don't call these unless it's actually a __TSMeshVertex_3xUVColor, for real. - // We don't want a vftable for virtual methods. - Point2F &tvert2() const { return *reinterpret_cast(reinterpret_cast(const_cast<__TSMeshVertexBase *>(this)) + 0x30); } - void tvert2(const Point2F &tv) { (*reinterpret_cast(reinterpret_cast(this) + 0x30)) = tv; } - - GFXVertexColor &color() const { return *reinterpret_cast(reinterpret_cast(const_cast<__TSMeshVertexBase *>(this)) + 0x38); } - void color(const GFXVertexColor &c) { (*reinterpret_cast(reinterpret_cast(this) + 0x38)) = c; } + void tvert(const Point2F &tv) { _tvert = tv; } }; - struct __TSMeshVertex_3xUVColor : public __TSMeshVertexBase + struct __TSMeshVertex_3xUVColor { Point2F _tvert2; GFXVertexColor _color; - F32 _tvert3; // Unused, but needed for alignment purposes + + const Point2F &tvert2() const { return _tvert2; } + void tvert2(const Point2F& c) { _tvert2 = c; } + + const GFXVertexColor &color() const { return _color; } + void color(const GFXVertexColor &c) { _color = c; } }; + + struct __TSMeshIndex_List { + U8 x; + U8 y; + U8 z; + U8 w; + }; + + struct __TSMeshVertex_BoneData + { + __TSMeshIndex_List _indexes; + Point4F _weights; + + const __TSMeshIndex_List &index() const { return _indexes; } + void index(const __TSMeshIndex_List& c) { _indexes = c; } + + const Point4F &weight() const { return _weights; } + void weight(const Point4F &w) { _weights = w; } + }; + #pragma pack() struct TSMeshVertexArray @@ -197,53 +202,136 @@ class TSMesh protected: U8 *base; dsize_t vertSz; - bool vertexDataReady; U32 numElements; - public: - TSMeshVertexArray() : base(NULL), vertexDataReady(false), numElements(0) {} - virtual ~TSMeshVertexArray() { set(NULL, 0, 0); } + U32 colorOffset; + U32 boneOffset; - virtual void set(void *b, dsize_t s, U32 n, bool autoFree = true ) + bool vertexDataReady; + bool ownsData; + + public: + TSMeshVertexArray() : base(NULL), numElements(0), colorOffset(0), boneOffset(0), vertexDataReady(false), ownsData(false) {} + virtual ~TSMeshVertexArray() { set(NULL, 0, 0, 0, 0); } + + virtual void set(void *b, dsize_t s, U32 n, S32 inColorOffset, S32 inBoneOffset, bool nowOwnsData = true) { - if(base && autoFree) - dFree_aligned(base); - base = reinterpret_cast(b); - vertSz = s; - numElements = n; + if (base && ownsData) + dFree_aligned(base); + base = reinterpret_cast(b); + vertSz = s; + numElements = n; + colorOffset = inColorOffset >= 0 ? inColorOffset : 0; + boneOffset = inBoneOffset >= 0 ? inBoneOffset : 0; + ownsData = nowOwnsData; + } + + /// Gets pointer to __TSMeshVertexBase for vertex idx + __TSMeshVertexBase &getBase(int idx) const + { + AssertFatal(idx < numElements, "Out of bounds access!"); return *reinterpret_cast<__TSMeshVertexBase *>(base + (idx * vertSz)); + } + + /// Gets pointer to __TSMeshVertex_3xUVColor for vertex idx + __TSMeshVertex_3xUVColor &getColor(int idx) const + { + AssertFatal(idx < numElements, "Out of bounds access!"); return *reinterpret_cast<__TSMeshVertex_3xUVColor *>(base + (idx * vertSz) + colorOffset); + } + + /// Gets pointer to __TSMeshVertex_BoneData for vertex idx, additionally offsetted by subBoneList + __TSMeshVertex_BoneData &getBone(int idx, int subBoneList) const + { + AssertFatal(idx < numElements, "Out of bounds access!"); return *reinterpret_cast<__TSMeshVertex_BoneData *>(base + (idx * vertSz) + boneOffset + (sizeof(__TSMeshVertex_BoneData) * subBoneList)); + } + + /// Returns base address of vertex data + __TSMeshVertexBase *address() const + { + return reinterpret_cast<__TSMeshVertexBase *>(base); } - // Vector-like interface - __TSMeshVertexBase &operator[](S32 idx) const { AssertFatal(idx < numElements, "Out of bounds access!"); return *reinterpret_cast<__TSMeshVertexBase *>(base + idx * vertSz); } - __TSMeshVertexBase *address() const { return reinterpret_cast<__TSMeshVertexBase *>(base); } U32 size() const { return numElements; } dsize_t mem_size() const { return numElements * vertSz; } dsize_t vertSize() const { return vertSz; } bool isReady() const { return vertexDataReady; } void setReady(bool r) { vertexDataReady = r; } + + U8* getPtr() { return base; } + + inline U32 getColorOffset() const { return colorOffset; } + inline U32 getBoneOffset() const { return boneOffset; } }; - bool mHasColor; - bool mHasTVert2; +protected: + + U32 meshType; + Box3F mBounds; + Point3F mCenter; + F32 mRadius; + F32 mVisibility; + + const GFXVertexFormat *mVertexFormat; + + TSMesh *parentMeshObject; ///< Current parent object instance + + U32 mPrimBufferOffset; + + GFXVertexBufferDataHandle mVB; + GFXPrimitiveBufferHandle mPB; + +public: + + S32 parentMesh; ///< index into shapes mesh list + S32 numFrames; + S32 numMatFrames; + S32 vertsPerFrame; + + U32 mVertOffset; + U32 mVertSize; + +protected: + + void _convertToVertexData(TSMeshVertexArray &outArray, const Vector &_verts, const Vector &_norms); + + public: + + enum + { + /// types... + StandardMeshType = 0, + SkinMeshType = 1, + DecalMeshType = 2, + SortedMeshType = 3, + NullMeshType = 4, + TypeMask = StandardMeshType|SkinMeshType|DecalMeshType|SortedMeshType|NullMeshType, + + /// flags (stored with meshType)... + Billboard = BIT(31), HasDetailTexture = BIT(30), + BillboardZAxis = BIT(29), UseEncodedNormals = BIT(28), + HasColor = BIT(27), HasTVert2 = BIT(26), + FlagMask = Billboard|BillboardZAxis|HasDetailTexture|UseEncodedNormals|HasColor|HasTVert2 + }; + + U32 getMeshType() const { return meshType & TypeMask; } + U32 getHasColor() const { return colors.size() > 0 || meshType & HasColor; } + U32 getHasTVert2() const { return tverts2.size() > 0 || meshType & HasTVert2; } + void setFlags(U32 flag) { meshType |= flag; } + void clearFlags(U32 flag) { meshType &= ~flag; } + U32 getFlags( U32 flag = 0xFFFFFFFF ) const { return meshType & flag; } + + const Point3F* getNormals( S32 firstVert ); TSMeshVertexArray mVertexData; - dsize_t mNumVerts; - virtual void convertToAlignedMeshData(); + U32 mNumVerts; ///< Number of verts allocated in main vertex buffer + + virtual void convertToVertexData(); + + virtual void copySourceVertexDataFrom(const TSMesh* srcMesh); /// @} /// @name Vertex data /// @{ - template - class FreeableVector : public Vector - { - public: - bool free_memory() { return Vector::resize(0); } - - FreeableVector& operator=(const Vector& p) { Vector::operator=(p); return *this; } - FreeableVector& operator=(const FreeableVector& p) { Vector::operator=(p); return *this; } - }; - FreeableVector verts; FreeableVector norms; FreeableVector tverts; @@ -279,16 +367,16 @@ class TSMesh /// This is used by sgShadowProjector to render the /// mesh directly, skipping the render manager. - virtual void render( TSVertexBufferHandle &vb, GFXPrimitiveBufferHandle &pb ); + virtual void render( TSVertexBufferHandle &vb ); void innerRender( TSVertexBufferHandle &vb, GFXPrimitiveBufferHandle &pb ); virtual void render( TSMaterialList *, const TSRenderState &data, bool isSkinDirty, const Vector &transforms, TSVertexBufferHandle &vertexBuffer, - GFXPrimitiveBufferHandle &primitiveBuffer ); + const char *meshName); - void innerRender( TSMaterialList *, const TSRenderState &data, TSVertexBufferHandle &vb, GFXPrimitiveBufferHandle &pb ); + void innerRender( TSMaterialList *, const TSRenderState &data, TSVertexBufferHandle &vb, GFXPrimitiveBufferHandle &pb, const char *meshName ); /// @} @@ -326,12 +414,13 @@ class TSMesh static const Point3F& decodeNormal( U8 ncode ) { return smU8ToNormalTable[ncode]; } /// @} + virtual U32 getMaxBonesPerVert() { return 0; } + /// persist methods... virtual void assemble( bool skip ); static TSMesh* assembleMesh( U32 meshType, bool skip ); virtual void disassemble(); - void createVBIB(); void createTangents(const Vector &_verts, const Vector &_norms); void findTangent( U32 index1, U32 index2, @@ -350,6 +439,9 @@ class TSMesh /// have less that this count of verts. static S32 smMaxInstancingVerts; + /// Default node transform for standard meshes which have blend indices + static MatrixF smDummyNodeTransform; + /// convert primitives on load... void convertToTris(const TSDrawPrimitive *primitivesIn, const S32 *indicesIn, S32 numPrimIn, S32 & numPrimOut, S32 & numIndicesOut, @@ -361,6 +453,14 @@ class TSMesh S32 numPrimIn, S32 &numPrimOut, S32 &numIndicesOut, TSDrawPrimitive *primitivesOut, S32 *indicesOut) const; + /// Moves vertices from the vertex buffer back into the split vert lists, unless verts already exist + virtual void makeEditable(); + + /// Clears split vertex lists + virtual void clearEditable(); + + void updateMeshFlags(); + /// methods used during assembly to share vertexand other info /// between meshes (and for skipping detail levels on load) S32* getSharedData32( S32 parentMesh, S32 size, S32 **source, bool skip ); @@ -402,6 +502,9 @@ class TSMesh bool buildPolyListOpcode( const S32 od, AbstractPolyList *polyList, const Box3F &nodeBox, TSMaterialList *materials ); bool castRayOpcode( const Point3F &start, const Point3F &end, RayInfo *rayInfo, TSMaterialList *materials ); + void dumpPrimitives(U32 startVertex, U32 startIndex, GFXPrimitive *piArray, U16* ibIndices); + virtual U32 getNumVerts(); + static const F32 VISIBILITY_EPSILON; }; @@ -413,7 +516,7 @@ public: { enum Constants { - maxBonePerVert = 16, // Abitrarily chosen + maxBonePerVert = 16, // Assumes a maximum of 4 blocks of bone indices for HW skinning }; /// @name Batch by vertex @@ -441,48 +544,6 @@ public: Vector vertexBatchOperations; /// @} - /// @name Batch by Bone Transform - /// These are used for batches where each element is a bone transform, - /// and verts/normals are batch transformed against each element - /// @{ - - - #pragma pack(1) - - dALIGN( - - struct BatchedVertWeight - { - Point3F vert; // Do not change the ordering of these members - F32 weight; - Point3F normal; - S32 vidx; - } - - ); // dALIGN - - #pragma pack() - - struct BatchedTransform - { - public: - BatchedVertWeight *alignedMem; - dsize_t numElements; - Vector *_tmpVec; - - BatchedTransform() : alignedMem(NULL), numElements(0), _tmpVec(NULL) {} - virtual ~BatchedTransform() - { - if(alignedMem) - dFree_aligned(alignedMem); - alignedMem = NULL; - SAFE_DELETE(_tmpVec); - } - }; - SparseArray transformBatchOperations; - Vector transformKeys; - /// @} - // # = num bones Vector nodeIndex; Vector initialTransforms; @@ -490,36 +551,62 @@ public: // # = numverts Vector initialVerts; Vector initialNorms; + + bool initialized; + + BatchData() : initialized(false) { ; } }; /// This method will build the batch operations and prepare the BatchData /// for use. - void createBatchData(); - virtual void convertToAlignedMeshData(); + void createSkinBatchData(); + + /// Inserts transform indices and weights into vertex data + void setupVertexTransforms(); + + /// Returns maximum bones used per vertex + virtual U32 getMaxBonesPerVert(); + + virtual void convertToVertexData(); + virtual void copySourceVertexDataFrom(const TSMesh* srcMesh); + + void printVerts(); + + void addWeightsFromVertexBuffer(); + + void makeEditable(); + void clearEditable(); public: typedef TSMesh Parent; + + /// @name Vertex tuples + /// { + FreeableVector weight; ///< blend weight + FreeableVector boneIndex; ///< Maps from mesh node to bone in shape + FreeableVector vertexIndex; ///< index of affected vertex + /// } + + /// Maximum number of bones referenced by this skin mesh + S32 maxBones; /// Structure containing data needed to batch skinning BatchData batchData; - bool batchDataInitialized; - - /// vectors that define the vertex, weight, bone tuples - Vector weight; - Vector boneIndex; - Vector vertexIndex; /// set verts and normals... - void updateSkin( const Vector &transforms, TSVertexBufferHandle &instanceVB, GFXPrimitiveBufferHandle &instancePB ); + void updateSkinBuffer( const Vector &transforms, U8 *buffer ); + + /// update bone transforms for this mesh + void updateSkinBones( const Vector &transforms, Vector& destTransforms ); // render methods.. - void render( TSVertexBufferHandle &instanceVB, GFXPrimitiveBufferHandle &instancePB ); + void render( TSVertexBufferHandle &instanceVB ); void render( TSMaterialList *, const TSRenderState &data, bool isSkinDirty, const Vector &transforms, TSVertexBufferHandle &vertexBuffer, - GFXPrimitiveBufferHandle &primitiveBuffer ); + const char *meshName ); // collision methods... bool buildPolyList( S32 frame, AbstractPolyList *polyList, U32 &surfaceKey, TSMaterialList *materials ); @@ -532,6 +619,14 @@ public: void assemble( bool skip ); void disassemble(); + /// Helper method to add a blend tuple for a vertex + inline void addWeightForVert(U32 vi, U32 bi, F32 w) + { + weight.push_back(w); + boneIndex.push_back(bi); + vertexIndex.push_back(vi); + } + /// variables used during assembly (for skipping mesh detail levels /// on load and for sharing verts between meshes) static Vector smInitTransformList; @@ -540,6 +635,8 @@ public: static Vector smWeightList; static Vector smNodeIndexList; + static bool smDebugSkinVerts; + TSSkinMesh(); }; diff --git a/Engine/source/ts/tsMeshFit.cpp b/Engine/source/ts/tsMeshFit.cpp index 6d11f773a..7ea2049c9 100644 --- a/Engine/source/ts/tsMeshFit.cpp +++ b/Engine/source/ts/tsMeshFit.cpp @@ -282,7 +282,7 @@ void MeshFit::addSourceMesh( const TSShape::Object& obj, const TSMesh* mesh ) S32 count, stride; U8* pVert; - if ( mesh->mVertexData.isReady() ) + if ( mesh->mVertexData.isReady() && mesh->verts.size() == 0 ) { count = mesh->mVertexData.size(); stride = mesh->mVertexData.vertSize(); @@ -327,8 +327,6 @@ TSMesh* MeshFit::createTriMesh( F32* verts, S32 numVerts, U32* indices, S32 numT mesh->numMatFrames = 1; mesh->vertsPerFrame = numVerts; mesh->setFlags(0); - mesh->mHasColor = false; - mesh->mHasTVert2 = false; mesh->mNumVerts = numVerts; mesh->indices.reserve( numTris * 3 ); @@ -406,12 +404,28 @@ void MeshFit::addBox( const Point3F& sides, const MatrixF& mat ) if ( !mesh ) return; - for ( S32 i = 0; i < mesh->mVertexData.size(); i++ ) + if (mesh->verts.size() > 0) { - Point3F v = mesh->mVertexData[i].vert(); - v.convolve( sides ); - mesh->mVertexData[i].vert( v ); + for (S32 i = 0; i < mesh->verts.size(); i++) + { + Point3F v = mesh->verts[i]; + v.convolve(sides); + mesh->verts[i] = v; + } + + mesh->mVertexData.setReady(false); } + else + { + for (S32 i = 0; i < mesh->mVertexData.size(); i++) + { + TSMesh::__TSMeshVertexBase &vdata = mesh->mVertexData.getBase(i); + Point3F v = vdata.vert(); + v.convolve(sides); + vdata.vert(v); + } + } + mesh->computeBounds(); mMeshes.increment(); @@ -437,8 +451,9 @@ void MeshFit::addSphere( F32 radius, const Point3F& center ) for ( S32 i = 0; i < mesh->mVertexData.size(); i++ ) { - Point3F v = mesh->mVertexData[i].vert(); - mesh->mVertexData[i].vert( v * radius ); + TSMesh::__TSMeshVertexBase &vdata = mesh->mVertexData.getBase(i); + Point3F v = vdata.vert(); + vdata.vert( v * radius ); } mesh->computeBounds(); @@ -470,9 +485,9 @@ void MeshFit::addCapsule( F32 radius, F32 height, const MatrixF& mat ) F32 offset = ( height / ( 2 * radius ) ) - 0.5f; for ( S32 i = 0; i < mesh->mVertexData.size(); i++ ) { - Point3F v = mesh->mVertexData[i].vert(); + Point3F v = mesh->mVertexData.getBase(i).vert(); v.y += ( ( v.y > 0 ) ? offset : -offset ); - mesh->mVertexData[i].vert( v * radius ); + mesh->mVertexData.getBase(i).vert( v * radius ); } mesh->computeBounds(); @@ -784,13 +799,14 @@ DefineTSShapeConstructorMethod( addPrimitive, bool, ( const char* meshName, cons MatrixF mat( txfm.getMatrix() ); // Transform the mesh vertices - if ( mesh->mVertexData.isReady() ) + if ( mesh->mVertexData.isReady() && mesh->verts.size() == 0 ) { for (S32 i = 0; i < mesh->mVertexData.size(); i++) { + TSMesh::__TSMeshVertexBase &vdata = mesh->mVertexData.getBase(i); Point3F v; - mat.mulP( mesh->mVertexData[i].vert(), &v ); - mesh->mVertexData[i].vert( v ); + mat.mulP( vdata.vert(), &v ); + vdata.vert( v ); } } else diff --git a/Engine/source/ts/tsMeshIntrinsics.cpp b/Engine/source/ts/tsMeshIntrinsics.cpp index 94b9abd22..e11035dc7 100644 --- a/Engine/source/ts/tsMeshIntrinsics.cpp +++ b/Engine/source/ts/tsMeshIntrinsics.cpp @@ -26,7 +26,6 @@ void (*zero_vert_normal_bulk)(const dsize_t count, U8 * __restrict const outPtr, const dsize_t outStride) = NULL; -void (*m_matF_x_BatchedVertWeightList)(const MatrixF &mat, const dsize_t count, const TSSkinMesh::BatchData::BatchedVertWeight * __restrict batch, U8 * const __restrict outPtr, const dsize_t outStride) = NULL; //------------------------------------------------------------------------------ // Default C++ Implementations (pretty slow) @@ -47,33 +46,6 @@ void zero_vert_normal_bulk_C(const dsize_t count, U8 * __restrict const outPtr, } } -//------------------------------------------------------------------------------ - -void m_matF_x_BatchedVertWeightList_C(const MatrixF &mat, - const dsize_t count, - const TSSkinMesh::BatchData::BatchedVertWeight * __restrict batch, - U8 * const __restrict outPtr, - const dsize_t outStride) -{ - const register MatrixF m = mat; - - register Point3F tempPt; - register Point3F tempNrm; - - for(register S32 i = 0; i < count; i++) - { - const TSSkinMesh::BatchData::BatchedVertWeight &inElem = batch[i]; - - TSMesh::__TSMeshVertexBase *outElem = reinterpret_cast(outPtr + inElem.vidx * outStride); - - m.mulP( inElem.vert, &tempPt ); - m.mulV( inElem.normal, &tempNrm ); - - outElem->_vert += ( tempPt * inElem.weight ); - outElem->_normal += ( tempNrm * inElem.weight ); - } -} - //------------------------------------------------------------------------------ // Initializer. //------------------------------------------------------------------------------ @@ -86,11 +58,9 @@ MODULE_BEGIN( TSMeshIntrinsics ) { // Assign defaults (C++ versions) zero_vert_normal_bulk = zero_vert_normal_bulk_C; - m_matF_x_BatchedVertWeightList = m_matF_x_BatchedVertWeightList_C; #if defined(TORQUE_OS_XENON) zero_vert_normal_bulk = zero_vert_normal_bulk_X360; - m_matF_x_BatchedVertWeightList = m_matF_x_BatchedVertWeightList_X360; #else // Find the best implementation for the current CPU if(Platform::SystemInfo.processor.properties & CPU_PROP_SSE) @@ -98,21 +68,12 @@ MODULE_BEGIN( TSMeshIntrinsics ) #if defined(TORQUE_CPU_X86) zero_vert_normal_bulk = zero_vert_normal_bulk_SSE; - m_matF_x_BatchedVertWeightList = m_matF_x_BatchedVertWeightList_SSE; - - /* This code still has a bug left in it - #if (_MSC_VER >= 1500) - if(Platform::SystemInfo.processor.properties & CPU_PROP_SSE4_1) - m_matF_x_BatchedVertWeightList = m_matF_x_BatchedVertWeightList_SSE4; - #endif - */ #endif } else if(Platform::SystemInfo.processor.properties & CPU_PROP_ALTIVEC) { #if !defined(TORQUE_OS_XENON) && defined(TORQUE_CPU_PPC) zero_vert_normal_bulk = zero_vert_normal_bulk_gccvec; - m_matF_x_BatchedVertWeightList = m_matF_x_BatchedVertWeightList_gccvec; #endif } #endif diff --git a/Engine/source/ts/tsMeshIntrinsics.h b/Engine/source/ts/tsMeshIntrinsics.h index 5bf7e61d4..40b01416f 100644 --- a/Engine/source/ts/tsMeshIntrinsics.h +++ b/Engine/source/ts/tsMeshIntrinsics.h @@ -23,20 +23,6 @@ #ifndef _TSMESHINTRINSICS_H_ #define _TSMESHINTRINSICS_H_ -/// This is the batch-by-transform skin loop -/// -/// @param mat Bone transform -/// @param count Number of input elements in the batch -/// @param batch Pointer to the first element in an aligned array of input elements -/// @param outPtr Pointer to index 0 of a TSMesh aligned vertex buffer -/// @param outStride Size, in bytes, of one entry in the vertex buffer -extern void (*m_matF_x_BatchedVertWeightList) - (const MatrixF &mat, - const dsize_t count, - const TSSkinMesh::BatchData::BatchedVertWeight * __restrict batch, - U8 * const __restrict outPtr, - const dsize_t outStride); - /// Set the vertex position and normal to (0, 0, 0) /// /// @param count Number of elements diff --git a/Engine/source/ts/tsPartInstance.cpp b/Engine/source/ts/tsPartInstance.cpp index 32e1a6fe3..4c0875c3f 100644 --- a/Engine/source/ts/tsPartInstance.cpp +++ b/Engine/source/ts/tsPartInstance.cpp @@ -206,7 +206,11 @@ void TSPartInstance::render(S32 od, const TSRenderState &rdata) // render mesh objects for (i=0; irender(od,mSourceShape->getMaterialList(),rdata,1.0); + { + TSRenderState objState = rdata; + const char *meshName = mSourceShape->mShape->names[mMeshObjects[i]->object->nameIndex]; + mMeshObjects[i]->render(od,mSourceShape->mShape->mShapeVertexBuffer,mSourceShape->getMaterialList(),objState,1.0, meshName); + } } //------------------------------------------------------------------------------------- diff --git a/Engine/source/ts/tsRenderState.cpp b/Engine/source/ts/tsRenderState.cpp index ef8d3db56..f8c9b6a71 100644 --- a/Engine/source/ts/tsRenderState.cpp +++ b/Engine/source/ts/tsRenderState.cpp @@ -34,7 +34,9 @@ TSRenderState::TSRenderState() mCuller( NULL ), mLightQuery( NULL ), mUseOriginSort( false ), - mAccuTex( NULL ) + mAccuTex( NULL ), + mNodeTransforms( NULL ), + mNodeTransformCount( 0 ) { } @@ -48,6 +50,9 @@ TSRenderState::TSRenderState( const TSRenderState &state ) mCuller( state.mCuller ), mUseOriginSort( state.mUseOriginSort ), mLightQuery( state.mLightQuery ), - mAccuTex( state.mAccuTex ) + mAccuTex( state.mAccuTex ), + mUseOriginSort( state.mUseOriginSort ), + mNodeTransforms( state.mNodeTransforms ), + mNodeTransformCount( state.mNodeTransformCount ) { } diff --git a/Engine/source/ts/tsRenderState.h b/Engine/source/ts/tsRenderState.h index 25dce4729..fcb765185 100644 --- a/Engine/source/ts/tsRenderState.h +++ b/Engine/source/ts/tsRenderState.h @@ -35,7 +35,7 @@ class SceneRenderState; class GFXCubemap; class Frustum; class LightQuery; - +class TSShape; /// A simple class for passing render state through the pre-render pipeline. /// @@ -109,6 +109,12 @@ protected: // volume. This is passed down per-object. GFXTextureObject* mAccuTex; + /// List of matrices to use for hardware skinning + MatrixF *mNodeTransforms; + + /// Count of matrices in the mNodeTransforms list + U32 mNodeTransformCount; + public: @@ -159,6 +165,10 @@ public: void setAccuTex( GFXTextureObject* query ) { mAccuTex = query; } GFXTextureObject* getAccuTex() const { return mAccuTex; } + ///@ see mNodeTransforms, mNodeTransformCount + void setNodeTransforms(MatrixF *list, U32 count) { mNodeTransforms = list; mNodeTransformCount = count; } + void getNodeTransforms(MatrixF **list, U32 *count) const { *list = mNodeTransforms; *count = mNodeTransformCount; } + /// @} }; diff --git a/Engine/source/ts/tsShape.cpp b/Engine/source/ts/tsShape.cpp index b88b10d04..1ce31a0bb 100644 --- a/Engine/source/ts/tsShape.cpp +++ b/Engine/source/ts/tsShape.cpp @@ -42,7 +42,7 @@ extern TSShape* loadColladaShape(const Torque::Path &path); #endif /// most recent version -- this is the version we write -S32 TSShape::smVersion = 26; +S32 TSShape::smVersion = 28; /// the version currently being read...valid only during a read S32 TSShape::smReadVersion = -1; const U32 TSShape::smMostRecentExporterVersion = DTS_EXPORTER_CURRENT_VERSION; @@ -58,13 +58,14 @@ F32 TSShape::smAlphaOutDefault = -1.0f; S32 TSShape::smNumSkipLoadDetails = 0; bool TSShape::smInitOnRead = true; +bool TSShape::smUseHardwareSkinning = true; +U32 TSShape::smMaxSkinBones = 70; TSShape::TSShape() { materialList = NULL; mReadVersion = -1; // -1 means constructed from scratch (e.g., in exporter or no read yet) - mHasSkinMesh = false; mSequencesConstructed = false; mShapeData = NULL; mShapeDataSize = 0; @@ -286,6 +287,29 @@ bool TSShape::findMeshIndex(const String& meshName, S32& objIndex, S32& meshInde return false; } +bool TSShape::needsBufferUpdate() +{ + // No buffer? definitely need an update! + if (mVertexSize == 0 || mShapeVertexData.size == 0) + return true; + + // Check if we have modified vertex data + for (Vector::iterator iter = meshes.begin(); iter != meshes.end(); iter++) + { + TSMesh *mesh = *iter; + if (!mesh || + (mesh->getMeshType() != TSMesh::StandardMeshType && + mesh->getMeshType() != TSMesh::SkinMeshType)) + continue; + + // NOTE: cant use mVertexData.isReady since that might not be init'd at this stage + if (mesh->mVertSize == 0) + return true; + } + + return false; +} + TSMesh* TSShape::findMesh(const String& meshName) { S32 objIndex, meshIndex; @@ -545,75 +569,356 @@ void TSShape::init() detailCollisionAccelerators[dca] = NULL; } + // Assign mesh parents & format + for (Vector::iterator iter = meshes.begin(); iter != meshes.end(); iter++) + { + TSMesh *mesh = *iter; + if (!mesh) + continue; + + if (mesh->parentMesh >= 0) + { + mesh->parentMeshObject = meshes[mesh->parentMesh]; + } + else + { + mesh->parentMeshObject = NULL; + } + + mesh->mVertexFormat = &mVertexFormat; + } + initVertexFeatures(); initMaterialList(); } +void TSShape::initVertexBuffers() +{ + // Assumes mVertexData is valid + if (!mShapeVertexData.vertexDataReady) + { + AssertFatal(false, "WTF"); + } + + U32 destIndices = 0; + U32 destPrims = 0; + + for (Vector::iterator iter = meshes.begin(); iter != meshes.end(); iter++) + { + TSMesh *mesh = *iter; + if (!mesh || + (mesh->getMeshType() != TSMesh::StandardMeshType && + mesh->getMeshType() != TSMesh::SkinMeshType)) + continue; + + destIndices += mesh->indices.size(); + destPrims += mesh->primitives.size(); + } + + // For HW skinning we can just use the static buffer + if (TSShape::smUseHardwareSkinning) + { + getVertexBuffer(mShapeVertexBuffer, GFXBufferTypeStatic); + } + + // Also the IBO + mShapeVertexIndices.set(GFX, destIndices, destPrims, GFXBufferTypeStatic); + U16 *indicesStart = NULL; + mShapeVertexIndices.lock(&indicesStart, NULL); + U16 *ibIndices = indicesStart; + GFXPrimitive *piInput = mShapeVertexIndices->mPrimitiveArray; + U32 vertStart = 0; + U32 primStart = 0; + U32 indStart = 0; + + // Create VBO + for (Vector::iterator iter = meshes.begin(); iter != meshes.end(); iter++) + { + TSMesh *mesh = *iter; + if (!mesh || + (mesh->getMeshType() != TSMesh::StandardMeshType && + mesh->getMeshType() != TSMesh::SkinMeshType)) + continue; + + // Make the offset vbo + mesh->mPrimBufferOffset = primStart; + + // Dump primitives to locked buffer + mesh->dumpPrimitives(vertStart, indStart, piInput, ibIndices); + + AssertFatal(mesh->mVertOffset / mVertexSize == vertStart, "offset mismatch"); + + vertStart += mesh->mNumVerts; + primStart += mesh->primitives.size(); + indStart += mesh->indices.size(); + + mesh->mVB = mShapeVertexBuffer; + mesh->mPB = mShapeVertexIndices; + + // Advance + piInput += mesh->primitives.size(); + ibIndices += mesh->indices.size(); + + if (TSSkinMesh::smDebugSkinVerts && mesh->getMeshType() == TSMesh::SkinMeshType) + { + static_cast(mesh)->printVerts(); + } + } + +#ifdef TORQUE_DEBUG + // Verify prims + if (TSSkinMesh::smDebugSkinVerts) + { + U32 vertsInBuffer = mShapeVertexData.size / mVertexSize; + U32 primsInBuffer = piInput - mShapeVertexIndices->mPrimitiveArray; + U32 indsInBuffer = ibIndices - indicesStart; + + for (U32 i = 0; i < primStart; i++) + { + GFXPrimitive &prim = mShapeVertexIndices->mPrimitiveArray[i]; + + if (prim.type != GFXTriangleList && prim.type != GFXTriangleStrip) + { + AssertFatal(false, "Unexpected triangle list"); + } + + if (prim.type == GFXTriangleStrip) + continue; + + AssertFatal(prim.startVertex < vertsInBuffer, "wrong start vertex"); + AssertFatal((prim.startVertex + prim.numVertices) <= vertsInBuffer, "too many verts"); + AssertFatal(prim.startIndex + (prim.numPrimitives * 3) <= indsInBuffer, "too many inds"); + + for (U32 i = prim.startIndex; i < prim.startIndex + (prim.numPrimitives * 3); i++) + { + if (indicesStart[i] >= vertsInBuffer) + { + AssertFatal(false, "vert not in buffer"); + } + U16 idx = indicesStart[i]; + if (idx < prim.minIndex) + { + AssertFatal(false, "index out of minIndex range"); + } + } + } + } +#endif + + mShapeVertexIndices.unlock(); +} + +void TSShape::getVertexBuffer(TSVertexBufferHandle &vb, GFXBufferType bufferType) +{ + vb.set(GFX, mVertexSize, &mVertexFormat, mShapeVertexData.size / mVertexSize, bufferType); + + U8 *vertexData = mShapeVertexData.base; + U8 *vertPtr = vb.lock(); + dMemcpy(vertPtr, mShapeVertexData.base, mShapeVertexData.size); + vb.unlock(); +} + +void TSShape::initVertexBufferPointers() +{ + if (mBasicVertexFormat.vertexSize == -1) + return; + AssertFatal(mVertexSize == mBasicVertexFormat.vertexSize, "vertex size mismatch"); + + for (Vector::iterator iter = meshes.begin(); iter != meshes.end(); iter++) + { + TSMesh *mesh = *iter; + if (mesh && + (mesh->getMeshType() == TSMesh::StandardMeshType || + mesh->getMeshType() == TSMesh::SkinMeshType)) + { + // Set buffer + AssertFatal(mesh->mNumVerts >= mesh->vertsPerFrame, "invalid verts per frame"); + if (mesh->mVertSize > 0 && !mesh->mVertexData.isReady()) + { + U32 boneOffset = 0; + U32 colorOffset = 0; + AssertFatal(mesh->mVertSize == mVertexFormat.getSizeInBytes(), "mismatch in format size"); + + if (mBasicVertexFormat.boneOffset >= 0) + { + boneOffset = mBasicVertexFormat.boneOffset; + } + + if (mBasicVertexFormat.colorOffset >= 0) + { + colorOffset = mBasicVertexFormat.colorOffset; + } + + // Initialize the vertex data + mesh->mVertexData.set(mShapeVertexData.base + mesh->mVertOffset, mesh->mVertSize, mesh->mNumVerts, colorOffset, boneOffset, false); + mesh->mVertexData.setReady(true); + } + } + } +} + void TSShape::initVertexFeatures() { bool hasColors = false; bool hasTexcoord2 = false; + bool hasSkin = false; + U32 vertStart = 0; + U32 primStart = 0; + U32 indStart = 0; - Vector::iterator iter = meshes.begin(); - for ( ; iter != meshes.end(); iter++ ) + if (!needsBufferUpdate()) + { + // Init format from basic format + mVertexFormat.clear(); + mBasicVertexFormat.getFormat(mVertexFormat); + mVertexSize = mVertexFormat.getSizeInBytes(); + + initVertexBufferPointers(); + + for (Vector::iterator iter = meshes.begin(); iter != meshes.end(); iter++) + { + TSMesh *mesh = *iter; + if (mesh && + (mesh->getMeshType() == TSMesh::SkinMeshType)) + { + static_cast(mesh)->createSkinBatchData(); + } + } + + // Make sure VBO is init'd + initVertexBuffers(); + return; + } + + // Cleanout VBO + mShapeVertexBuffer = NULL; + + // Make sure mesh has verts stored in mesh data, we're recreating the buffer + TSBasicVertexFormat basicFormat; + + initVertexBufferPointers(); + + for (Vector::iterator iter = meshes.begin(); iter != meshes.end(); iter++) { TSMesh *mesh = *iter; - if ( mesh && - ( mesh->getMeshType() == TSMesh::StandardMeshType || - mesh->getMeshType() == TSMesh::SkinMeshType ) ) + if (mesh && + (mesh->getMeshType() == TSMesh::StandardMeshType || + mesh->getMeshType() == TSMesh::SkinMeshType)) { - if ( mesh->mVertexData.isReady() ) + // Make sure we have everything in the vert lists + mesh->makeEditable(); + + // We need the skin batching data here to determine bone counts + if (mesh->getMeshType() == TSMesh::SkinMeshType) { - hasColors |= mesh->mHasColor; - hasTexcoord2 |= mesh->mHasTVert2; - } - else - { - hasColors |= !mesh->colors.empty(); - hasTexcoord2 |= !mesh->tverts2.empty(); + static_cast(mesh)->createSkinBatchData(); } + + basicFormat.addMeshRequirements(mesh); } } - mVertSize = ( hasTexcoord2 || hasColors ) ? sizeof(TSMesh::__TSMeshVertex_3xUVColor) : sizeof(TSMesh::__TSMeshVertexBase); mVertexFormat.clear(); - - mVertexFormat.addElement( GFXSemantic::POSITION, GFXDeclType_Float3 ); - mVertexFormat.addElement( GFXSemantic::TANGENTW, GFXDeclType_Float, 3 ); - mVertexFormat.addElement( GFXSemantic::NORMAL, GFXDeclType_Float3 ); - mVertexFormat.addElement( GFXSemantic::TANGENT, GFXDeclType_Float3 ); + mBasicVertexFormat = basicFormat; + mBasicVertexFormat.getFormat(mVertexFormat); + mBasicVertexFormat.vertexSize = mVertexFormat.getSizeInBytes(); + mVertexSize = mBasicVertexFormat.vertexSize; - mVertexFormat.addElement( GFXSemantic::TEXCOORD, GFXDeclType_Float2, 0 ); - - if(hasTexcoord2 || hasColors) - { - mVertexFormat.addElement( GFXSemantic::TEXCOORD, GFXDeclType_Float2, 1 ); - mVertexFormat.addElement( GFXSemantic::COLOR, GFXDeclType_Color ); - mVertexFormat.addElement( GFXSemantic::TEXCOORD, GFXDeclType_Float, 2 ); - } + U32 destVertex = 0; + U32 destIndices = 0; // Go fix up meshes to include defaults for optional features // and initialize them if they're not a skin mesh. - iter = meshes.begin(); - for ( ; iter != meshes.end(); iter++ ) + U32 count = 0; + for (Vector::iterator iter = meshes.begin(); iter != meshes.end(); iter++) { TSMesh *mesh = *iter; - if ( !mesh || - ( mesh->getMeshType() != TSMesh::StandardMeshType && - mesh->getMeshType() != TSMesh::SkinMeshType ) ) + if (!mesh || + (mesh->getMeshType() != TSMesh::StandardMeshType && + mesh->getMeshType() != TSMesh::SkinMeshType)) continue; - // Set the flags. - mesh->mVertexFormat = &mVertexFormat; - mesh->mVertSize = mVertSize; + mesh->mVertSize = mVertexSize; + mesh->mVertOffset = destVertex; - // Create and fill aligned data structure - mesh->convertToAlignedMeshData(); + destVertex += mesh->mVertSize * mesh->getNumVerts(); + destIndices += mesh->indices.size(); - // Init the vertex buffer. - if ( mesh->getMeshType() == TSMesh::StandardMeshType ) - mesh->createVBIB(); + count += 1; } + + // Don't set up if we have no meshes + if (count == 0) + { + mShapeVertexData.set(NULL, 0); + mShapeVertexData.vertexDataReady = false; + return; + } + + // Now we can create the VBO + U8 *vertexData = (U8*)dMalloc_aligned(destVertex, 16); + U8 *vertexDataPtr = vertexData; + mShapeVertexData.set(vertexData, destVertex); + + // Create VBO + for (Vector::iterator iter = meshes.begin(); iter != meshes.end(); iter++) + { + TSMesh *mesh = *iter; + U32 idx = iter - meshes.begin(); + + if (!mesh || + (mesh->getMeshType() != TSMesh::StandardMeshType && + mesh->getMeshType() != TSMesh::SkinMeshType)) + continue; + + U32 boneOffset = 0; + U32 colorOffset = 0; + AssertFatal(mesh->mVertSize == mVertexFormat.getSizeInBytes(), "mismatch in format size"); + + if (mBasicVertexFormat.boneOffset >= 0) + { + boneOffset = mBasicVertexFormat.boneOffset; + } + + if (mBasicVertexFormat.colorOffset >= 0) + { + colorOffset = mBasicVertexFormat.colorOffset; + } + + // Dump everything + mesh->mVertexData.setReady(false); + mesh->mVertSize = mVertexSize; + AssertFatal(mesh->mVertOffset == vertexDataPtr - vertexData, "vertex offset mismatch"); + mesh->mNumVerts = mesh->getNumVerts(); + + mesh->mVertexData.set(mShapeVertexData.base + mesh->mVertOffset, mesh->mVertSize, mesh->mNumVerts, colorOffset, boneOffset, false); + mesh->convertToVertexData(); + mesh->mVertexData.setReady(true); + +#ifdef TORQUE_DEBUG + AssertFatal(mesh->mNumVerts == mesh->verts.size(), "vert mismatch"); + for (U32 i = 0; i < mesh->mNumVerts; i++) + { + Point3F v1 = mesh->verts[i]; + Point3F v2 = mesh->mVertexData.getBase(i).vert(); + AssertFatal(mesh->verts[i] == mesh->mVertexData.getBase(i).vert(), "vert data mismatch"); + } + + if (mesh->getMeshType() == TSMesh::SkinMeshType) + { + AssertFatal(mesh->getMaxBonesPerVert() != 0, "Skin mesh has no bones used, very strange!"); + } +#endif + + // Advance + vertexDataPtr += mesh->mVertSize * mesh->mNumVerts; + + AssertFatal(vertexDataPtr - vertexData <= destVertex, "Vertex data overflow"); + } + + mShapeVertexData.vertexDataReady = true; + + initVertexBuffers(); } void TSShape::setupBillboardDetails( const String &cachePath ) @@ -654,8 +959,6 @@ void TSShape::initMaterialList() subShapeFirstTranslucentObject.setSize(numSubShapes); #endif - mHasSkinMesh = false; - S32 i,j,k; // for each subshape, find the first translucent object // also, while we're at it, set mHasTranslucency @@ -674,8 +977,6 @@ void TSShape::initMaterialList() if (!mesh) continue; - mHasSkinMesh |= mesh->getMeshType() == TSMesh::SkinMeshType; - for (k=0; kprimitives.size(); k++) { if (mesh->primitives[k].matIndex & TSDrawPrimitive::NoMaterial) @@ -1118,6 +1419,40 @@ void TSShape::assembleShape() tsalloc.checkGuard(); + if (TSShape::smReadVersion >= 27) + { + // Vertex format is set here + S8 *vboData = NULL; + S32 vboSize = 0; + + mBasicVertexFormat.readAlloc(&tsalloc); + mVertexFormat.clear(); + mBasicVertexFormat.getFormat(mVertexFormat); + mVertexSize = mVertexFormat.getSizeInBytes(); + + AssertFatal(mVertexSize == mBasicVertexFormat.vertexSize, "vertex size mismatch"); + + vboSize = tsalloc.get32(); + vboData = tsalloc.getPointer8(vboSize); + + if (tsalloc.getBuffer() && vboSize > 0) + { + U8 *vertexData = (U8*)dMalloc_aligned(vboSize, 16); + U8 *vertexDataPtr = vertexData; + dMemcpy(vertexData, vboData, vboSize); + mShapeVertexData.set(vertexData, vboSize); + mShapeVertexData.vertexDataReady = true; + } + else + { + mShapeVertexData.set(NULL, 0); + } + } + else + { + mShapeVertexData.set(NULL, 0); + } + // about to read in the meshes...first must allocate some scratch space S32 scratchSize = getMax(numSkins,numMeshes); TSMesh::smVertsList.setSize(scratchSize); @@ -1401,6 +1736,19 @@ void TSShape::disassembleShape() } tsalloc.setGuard(); + if (TSShape::smVersion >= 27) + { + // Vertex format now included with mesh data. Note this doesn't include index data which + // is constructed directly in the buffer from the meshes + S8 *vboData = NULL; + S32 vboSize = 0; + + mBasicVertexFormat.writeAlloc(&tsalloc); + + tsalloc.set32(mShapeVertexData.size); + tsalloc.copyToBuffer8((S8*)mShapeVertexData.base, mShapeVertexData.size); + } + // read in the meshes (sans skins)... bool * isMesh = new bool[numMeshes]; // funny business because decals are pretend meshes (legacy issue) for (i=0;imeshType = %d;", i, obj->meshType); - // Con::errorf(" meshes[%d]->mBounds.minExtents.set(%g, %g, %g);", i, obj->mBounds.minExtents.x, obj->mBounds.minExtents.y, obj->mBounds.minExtents.z); - // Con::errorf(" meshes[%d]->mBounds.maxExtents.set(%g, %g, %g);", i, obj->mBounds.maxExtents.x, obj->mBounds.maxExtents.y, obj->mBounds.maxExtents.z); - // Con::errorf(" meshes[%d]->mCenter.set(%g, %g, %g);", i, obj->mCenter.x, obj->mCenter.y, obj->mCenter.z); - // Con::errorf(" meshes[%d]->mRadius = %g;", i, obj->mRadius); - // Con::errorf(" meshes[%d]->mVisibility = %g;", i, obj->mVisibility); - // Con::errorf(" meshes[%d]->mDynamic = %d;", i, obj->mDynamic); - // Con::errorf(" meshes[%d]->parentMesh = %d;", i, obj->parentMesh); - // Con::errorf(" meshes[%d]->numFrames = %d;", i, obj->numFrames); - // Con::errorf(" meshes[%d]->numMatFrames = %d;", i, obj->numMatFrames); - // Con::errorf(" meshes[%d]->vertsPerFrame = %d;", i, obj->vertsPerFrame); - - // Con::errorf("\n meshes[%d]->verts.set(dMalloc(%d * sizeof(Point3F)), %d);", obj->verts.size(), obj->verts.size()); - // for (U32 j = 0; j < obj->verts.size(); j++) - // Con::errorf(" meshes[%d]->verts[%d].set(%g, %g, %g);", i, j, obj->verts[j].x, obj->verts[j].y, obj->verts[j].z); - - // Con::errorf("\n meshes[%d]->norms.set(dMalloc(%d * sizeof(Point3F)), %d);", obj->norms.size(), obj->norms.size()); - // for (U32 j = 0; j < obj->norms.size(); j++) - // Con::errorf(" meshes[%d]->norms[%d].set(%g, %g, %g);", i, j, obj->norms[j].x, obj->norms[j].y, obj->norms[j].z); - - // Con::errorf("\n meshes[%d]->tverts.set(dMalloc(%d * sizeof(Point2F)), %d);", obj->tverts.size(), obj->tverts.size()); - // for (U32 j = 0; j < obj->tverts.size(); j++) - // Con::errorf(" meshes[%d]->tverts[%d].set(%g, %g);", i, j, obj->tverts[j].x, obj->tverts[j].y); - - // Con::errorf("\n meshes[%d]->primitives.set(dMalloc(%d * sizeof(TSDrawPrimitive)), %d);", obj->primitives.size(), obj->primitives.size()); - // for (U32 j = 0; j < obj->primitives.size(); j++) - // { - // TSDrawPrimitive& prim = obj->primitives[j]; - - // Con::errorf(" meshes[%d]->primitives[%d].start = %d;", i, j, prim.start); - // Con::errorf(" meshes[%d]->primitives[%d].numElements = %d;", i, j, prim.numElements); - // Con::errorf(" meshes[%d]->primitives[%d].matIndex = %d;", i, j, prim.matIndex); - // } - - // Con::errorf("\n meshes[%d]->encodedNorms.set(dMalloc(%d * sizeof(U8)), %d);", obj->encodedNorms.size(), obj->encodedNorms.size()); - // for (U32 j = 0; j < obj->encodedNorms.size(); j++) - // Con::errorf(" meshes[%d]->encodedNorms[%d] = %c;", i, j, obj->encodedNorms[j]); - - // Con::errorf("\n meshes[%d]->indices.set(dMalloc(%d * sizeof(U16)), %d);", obj->indices.size(), obj->indices.size()); - // for (U32 j = 0; j < obj->indices.size(); j++) - // Con::errorf(" meshes[%d]->indices[%d] = %d;", i, j, obj->indices[j]); - - // Con::errorf("\n meshes[%d]->initialTangents.set(dMalloc(%d * sizeof(Point3F)), %d);", obj->initialTangents.size(), obj->initialTangents.size()); - // for (U32 j = 0; j < obj->initialTangents.size(); j++) - // Con::errorf(" meshes[%d]->initialTangents[%d].set(%g, %g, %g);", i, j, obj->initialTangents[j].x, obj->initialTangents[j].y, obj->initialTangents[j].z); - - // Con::errorf("\n meshes[%d]->tangents.set(dMalloc(%d * sizeof(Point4F)), %d);", obj->tangents.size(), obj->tangents.size()); - // for (U32 j = 0; j < obj->tangents.size(); j++) - // Con::errorf(" meshes[%d]->tangents[%d].set(%g, %g, %g, %g);", i, j, obj->tangents[j].x, obj->tangents[j].y, obj->tangents[j].z, obj->tangents[j].w); - - // Con::errorf(" meshes[%d]->billboardAxis.set(%g, %g, %g);", i, obj->billboardAxis.x, obj->billboardAxis.y, obj->billboardAxis.z); - - // Con::errorf("\n meshes[%d]->planeNormals.set(dMalloc(%d * sizeof(Point3F)), %d);", obj->planeNormals.size(), obj->planeNormals.size()); - // for (U32 j = 0; j < obj->planeNormals.size(); j++) - // Con::errorf(" meshes[%d]->planeNormals[%d].set(%g, %g, %g);", i, j, obj->planeNormals[j].x, obj->planeNormals[j].y, obj->planeNormals[j].z); - - // Con::errorf("\n meshes[%d]->planeConstants.set(dMalloc(%d * sizeof(F32)), %d);", obj->planeConstants.size(), obj->planeConstants.size()); - // for (U32 j = 0; j < obj->planeConstants.size(); j++) - // Con::errorf(" meshes[%d]->planeConstants[%d] = %g;", i, j, obj->planeConstants[j]); - - // Con::errorf("\n meshes[%d]->planeMaterials.set(dMalloc(%d * sizeof(U32)), %d);", obj->planeMaterials.size(), obj->planeMaterials.size()); - // for (U32 j = 0; j < obj->planeMaterials.size(); j++) - // Con::errorf(" meshes[%d]->planeMaterials[%d] = %d;", i, j, obj->planeMaterials[j]); - - // Con::errorf(" meshes[%d]->planesPerFrame = %d;", i, obj->planesPerFrame); - // Con::errorf(" meshes[%d]->mergeBufferStart = %d;", i, obj->mergeBufferStart); - // } - // } - - // Con::errorf("\nalphaIn.set(dMalloc(%d * sizeof(F32)), %d);", alphaIn.size(), alphaIn.size()); - // for (U32 i = 0; i < alphaIn.size(); i++) - // Con::errorf(" alphaIn[%d] = %g;", i, alphaIn[i]); - - // Con::errorf("\nalphaOut.set(dMalloc(%d * sizeof(F32)), %d);", alphaOut.size(), alphaOut.size()); - // for (U32 i = 0; i < alphaOut.size(); i++) - // Con::errorf(" alphaOut[%d] = %g;", i, alphaOut[i]); - - // //Con::errorf("numSequences = %d", sequences.size()); - // //Con::errorf("numNodeRotations = %d", nodeRotations.size()); - // //Con::errorf("numNodeTranslations = %d", nodeTranslations.size()); - // //Con::errorf("numNodeUniformScales = %d", nodeUniformScales.size()); - // //Con::errorf("numNodeAlignedScales = %d", nodeAlignedScales.size()); - // //Con::errorf("numNodeArbitraryScaleRots = %d", nodeArbitraryScaleRots.size()); - // //Con::errorf("numNodeArbitraryScaleFactors = %d", nodeArbitraryScaleFactors.size()); - // //Con::errorf("numGroundRotations = %d", groundRotations.size()); - // //Con::errorf("numGroundTranslations = %d", groundTranslations.size()); - // //Con::errorf("numTriggers = %d", triggers.size()); - // //Con::errorf("numBillboardDetails = %d", billboardDetails.size()); - - // //Con::errorf("\nnumDetailCollisionAccelerators = %d", detailCollisionAccelerators.size()); - // //for (U32 i = 0; i < detailCollisionAccelerators.size(); i++) - // //{ - // // ConvexHullAccelerator* obj = detailCollisionAccelerators[i]; - - // // if (obj) - // // { - // // Con::errorf(" detailCollisionAccelerators[%d].numVerts = %d", i, obj->numVerts); - - // // for (U32 j = 0; j < obj->numVerts; j++) - // // { - // // Con::errorf(" verts[%d](%g, %g, %g)", j, obj->vertexList[j].x, obj->vertexList[j].y, obj->vertexList[j].z); - // // Con::errorf(" norms[%d](%g, %g, %g)", j, obj->normalList[j].x, obj->normalList[j].y, obj->normalList[j].z); - // // //U8** emitStrings; - // // } - // // } - // //} - - // Con::errorf("\nnames.setSize(%d);", names.size()); - // for (U32 i = 0; i < names.size(); i++) - // Con::errorf(" names[%d] = StringTable->insert(\"%s\");", i, names[i]); - - // //TSMaterialList * materialList; - - // Con::errorf("\nradius = %g;", radius); - // Con::errorf("tubeRadius = %g;", tubeRadius); - // Con::errorf("center.set(%g, %g, %g);", center.x, center.y, center.z); - // Con::errorf("bounds.minExtents.set(%g, %g, %g);", bounds.minExtents.x, bounds.minExtents.y, bounds.minExtents.z); - // Con::errorf("bounds.maxExtents.set(%g, %g, %g);", bounds.maxExtents.x, bounds.maxExtents.y, bounds.maxExtents.z); - - // Con::errorf("\nmExporterVersion = %d;", mExporterVersion); - // Con::errorf("mSmallestVisibleSize = %g;", mSmallestVisibleSize); - // Con::errorf("mSmallestVisibleDL = %d;", mSmallestVisibleDL); - // Con::errorf("mReadVersion = %d;", mReadVersion); - // Con::errorf("mFlags = %d;", mFlags); - // //Con::errorf("data = %d", data); - // Con::errorf("mSequencesConstructed = %d;", mSequencesConstructed); - //} + } return true; } @@ -2294,3 +2425,14 @@ void TSShape::computeAccelerator(S32 dl) AssertFatal(currPos == emitStringLen, "Error, over/underflowed the emission string!"); } } + +void TSShape::finalizeEditable() +{ + for (U32 i = 0; i < meshes.size(); i++) + { + if (meshes[i]) + { + meshes[i]->clearEditable(); + } + } +} diff --git a/Engine/source/ts/tsShape.h b/Engine/source/ts/tsShape.h index ba98826f5..09a5a84f1 100644 --- a/Engine/source/ts/tsShape.h +++ b/Engine/source/ts/tsShape.h @@ -50,6 +50,25 @@ struct CollisionShapeInfo PhysicsCollision *colShape; }; +/// Data storage helper for main shape buffer +struct TSShapeVertexArray +{ + U8 *base; + U32 size; + bool vertexDataReady; + + TSShapeVertexArray() : base(NULL), size(0), vertexDataReady(false) {} + virtual ~TSShapeVertexArray() { set(NULL, 0); } + + virtual void set(void *b, U32 s, bool autoFree = true) + { + if (base && autoFree) + dFree_aligned(base); + base = reinterpret_cast(b); + size = s; + } +}; + /// TSShape stores generic data for a 3space model. /// /// TSShape and TSShapeInstance act in conjunction to allow the rendering and @@ -381,19 +400,21 @@ class TSShape /// The GFX vertex format for all detail meshes in the shape. /// @see initVertexFeatures() GFXVertexFormat mVertexFormat; - - /// The GFX vertex size in bytes for all detail meshes in the shape. - /// @see initVertexFeatures() - U32 mVertSize; - - /// Is true if this shape contains skin meshes. - bool mHasSkinMesh; - - bool mSequencesConstructed; + TSBasicVertexFormat mBasicVertexFormat; + U32 mVertexSize; S8* mShapeData; U32 mShapeDataSize; + + // Processed vertex data + TSShapeVertexArray mShapeVertexData; + TSVertexBufferHandle mShapeVertexBuffer; + GFXPrimitiveBufferHandle mShapeVertexIndices; + + bool mSequencesConstructed; + + // shape class has few methods -- // just constructor/destructor, io, and lookup methods @@ -402,14 +423,24 @@ class TSShape ~TSShape(); void init(); void initMaterialList(); ///< you can swap in a new material list, but call this if you do + void finalizeEditable(); bool preloadMaterialList(const Torque::Path &path); ///< called to preload and validate the materials in the mat list void setupBillboardDetails( const String &cachePath ); + + /// Initializes the main vertex buffer + void initVertexBuffers(); + + /// Loads shape vertex data into specified buffer + void getVertexBuffer(TSVertexBufferHandle &vb, GFXBufferType bufferType); /// Called from init() to calcuate the GFX vertex features for /// all detail meshes in the shape. void initVertexFeatures(); + /// Inits basic buffer pointers on load + void initVertexBufferPointers(); + bool getSequencesConstructed() const { return mSequencesConstructed; } void setSequencesConstructed(const bool c) { mSequencesConstructed = c; } @@ -526,7 +557,7 @@ class TSShape const GFXVertexFormat* getVertexFormat() const { return &mVertexFormat; } - U32 getVertexSize() const { return mVertSize; } + bool needsBufferUpdate(); /// @} @@ -548,6 +579,12 @@ class TSShape /// by default we initialize shape when we read... static bool smInitOnRead; + /// Enables hardware skinning features + static bool smUseHardwareSkinning; + + /// Determines maximum number of bones to use in hardware skinning shaders + static U32 smMaxSkinBones; + /// @name Version Info /// @{ diff --git a/Engine/source/ts/tsShapeEdit.cpp b/Engine/source/ts/tsShapeEdit.cpp index 8d585022f..13d6abd13 100644 --- a/Engine/source/ts/tsShapeEdit.cpp +++ b/Engine/source/ts/tsShapeEdit.cpp @@ -892,10 +892,6 @@ TSMesh* TSShape::copyMesh( const TSMesh* srcMesh ) const mesh = new TSMesh; } - // Set vertex format (all meshes in this shape must share the same format) - mesh->mVertSize = mVertSize; - mesh->mVertexFormat = &mVertexFormat; - if ( !srcMesh ) return mesh; // return an empty mesh @@ -906,53 +902,16 @@ TSMesh* TSShape::copyMesh( const TSMesh* srcMesh ) const mesh->numMatFrames = srcMesh->numMatFrames; mesh->vertsPerFrame = srcMesh->vertsPerFrame; mesh->setFlags(srcMesh->getFlags()); - mesh->mHasColor = srcMesh->mHasColor; - mesh->mHasTVert2 = srcMesh->mHasTVert2; mesh->mNumVerts = srcMesh->mNumVerts; - if ( srcMesh->mVertexData.isReady() ) - { - mesh->mVertexData.set( NULL, 0, 0, false ); - void *aligned_mem = dMalloc_aligned( mVertSize * srcMesh->mVertexData.size(), 16 ); + // Copy vertex data in an *unpacked* form + mesh->copySourceVertexDataFrom(srcMesh); - // Copy the source data (note that the destination shape may have different vertex size) - if ( mVertSize == srcMesh->mVertexData.size() ) - { - dMemcpy( aligned_mem, srcMesh->mVertexData.address(), srcMesh->mVertexData.mem_size() ); - } - else - { - U8* src = (U8*)srcMesh->mVertexData.address(); - U8* dest = (U8*)aligned_mem; - for ( S32 i = 0; i < srcMesh->mVertexData.size(); i++ ) - { - dMemcpy( dest, src, srcMesh->mVertexData.vertSize() ); - src += srcMesh->mVertexData.vertSize(); - dest += mVertSize; - } - } - mesh->mVertexData.set( aligned_mem, mVertSize, srcMesh->mVertexData.size() ); - mesh->mVertexData.setReady( true ); - } - else - { - mesh->verts = srcMesh->verts; - mesh->tverts = srcMesh->tverts; - mesh->tverts2 = srcMesh->tverts2; - mesh->colors = srcMesh->colors; - mesh->norms = srcMesh->norms; - - mesh->createTangents(mesh->verts, mesh->norms); - mesh->encodedNorms.set(NULL,0); - - mesh->convertToAlignedMeshData(); - } + mesh->createTangents(mesh->verts, mesh->norms); + mesh->encodedNorms.set(NULL, 0); mesh->computeBounds(); - if ( mesh->getMeshType() != TSMesh::SkinMeshType ) - mesh->createVBIB(); - return mesh; } diff --git a/Engine/source/ts/tsShapeInstance.cpp b/Engine/source/ts/tsShapeInstance.cpp index 03b8c79c3..6d24e49f5 100644 --- a/Engine/source/ts/tsShapeInstance.cpp +++ b/Engine/source/ts/tsShapeInstance.cpp @@ -368,8 +368,9 @@ void TSShapeInstance::renderDebugNormals( F32 normalScalar, S32 dl ) PrimBuild::begin( GFXLineList, 2 * numNrms ); for ( U32 n = 0; n < numNrms; n++ ) { - Point3F norm = mesh->mVertexData[n].normal(); - Point3F vert = mesh->mVertexData[n].vert(); + const TSMesh::__TSMeshVertexBase &v = mesh->mVertexData.getBase(n); + Point3F norm = v.normal(); + Point3F vert = v.vert(); meshMat.mulP( vert ); meshMat.mulV( norm ); @@ -527,17 +528,65 @@ void TSShapeInstance::render( const TSRenderState &rdata, S32 dl, F32 intraDL ) return; } - // run through the meshes S32 start = rdata.isNoRenderNonTranslucent() ? mShape->subShapeFirstTranslucentObject[ss] : mShape->subShapeFirstObject[ss]; - S32 end = rdata.isNoRenderTranslucent() ? mShape->subShapeFirstTranslucentObject[ss] : mShape->subShapeFirstObject[ss] + mShape->subShapeNumObjects[ss]; + S32 end = rdata.isNoRenderTranslucent() ? mShape->subShapeFirstTranslucentObject[ss] : mShape->subShapeFirstObject[ss] + mShape->subShapeNumObjects[ss]; + TSVertexBufferHandle *realBuffer; + + if (TSShape::smUseHardwareSkinning) + { + // For hardware skinning, just using the buffer associated with the shape will work fine + realBuffer = &mShape->mShapeVertexBuffer; + } + else + { + // For software skinning, we need to update our own buffer each frame + realBuffer = &mSoftwareVertexBuffer; + if (realBuffer->getPointer() == NULL) + { + mShape->getVertexBuffer(*realBuffer, GFXBufferTypeDynamic); + } + + if (bufferNeedsUpdate(od, start, end)) + { + U8 *buffer = realBuffer->lock(); + if (!buffer) + return; + + // Base vertex data + dMemcpy(buffer, mShape->mShapeVertexData.base, mShape->mShapeVertexData.size); + + // Apply skinned verts (where applicable) + for (i = start; i < end; i++) + { + mMeshObjects[i].updateVertexBuffer(od, buffer); + } + + realBuffer->unlock(); + } + } + + // run through the meshes for (i=start; inames[ mMeshObjects[i].object->nameIndex ]; - mMeshObjects[i].render( od, mMaterialList, rdata, mAlphaAlways ? mAlphaAlwaysValue : 1.0f ); + const char *name = mShape->names[ mMeshObjects[i].object->nameIndex ]; + mMeshObjects[i].render( od, *realBuffer, mMaterialList, objState, mAlphaAlways ? mAlphaAlwaysValue : 1.0f, name ); } } +bool TSShapeInstance::bufferNeedsUpdate(S32 objectDetail, S32 start, S32 end) +{ + // run through the meshes + for (U32 i = start; igetMeshType() == TSMesh::SkinMeshType ) + { + if (isSkinDirty) + { + static_cast(mesh)->updateSkinBones(*mTransforms, mActiveTransforms); + } + rdata.setNodeTransforms(mActiveTransforms.address(), mActiveTransforms.size()); + } + mesh->render( materials, rdata, isSkinDirty, *mTransforms, - mVertexBuffer, - mPrimitiveBuffer ); + vb, + meshName ); // Update the last render time. mLastTime = currTime; @@ -764,6 +835,33 @@ void TSShapeInstance::MeshObjectInstance::render( S32 objectDetail, GFX->popWorldMatrix(); } +void TSShapeInstance::MeshObjectInstance::updateVertexBuffer(S32 objectDetail, U8 *buffer) +{ + PROFILE_SCOPE(TSShapeInstance_MeshObjectInstance_updateVertexBuffer); + + if (forceHidden || ((visible) <= 0.01f)) + return; + + TSMesh *mesh = getMesh(objectDetail); + if (!mesh) + return; + + // Update the buffer here + if (mesh->getMeshType() == TSMesh::SkinMeshType) + { + static_cast(mesh)->updateSkinBuffer(*mTransforms, buffer); + } + + mLastTime = Sim::getCurrentTime(); +} + +bool TSShapeInstance::MeshObjectInstance::bufferNeedsUpdate( S32 objectDetail ) +{ + TSMesh *mesh = getMesh(objectDetail); + const U32 currTime = Sim::getCurrentTime(); + return mesh && mesh->getMeshType() == TSMesh::SkinMeshType && currTime != mLastTime; +} + TSShapeInstance::MeshObjectInstance::MeshObjectInstance() : meshList(0), object(0), frame(0), matFrame(0), visible(1.0f), forceHidden(false), mLastTime( 0 ) diff --git a/Engine/source/ts/tsShapeInstance.h b/Engine/source/ts/tsShapeInstance.h index 744346850..e133beb00 100644 --- a/Engine/source/ts/tsShapeInstance.h +++ b/Engine/source/ts/tsShapeInstance.h @@ -126,7 +126,11 @@ class TSShapeInstance /// @{ /// Render! This draws the base-textured object. - virtual void render( S32 objectDetail, TSMaterialList *, const TSRenderState &rdata, F32 alpha ); + virtual void render( S32 objectDetail, TSVertexBufferHandle &vb, TSMaterialList *, TSRenderState &rdata, F32 alpha, const char *meshName ); + + /// Updates the vertex buffer data for this mesh (used for software skinning) + virtual void updateVertexBuffer( S32 objectDetail, U8 *buffer ); + virtual bool bufferNeedsUpdate( S32 objectDetail ); /// @} /// @name Collision Routines @@ -157,18 +161,21 @@ class TSShapeInstance /// If true this mesh is forced to be hidden /// regardless of the animation state. bool forceHidden; - - TSVertexBufferHandle mVertexBuffer; - GFXPrimitiveBufferHandle mPrimitiveBuffer; /// The time at which this mesh /// was last rendered. U32 mLastTime; + Vector mActiveTransforms; + MeshObjectInstance(); virtual ~MeshObjectInstance() {} - void render( S32 objectDetail, TSMaterialList *, const TSRenderState &rdata, F32 alpha ); + void render( S32 objectDetail, TSVertexBufferHandle &vb, TSMaterialList *, TSRenderState &rdata, F32 alpha, const char *meshName ); + + void updateVertexBuffer( S32 objectDetail, U8 *buffer ); + + bool bufferNeedsUpdate(S32 objectDetail); /// Gets the mesh with specified detail level TSMesh * getMesh(S32 num) const { return numnumMeshes ? *(meshList+num) : NULL; } @@ -268,7 +275,9 @@ protected: /// equal mShapeResource if it was created from a resource. TSShape *mShape; - + /// Vertex buffer used for software skinning this instance + TSVertexBufferHandle mSoftwareVertexBuffer; + bool mOwnMaterialList; ///< Does this own the material list pointer? bool mAlphaAlways; @@ -488,6 +497,8 @@ protected: void render( const TSRenderState &rdata ); void render( const TSRenderState &rdata, S32 dl, F32 intraDL = 0.0f ); + bool bufferNeedsUpdate(S32 objectDetail, S32 start, S32 end); + void animate() { animate( mCurrentDetailLevel ); } void animate(S32 dl); void animateNodes(S32 ss);