mirror of
https://github.com/TorqueGameEngines/Torque3D.git
synced 2026-01-20 04:34:48 +00:00
Fixed various inspector field editor button icons Fixed drag-n-drop apply logic for material assets Fixed up some convex shape editor material references Fixed tools overlay gui profile opaqueness flag Fixed uvEditor image asset binding/lookups Fixed decal editor preview display Fixed shape editor would you like to save your changes prompt Added additional sanity check for bad material reference in tsMesh Fixed saving of terrain asset when working in editor default level
3484 lines
115 KiB
C++
3484 lines
115 KiB
C++
//-----------------------------------------------------------------------------
|
|
// Copyright (c) 2012 GarageGames, LLC
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
// of this software and associated documentation files (the "Software"), to
|
|
// deal in the Software without restriction, including without limitation the
|
|
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
|
// sell copies of the Software, and to permit persons to whom the Software is
|
|
// furnished to do so, subject to the following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be included in
|
|
// all copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
|
// IN THE SOFTWARE.
|
|
//-----------------------------------------------------------------------------
|
|
|
|
#include "platform/platform.h"
|
|
#include "ts/tsMesh.h"
|
|
|
|
#include "ts/tsMeshIntrinsics.h"
|
|
#include "ts/tsDecal.h"
|
|
#include "ts/tsSortedMesh.h"
|
|
#include "ts/tsShape.h"
|
|
#include "ts/tsShapeInstance.h"
|
|
#include "ts/tsRenderState.h"
|
|
#include "ts/tsMaterialList.h"
|
|
#include "ts/instancingMatHook.h"
|
|
#include "math/mMath.h"
|
|
#include "math/mathIO.h"
|
|
#include "math/mathUtils.h"
|
|
#include "console/console.h"
|
|
#include "scene/sceneObject.h"
|
|
#include "core/bitRender.h"
|
|
#include "collision/convex.h"
|
|
#include "collision/optimizedPolyList.h"
|
|
#include "core/frameAllocator.h"
|
|
#include "platform/profiler.h"
|
|
#include "materials/sceneData.h"
|
|
#include "materials/materialManager.h"
|
|
#include "scene/sceneManager.h"
|
|
#include "scene/sceneRenderState.h"
|
|
#include "materials/matInstance.h"
|
|
#include "materials/materialFeatureTypes.h"
|
|
#include "renderInstance/renderPassManager.h"
|
|
#include "materials/customMaterialDefinition.h"
|
|
#include "gfx/util/triListOpt.h"
|
|
#include "util/triRayCheck.h"
|
|
|
|
#include "opcode/Opcode.h"
|
|
|
|
GFXPrimitiveType drawTypes[] = { GFXTriangleList, GFXTriangleStrip };
|
|
#define getDrawType(a) (drawTypes[a])
|
|
|
|
|
|
// structures used to share data between detail levels...
|
|
// used (and valid) during load only
|
|
Vector<Point3F*> TSMesh::smVertsList;
|
|
Vector<Point3F*> TSMesh::smNormsList;
|
|
Vector<U8*> TSMesh::smEncodedNormsList;
|
|
Vector<Point2F*> TSMesh::smTVertsList;
|
|
Vector<Point2F*> TSMesh::smTVerts2List;
|
|
Vector<ColorI*> TSMesh::smColorsList;
|
|
|
|
Vector<bool> TSMesh::smDataCopied;
|
|
|
|
Vector<MatrixF*> TSSkinMesh::smInitTransformList;
|
|
Vector<S32*> TSSkinMesh::smVertexIndexList;
|
|
Vector<S32*> TSSkinMesh::smBoneIndexList;
|
|
Vector<F32*> TSSkinMesh::smWeightList;
|
|
Vector<S32*> TSSkinMesh::smNodeIndexList;
|
|
|
|
bool TSSkinMesh::smDebugSkinVerts = false;
|
|
|
|
Vector<Point3F> gNormalStore;
|
|
|
|
bool TSMesh::smUseTriangles = false; // convert all primitives to triangle lists on load
|
|
bool TSMesh::smUseOneStrip = true; // join triangle strips into one long strip on load
|
|
S32 TSMesh::smMinStripSize = 1; // smallest number of _faces_ allowed per strip (all else put in tri list)
|
|
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 )
|
|
{
|
|
Point4F p;
|
|
mat->getColumn( 3, &p );
|
|
mat->identity();
|
|
mat->setColumn( 3, p );
|
|
|
|
if ( objScale )
|
|
{
|
|
MatrixF scale( true );
|
|
scale.scale( *objScale );
|
|
mat->mul( scale );
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------
|
|
// TSMesh render methods
|
|
//-----------------------------------------------------
|
|
|
|
void TSMesh::render( TSVertexBufferHandle &instanceVB )
|
|
{
|
|
innerRender(instanceVB, mPB);
|
|
}
|
|
|
|
void TSMesh::innerRender( TSVertexBufferHandle &vb, GFXPrimitiveBufferHandle &pb )
|
|
{
|
|
if ( !vb.isValid() || !pb.isValid() )
|
|
return;
|
|
|
|
GFX->setVertexBuffer( vb );
|
|
GFX->setPrimitiveBuffer( pb );
|
|
|
|
for( U32 p = 0; p < mPrimitives.size(); p++ )
|
|
GFX->drawPrimitive( p );
|
|
}
|
|
|
|
|
|
void TSMesh::render( TSMaterialList *materials,
|
|
const TSRenderState &rdata,
|
|
bool isSkinDirty,
|
|
const Vector<MatrixF> &transforms,
|
|
TSVertexBufferHandle &vertexBuffer,
|
|
const char *meshName)
|
|
{
|
|
// These are only used by TSSkinMesh.
|
|
TORQUE_UNUSED( isSkinDirty );
|
|
TORQUE_UNUSED( transforms );
|
|
|
|
// Pass our shared VB.
|
|
innerRender(materials, rdata, vertexBuffer, mPB, meshName);
|
|
}
|
|
|
|
void TSMesh::innerRender( TSMaterialList *materials, const TSRenderState &rdata, TSVertexBufferHandle &vb, GFXPrimitiveBufferHandle &pb, const char *meshName )
|
|
{
|
|
PROFILE_SCOPE( TSMesh_InnerRender );
|
|
|
|
if( vertsPerFrame <= 0 )
|
|
return;
|
|
|
|
F32 meshVisibility = rdata.getFadeOverride() * mVisibility;
|
|
if ( meshVisibility < VISIBILITY_EPSILON )
|
|
return;
|
|
|
|
const SceneRenderState *state = rdata.getSceneState();
|
|
RenderPassManager *renderPass = state->getRenderPass();
|
|
|
|
MeshRenderInst *coreRI = renderPass->allocInst<MeshRenderInst>();
|
|
coreRI->type = RenderPassManager::RIT_Mesh;
|
|
#ifdef TORQUE_ENABLE_GFXDEBUGEVENTS
|
|
coreRI->meshName = meshName;
|
|
#endif
|
|
|
|
// Pass accumulation texture along.
|
|
coreRI->accuTex = rdata.getAccuTex();
|
|
|
|
const MatrixF &objToWorld = GFX->getWorldMatrix();
|
|
|
|
// Sort by the center point or the bounds.
|
|
if ( rdata.useOriginSort() )
|
|
coreRI->sortDistSq = ( objToWorld.getPosition() - state->getCameraPosition() ).lenSquared();
|
|
else
|
|
{
|
|
Box3F rBox = mBounds;
|
|
objToWorld.mul( rBox );
|
|
coreRI->sortDistSq = rBox.getSqDistanceToPoint( state->getCameraPosition() );
|
|
}
|
|
|
|
if (getFlags(Billboard))
|
|
{
|
|
Point3F camPos = state->getDiffuseCameraPosition();
|
|
Point3F objPos;
|
|
objToWorld.getColumn(3, &objPos);
|
|
Point3F targetVector = camPos - objPos;
|
|
if(getFlags(BillboardZAxis))
|
|
targetVector.z = 0.0f;
|
|
targetVector.normalize();
|
|
MatrixF orient = MathUtils::createOrientFromDir(targetVector);
|
|
orient.setPosition(objPos);
|
|
orient.scale(objToWorld.getScale());
|
|
|
|
coreRI->objectToWorld = renderPass->allocUniqueXform( orient );
|
|
}
|
|
else
|
|
coreRI->objectToWorld = renderPass->allocUniqueXform( objToWorld );
|
|
|
|
coreRI->worldToCamera = renderPass->allocSharedXform(RenderPassManager::View);
|
|
coreRI->projection = renderPass->allocSharedXform(RenderPassManager::Projection);
|
|
|
|
AssertFatal( vb.isValid(), "TSMesh::innerRender() - Got invalid vertex buffer!" );
|
|
AssertFatal( pb.isValid(), "TSMesh::innerRender() - Got invalid primitive buffer!" );
|
|
|
|
coreRI->vertBuff = &vb;
|
|
coreRI->primBuff = &pb;
|
|
coreRI->defaultKey2 = (uintptr_t) coreRI->vertBuff;
|
|
|
|
coreRI->materialHint = rdata.getMaterialHint();
|
|
|
|
coreRI->mCustomShaderData = rdata.getCustomShaderBinding();
|
|
|
|
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();
|
|
|
|
for ( S32 i = 0; i < mPrimitives.size(); i++ )
|
|
{
|
|
const TSDrawPrimitive &draw = mPrimitives[i];
|
|
|
|
// We need to have a material.
|
|
if ( draw.matIndex & TSDrawPrimitive::NoMaterial )
|
|
continue;
|
|
|
|
#ifdef TORQUE_DEBUG_BREAK_INSPECT
|
|
// for inspection if you happen to be running in a debugger and can't do bit
|
|
// operations in your head.
|
|
S32 triangles = draw.matIndex & TSDrawPrimitive::Triangles;
|
|
S32 strip = draw.matIndex & TSDrawPrimitive::Strip;
|
|
S32 fan = draw.matIndex & TSDrawPrimitive::Fan;
|
|
S32 indexed = draw.matIndex & TSDrawPrimitive::Indexed;
|
|
S32 type = draw.matIndex & TSDrawPrimitive::TypeMask;
|
|
TORQUE_UNUSED(triangles);
|
|
TORQUE_UNUSED(strip);
|
|
TORQUE_UNUSED(fan);
|
|
TORQUE_UNUSED(indexed);
|
|
TORQUE_UNUSED(type);
|
|
//define TORQUE_DEBUG_BREAK_INSPECT, and insert debug break here to inspect the above elements at runtime
|
|
#endif
|
|
|
|
const U32 matIndex = draw.matIndex & TSDrawPrimitive::MaterialMask;
|
|
BaseMatInstance *matInst = materials->getMaterialInst( matIndex );
|
|
|
|
#ifndef TORQUE_OS_MAC
|
|
|
|
// Get the instancing material if this mesh qualifies.
|
|
if (mMeshType != SkinMeshType && pb->mPrimitiveArray[i].numVertices < smMaxInstancingVerts )
|
|
if (matInst && !matInst->getFeatures().hasFeature(MFT_HardwareSkinning))
|
|
matInst = InstancingMaterialHook::getInstancingMat( matInst );
|
|
|
|
#endif
|
|
|
|
// If we don't have a material instance after the overload then
|
|
// there is nothing to render... skip this primitive.
|
|
matInst = state->getOverrideMaterial( matInst );
|
|
if ( !matInst || !matInst->isValid() || !matInst->getMaterial())
|
|
continue;
|
|
|
|
// If the material needs lights then gather them
|
|
// here once and set them on the core render inst.
|
|
if ( matInst->isForwardLit() && !coreRI->lights[0] && rdata.getLightQuery() )
|
|
rdata.getLightQuery()->getLights( coreRI->lights, 8 );
|
|
|
|
MeshRenderInst *ri = renderPass->allocInst<MeshRenderInst>();
|
|
*ri = *coreRI;
|
|
|
|
ri->matInst = matInst;
|
|
ri->defaultKey = matInst->getStateHint();
|
|
ri->primBuffIndex = mPrimBufferOffset + i;
|
|
|
|
// Translucent materials need the translucent type.
|
|
if ( matInst->getMaterial()->isTranslucent() && (!(matInst->getMaterial()->isAlphatest() && state->isShadowPass())))
|
|
{
|
|
ri->type = RenderPassManager::RIT_Translucent;
|
|
ri->translucentSort = true;
|
|
}
|
|
|
|
renderPass->addInst( ri );
|
|
}
|
|
}
|
|
|
|
const Point3F * TSMesh::getNormals( S32 firstVert )
|
|
{
|
|
if ( getFlags( UseEncodedNormals ) )
|
|
{
|
|
gNormalStore.setSize( vertsPerFrame );
|
|
for ( S32 i = 0; i < mEncodedNorms.size(); i++ )
|
|
gNormalStore[i] = decodeNormal(mEncodedNorms[ i + firstVert ] );
|
|
|
|
return gNormalStore.address();
|
|
}
|
|
|
|
return &mNorms[firstVert];
|
|
}
|
|
|
|
//-----------------------------------------------------
|
|
// TSMesh collision methods
|
|
//-----------------------------------------------------
|
|
|
|
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 )
|
|
{
|
|
if ( mVertexData.isReady() )
|
|
{
|
|
OptimizedPolyList* opList = dynamic_cast<OptimizedPolyList*>(polyList);
|
|
if ( opList )
|
|
{
|
|
base = opList->mVertexList.size();
|
|
for ( i = 0; i < vertsPerFrame; i++ )
|
|
{
|
|
// Don't use vertex() method as we want to retain the original indices
|
|
OptimizedPolyList::VertIndex vert;
|
|
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.getBase( firstVert ).vert(), mVertexData.getBase( firstVert ).normal() );
|
|
for ( i = 1; i < vertsPerFrame; i++ )
|
|
{
|
|
polyList->addPointAndNormal( mVertexData.getBase( i + firstVert ).vert(), mVertexData.getBase( i + firstVert ).normal() );
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
OptimizedPolyList* opList = dynamic_cast<OptimizedPolyList*>(polyList);
|
|
if ( opList )
|
|
{
|
|
base = opList->mVertexList.size();
|
|
for ( i = 0; i < vertsPerFrame; i++ )
|
|
{
|
|
// Don't use vertex() method as we want to retain the original indices
|
|
OptimizedPolyList::VertIndex vert;
|
|
vert.vertIdx = opList->insertPoint( mVerts[ i + firstVert ] );
|
|
vert.normalIdx = opList->insertNormal( mNorms[ i + firstVert ] );
|
|
vert.uv0Idx = opList->insertUV0( mTverts[ i + firstVert ] );
|
|
if ( hasTVert2 )
|
|
vert.uv1Idx = opList->insertUV1(mTverts[ i + firstVert ] );
|
|
|
|
opList->mVertexList.push_back( vert );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
base = polyList->addPointAndNormal( mVerts[firstVert], mNorms[firstVert] );
|
|
for ( i = 1; i < vertsPerFrame; i++ )
|
|
polyList->addPointAndNormal(mVerts[ i + firstVert ], mNorms[ i + firstVert ] );
|
|
}
|
|
}
|
|
}
|
|
|
|
// add the polys...
|
|
for ( i = 0; i < mPrimitives.size(); i++ )
|
|
{
|
|
TSDrawPrimitive & draw = mPrimitives[i];
|
|
U32 start = draw.start;
|
|
|
|
AssertFatal( draw.matIndex & TSDrawPrimitive::Indexed,"TSMesh::buildPolyList (1)" );
|
|
|
|
U32 matIndex = draw.matIndex & TSDrawPrimitive::MaterialMask;
|
|
BaseMatInstance* material = ( materials ? materials->getMaterialInst( matIndex ) : 0 );
|
|
|
|
// gonna depend on what kind of primitive it is...
|
|
if ( (draw.matIndex & TSDrawPrimitive::TypeMask) == TSDrawPrimitive::Triangles )
|
|
{
|
|
for ( S32 j = 0; j < draw.numElements; )
|
|
{
|
|
U32 idx0 = base + mIndices[start + j + 0];
|
|
U32 idx1 = base + mIndices[start + j + 1];
|
|
U32 idx2 = base + mIndices[start + j + 2];
|
|
polyList->begin(material,surfaceKey++);
|
|
polyList->vertex( idx0 );
|
|
polyList->vertex( idx1 );
|
|
polyList->vertex( idx2 );
|
|
polyList->plane( idx0, idx1, idx2 );
|
|
polyList->end();
|
|
j += 3;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
AssertFatal( (draw.matIndex & TSDrawPrimitive::TypeMask) == TSDrawPrimitive::Strip,"TSMesh::buildPolyList (2)" );
|
|
|
|
U32 idx0 = base + mIndices[start + 0];
|
|
U32 idx1;
|
|
U32 idx2 = base + mIndices[start + 1];
|
|
U32 * nextIdx = &idx1;
|
|
for ( S32 j = 2; j < draw.numElements; j++ )
|
|
{
|
|
*nextIdx = idx2;
|
|
// nextIdx = (j%2)==0 ? &idx0 : &idx1;
|
|
nextIdx = (U32*) ( (dsize_t)nextIdx ^ (dsize_t)&idx0 ^ (dsize_t)&idx1);
|
|
idx2 = base + mIndices[start + j];
|
|
if ( idx0 == idx1 || idx0 == idx2 || idx1 == idx2 )
|
|
continue;
|
|
|
|
polyList->begin( material, surfaceKey++ );
|
|
polyList->vertex( idx0 );
|
|
polyList->vertex( idx1 );
|
|
polyList->vertex( idx2 );
|
|
polyList->plane( idx0, idx1, idx2 );
|
|
polyList->end();
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool TSMesh::getFeatures( S32 frame, const MatrixF& mat, const VectorF&, ConvexFeature* cf, U32& )
|
|
{
|
|
S32 firstVert = vertsPerFrame * frame;
|
|
S32 i;
|
|
S32 base = cf->mVertexList.size();
|
|
|
|
for ( i = 0; i < vertsPerFrame; i++ )
|
|
{
|
|
cf->mVertexList.increment();
|
|
mat.mulP( mVertexData.getBase(firstVert + i).vert(), &cf->mVertexList.last() );
|
|
}
|
|
|
|
// add the polys...
|
|
for ( i = 0; i < mPrimitives.size(); i++ )
|
|
{
|
|
TSDrawPrimitive & draw = mPrimitives[i];
|
|
U32 start = draw.start;
|
|
|
|
AssertFatal( draw.matIndex & TSDrawPrimitive::Indexed,"TSMesh::buildPolyList (1)" );
|
|
|
|
// gonna depend on what kind of primitive it is...
|
|
if ( (draw.matIndex & TSDrawPrimitive::TypeMask) == TSDrawPrimitive::Triangles)
|
|
{
|
|
for ( S32 j = 0; j < draw.numElements; j += 3 )
|
|
{
|
|
PlaneF plane( cf->mVertexList[base + mIndices[start + j + 0]],
|
|
cf->mVertexList[base + mIndices[start + j + 1]],
|
|
cf->mVertexList[base + mIndices[start + j + 2]]);
|
|
|
|
cf->mFaceList.increment();
|
|
|
|
ConvexFeature::Face& lastFace = cf->mFaceList.last();
|
|
lastFace.normal = plane;
|
|
|
|
lastFace.vertex[0] = base + mIndices[start + j + 0];
|
|
lastFace.vertex[1] = base + mIndices[start + j + 1];
|
|
lastFace.vertex[2] = base + mIndices[start + j + 2];
|
|
|
|
for ( U32 l = 0; l < 3; l++ )
|
|
{
|
|
U32 newEdge0, newEdge1;
|
|
U32 zero = base + mIndices[start + j + l];
|
|
U32 one = base + mIndices[start + j + ((l+1)%3)];
|
|
newEdge0 = getMin( zero, one );
|
|
newEdge1 = getMax( zero, one );
|
|
bool found = false;
|
|
for ( S32 k = 0; k < cf->mEdgeList.size(); k++ )
|
|
{
|
|
if ( cf->mEdgeList[k].vertex[0] == newEdge0 &&
|
|
cf->mEdgeList[k].vertex[1] == newEdge1)
|
|
{
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( !found )
|
|
{
|
|
cf->mEdgeList.increment();
|
|
cf->mEdgeList.last().vertex[0] = newEdge0;
|
|
cf->mEdgeList.last().vertex[1] = newEdge1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
AssertFatal( (draw.matIndex & TSDrawPrimitive::TypeMask) == TSDrawPrimitive::Strip,"TSMesh::buildPolyList (2)" );
|
|
|
|
U32 idx0 = base + mIndices[start + 0];
|
|
U32 idx1;
|
|
U32 idx2 = base + mIndices[start + 1];
|
|
U32 * nextIdx = &idx1;
|
|
for ( S32 j = 2; j < draw.numElements; j++ )
|
|
{
|
|
*nextIdx = idx2;
|
|
nextIdx = (U32*) ( (dsize_t)nextIdx ^ (dsize_t)&idx0 ^ (dsize_t)&idx1);
|
|
idx2 = base + mIndices[start + j];
|
|
if ( idx0 == idx1 || idx0 == idx2 || idx1 == idx2 )
|
|
continue;
|
|
|
|
PlaneF plane( cf->mVertexList[idx0],
|
|
cf->mVertexList[idx1],
|
|
cf->mVertexList[idx2] );
|
|
|
|
cf->mFaceList.increment();
|
|
cf->mFaceList.last().normal = plane;
|
|
|
|
cf->mFaceList.last().vertex[0] = idx0;
|
|
cf->mFaceList.last().vertex[1] = idx1;
|
|
cf->mFaceList.last().vertex[2] = idx2;
|
|
|
|
U32 newEdge0, newEdge1;
|
|
newEdge0 = getMin( idx0, idx1 );
|
|
newEdge1 = getMax( idx0, idx1 );
|
|
bool found = false;
|
|
S32 k;
|
|
for ( k = 0; k < cf->mEdgeList.size(); k++ )
|
|
{
|
|
ConvexFeature::Edge currentEdge = cf->mEdgeList[k];
|
|
if (currentEdge.vertex[0] == newEdge0 &&
|
|
currentEdge.vertex[1] == newEdge1)
|
|
{
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( !found )
|
|
{
|
|
cf->mEdgeList.increment();
|
|
cf->mEdgeList.last().vertex[0] = newEdge0;
|
|
cf->mEdgeList.last().vertex[1] = newEdge1;
|
|
}
|
|
|
|
newEdge0 = getMin( idx1, idx2 );
|
|
newEdge1 = getMax( idx1, idx2 );
|
|
found = false;
|
|
for ( k = 0; k < cf->mEdgeList.size(); k++ )
|
|
{
|
|
if ( cf->mEdgeList[k].vertex[0] == newEdge0 &&
|
|
cf->mEdgeList[k].vertex[1] == newEdge1 )
|
|
{
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( !found )
|
|
{
|
|
cf->mEdgeList.increment();
|
|
cf->mEdgeList.last().vertex[0] = newEdge0;
|
|
cf->mEdgeList.last().vertex[1] = newEdge1;
|
|
}
|
|
|
|
newEdge0 = getMin(idx0, idx2);
|
|
newEdge1 = getMax(idx0, idx2);
|
|
found = false;
|
|
for ( k = 0; k < cf->mEdgeList.size(); k++ )
|
|
{
|
|
if ( cf->mEdgeList[k].vertex[0] == newEdge0 &&
|
|
cf->mEdgeList[k].vertex[1] == newEdge1 )
|
|
{
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( !found )
|
|
{
|
|
cf->mEdgeList.increment();
|
|
cf->mEdgeList.last().vertex[0] = newEdge0;
|
|
cf->mEdgeList.last().vertex[1] = newEdge1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
void TSMesh::support( S32 frame, const Point3F &v, F32 *currMaxDP, Point3F *currSupport )
|
|
{
|
|
if ( vertsPerFrame == 0 )
|
|
return;
|
|
|
|
U32 waterMark = FrameAllocator::getWaterMark();
|
|
F32* pDots = (F32*)FrameAllocator::alloc( sizeof(F32) * vertsPerFrame );
|
|
|
|
S32 firstVert = vertsPerFrame * frame;
|
|
m_point3F_bulk_dot( &v.x,
|
|
&mVertexData.getBase(firstVert).vert().x,
|
|
vertsPerFrame,
|
|
mVertexData.vertSize(),
|
|
pDots );
|
|
|
|
F32 localdp = *currMaxDP;
|
|
S32 index = -1;
|
|
|
|
for ( S32 i = 0; i < vertsPerFrame; i++ )
|
|
{
|
|
if ( pDots[i] > localdp )
|
|
{
|
|
localdp = pDots[i];
|
|
index = i;
|
|
}
|
|
}
|
|
|
|
FrameAllocator::setWaterMark(waterMark);
|
|
|
|
if ( index != -1 )
|
|
{
|
|
*currMaxDP = localdp;
|
|
*currSupport = mVertexData.getBase(index + firstVert).vert();
|
|
}
|
|
}
|
|
|
|
bool TSMesh::castRay( S32 frame, const Point3F & start, const Point3F & end, RayInfo * rayInfo, TSMaterialList* materials )
|
|
{
|
|
if ( mPlaneNormals.empty() )
|
|
buildConvexHull(); // if haven't done it yet...
|
|
|
|
// Keep track of startTime and endTime. They start out at just under 0 and just over 1, respectively.
|
|
// As we check against each plane, prune start and end times back to represent current intersection of
|
|
// line with all the planes (or rather with all the half-spaces defined by the planes).
|
|
// But, instead of explicitly keeping track of startTime and endTime, keep track as numerator and denominator
|
|
// so that we can avoid as many divisions as possible.
|
|
|
|
// F32 startTime = -0.01f;
|
|
F32 startNum = -0.01f;
|
|
F32 startDen = 1.00f;
|
|
// F32 endTime = 1.01f;
|
|
F32 endNum = 1.01f;
|
|
F32 endDen = 1.00f;
|
|
|
|
S32 curPlane = 0;
|
|
U32 curMaterial = 0;
|
|
bool found = false;
|
|
|
|
// the following block of code is an optimization...
|
|
// it isn't necessary if the longer version of the main loop is used
|
|
bool tmpFound;
|
|
S32 tmpPlane;
|
|
F32 sgn = -1.0f;
|
|
F32 * pnum = &startNum;
|
|
F32 * pden = &startDen;
|
|
S32 * pplane = &curPlane;
|
|
bool * pfound = &found;
|
|
|
|
S32 startPlane = frame * mPlanesPerFrame;
|
|
for ( S32 i = startPlane; i < startPlane + mPlanesPerFrame; i++ )
|
|
{
|
|
// if start & end outside, no collision
|
|
// if start & end inside, continue
|
|
// if start outside, end inside, or visa versa, find intersection of line with plane
|
|
// then update intersection of line with hull (using startTime and endTime)
|
|
F32 dot1 = mDot(mPlaneNormals[i], start ) - mPlaneConstants[i];
|
|
F32 dot2 = mDot(mPlaneNormals[i], end) - mPlaneConstants[i];
|
|
if ( dot1 * dot2 > 0.0f )
|
|
{
|
|
// same side of the plane...which side -- dot==0 considered inside
|
|
if ( dot1 > 0.0f )
|
|
return false; // start and end outside of this plane, no collision
|
|
|
|
// start and end inside plane, continue
|
|
continue;
|
|
}
|
|
|
|
//AssertFatal( dot1 / ( dot1 - dot2 ) >= 0.0f && dot1 / ( dot1 - dot2 ) <= 1.0f,"TSMesh::castRay (1)" );
|
|
|
|
// find intersection (time) with this plane...
|
|
// F32 time = dot1 / (dot1-dot2);
|
|
F32 num = mFabs( dot1 );
|
|
F32 den = mFabs( dot1 - dot2 );
|
|
|
|
// the following block of code is an optimized version...
|
|
// this can be commented out and the following block of code used instead
|
|
// if debugging a problem in this code, that should probably be done
|
|
// if you want to see how this works, look at the following block of code,
|
|
// not this one...
|
|
// Note that this does not get optimized appropriately...it is included this way
|
|
// as an idea for future optimization.
|
|
if ( sgn * dot1 >= 0 )
|
|
{
|
|
sgn *= -1.0f;
|
|
pnum = (F32*) ((dsize_t)pnum ^ (dsize_t)&endNum ^ (dsize_t)&startNum);
|
|
pden = (F32*) ((dsize_t)pden ^ (dsize_t)&endDen ^ (dsize_t)&startDen);
|
|
pplane = (S32*) ((dsize_t)pplane ^ (dsize_t)&tmpPlane ^ (dsize_t)&curPlane);
|
|
pfound = (bool*) ((dsize_t)pfound ^ (dsize_t)&tmpFound ^ (dsize_t)&found);
|
|
}
|
|
|
|
bool noCollision = num * endDen * sgn < endNum * den * sgn && num * startDen * sgn < startNum * den * sgn;
|
|
if (num * *pden * sgn < *pnum * den * sgn && !noCollision)
|
|
{
|
|
*pnum = num;
|
|
*pden = den;
|
|
*pplane = i;
|
|
*pfound = true;
|
|
}
|
|
else if ( noCollision )
|
|
return false;
|
|
|
|
// if (dot1<=0.0f)
|
|
// {
|
|
// // start is inside plane, end is outside...chop off end
|
|
// if (num*endDen<endNum*den) // if (time<endTime)
|
|
// {
|
|
// if (num*startDen<startNum*den) //if (time<startTime)
|
|
// // no intersection of line and hull
|
|
// return false;
|
|
// // endTime = time;
|
|
// endNum = num;
|
|
// endDen = den;
|
|
// }
|
|
// // else, no need to do anything, just continue (we've been more inside than this)
|
|
// }
|
|
// else // dot2<=0.0f
|
|
// {
|
|
// // end is inside poly, start is outside...chop off start
|
|
// AssertFatal(dot2<=0.0f,"TSMesh::castRay (2)");
|
|
// if (num*startDen>startNum*den) // if (time>startTime)
|
|
// {
|
|
// if (num*endDen>endNum*den) //if (time>endTime)
|
|
// // no intersection of line and hull
|
|
// return false;
|
|
// // startTime = time;
|
|
// startNum = num;
|
|
// startDen = den;
|
|
// curPlane = i;
|
|
// curMaterial = planeMaterials[i-startPlane];
|
|
// found = true;
|
|
// }
|
|
// // else, no need to do anything, just continue (we've been more inside than this)
|
|
// }
|
|
}
|
|
|
|
// setup rayInfo
|
|
if ( found && rayInfo )
|
|
{
|
|
curMaterial = mPlaneMaterials[ curPlane - startPlane ];
|
|
|
|
rayInfo->t = (F32)startNum/(F32)startDen; // finally divide...
|
|
rayInfo->normal = mPlaneNormals[curPlane];
|
|
|
|
if (materials && materials->size() > 0)
|
|
rayInfo->material = materials->getMaterialInst( curMaterial );
|
|
else
|
|
rayInfo->material = NULL;
|
|
|
|
rayInfo->setContactPoint( start, end );
|
|
|
|
return true;
|
|
}
|
|
else if ( found )
|
|
return true;
|
|
|
|
// only way to get here is if start is inside hull...
|
|
// we could return null and just plug in garbage for the material and normal...
|
|
return false;
|
|
}
|
|
|
|
bool TSMesh::castRayRendered( S32 frame, const Point3F & start, const Point3F & end, RayInfo * rayInfo, TSMaterialList* materials )
|
|
{
|
|
if( vertsPerFrame <= 0 )
|
|
return false;
|
|
|
|
if( mNumVerts == 0 )
|
|
return false;
|
|
|
|
S32 firstVert = vertsPerFrame * frame;
|
|
|
|
bool found = false;
|
|
F32 best_t = F32_MAX;
|
|
U32 bestIdx0 = 0, bestIdx1 = 0, bestIdx2 = 0;
|
|
BaseMatInstance* bestMaterial = NULL;
|
|
Point3F dir = end - start;
|
|
|
|
for ( S32 i = 0; i < mPrimitives.size(); i++ )
|
|
{
|
|
TSDrawPrimitive & draw = mPrimitives[i];
|
|
U32 drawStart = draw.start;
|
|
|
|
AssertFatal( draw.matIndex & TSDrawPrimitive::Indexed,"TSMesh::castRayRendered (1)" );
|
|
|
|
U32 matIndex = draw.matIndex & TSDrawPrimitive::MaterialMask;
|
|
BaseMatInstance* material = ( materials ? materials->getMaterialInst( matIndex ) : 0 );
|
|
|
|
U32 idx0, idx1, idx2;
|
|
|
|
// gonna depend on what kind of primitive it is...
|
|
if ( (draw.matIndex & TSDrawPrimitive::TypeMask) == TSDrawPrimitive::Triangles )
|
|
{
|
|
for ( S32 j = 0; j < draw.numElements-2; j += 3 )
|
|
{
|
|
idx0 = mIndices[drawStart + j + 0];
|
|
idx1 = mIndices[drawStart + j + 1];
|
|
idx2 = mIndices[drawStart + j + 2];
|
|
|
|
F32 cur_t = 0;
|
|
Point2F 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)
|
|
{
|
|
best_t = cur_t;
|
|
bestIdx0 = idx0;
|
|
bestIdx1 = idx1;
|
|
bestIdx2 = idx2;
|
|
bestMaterial = material;
|
|
found = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
AssertFatal( (draw.matIndex & TSDrawPrimitive::TypeMask) == TSDrawPrimitive::Strip,"TSMesh::castRayRendered (2)" );
|
|
|
|
idx0 = mIndices[drawStart + 0];
|
|
idx2 = mIndices[drawStart + 1];
|
|
U32 * nextIdx = &idx1;
|
|
for ( S32 j = 2; j < draw.numElements; j++ )
|
|
{
|
|
*nextIdx = idx2;
|
|
// nextIdx = (j%2)==0 ? &idx0 : &idx1;
|
|
nextIdx = (U32*) ( (dsize_t)nextIdx ^ (dsize_t)&idx0 ^ (dsize_t)&idx1);
|
|
idx2 = mIndices[drawStart + j];
|
|
if ( idx0 == idx1 || idx0 == idx2 || idx1 == idx2 )
|
|
continue;
|
|
|
|
F32 cur_t = 0;
|
|
Point2F 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)
|
|
{
|
|
best_t = cur_t;
|
|
bestIdx0 = firstVert + idx0;
|
|
bestIdx1 = firstVert + idx1;
|
|
bestIdx2 = firstVert + idx2;
|
|
bestMaterial = material;
|
|
found = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// setup rayInfo
|
|
if ( found && rayInfo )
|
|
{
|
|
rayInfo->t = best_t;
|
|
|
|
Point3F 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.getBase(bestIdx0).vert() - mVertexData.getBase(bestIdx1).vert(), mVertexData.getBase(bestIdx2).vert() - mVertexData.getBase(bestIdx1).vert(), &normal );
|
|
if ( mDot( normal, normal ) < 0.001f )
|
|
{
|
|
mCross( mVertexData.getBase(bestIdx1).vert() - mVertexData.getBase(bestIdx2).vert(), mVertexData.getBase(bestIdx0).vert() - mVertexData.getBase(bestIdx2).vert(), &normal );
|
|
}
|
|
}
|
|
normal.normalize();
|
|
rayInfo->normal = normal;
|
|
|
|
rayInfo->material = bestMaterial;
|
|
|
|
rayInfo->setContactPoint( start, end );
|
|
|
|
return true;
|
|
}
|
|
else if ( found )
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
bool TSMesh::addToHull( U32 idx0, U32 idx1, U32 idx2 )
|
|
{
|
|
// calculate the normal of this triangle... remember, we lose precision
|
|
// when we subtract two large numbers that are very close to each other,
|
|
// so depending on how we calculate the normal, we could get a
|
|
// different result. so, we will calculate the normal three different
|
|
// ways and take the one that gives us the largest vector before we
|
|
// normalize.
|
|
Point3F normal1, normal2, 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);
|
|
if (magSquared > greatestMagSquared)
|
|
{
|
|
normal = normal2;
|
|
greatestMagSquared = magSquared;
|
|
}
|
|
magSquared = mDot(normal3, normal3);
|
|
if (magSquared > greatestMagSquared)
|
|
{
|
|
normal = normal3;
|
|
greatestMagSquared = magSquared;
|
|
}
|
|
if (mDot(normal, normal) < 0.00000001f)
|
|
return false;
|
|
|
|
normal.normalize();
|
|
F32 k = mDot( normal, mVertexData.getBase(idx0).vert() );
|
|
for ( S32 i = 0; i < mPlaneNormals.size(); i++ )
|
|
{
|
|
if ( mDot(mPlaneNormals[i], normal ) > 0.99f && mFabs( k- mPlaneConstants[i] ) < 0.01f )
|
|
return false; // this is a repeat...
|
|
}
|
|
// new plane, add it to the list...
|
|
mPlaneNormals.push_back( normal );
|
|
mPlaneConstants.push_back( k );
|
|
return true;
|
|
}
|
|
|
|
bool TSMesh::buildConvexHull()
|
|
{
|
|
// already done, return without error
|
|
if (mPlaneNormals.size() )
|
|
return true;
|
|
|
|
bool error = false;
|
|
|
|
// should probably only have 1 frame, but just in case...
|
|
mPlanesPerFrame = 0;
|
|
S32 frame, i, j;
|
|
for ( frame = 0; frame < numFrames; frame++ )
|
|
{
|
|
S32 firstVert = vertsPerFrame * frame;
|
|
S32 firstPlane = mPlaneNormals.size();
|
|
for ( i = 0; i < mPrimitives.size(); i++ )
|
|
{
|
|
TSDrawPrimitive & draw = mPrimitives[i];
|
|
U32 start = draw.start;
|
|
|
|
AssertFatal( draw.matIndex & TSDrawPrimitive::Indexed,"TSMesh::buildConvexHull (1)" );
|
|
|
|
// gonna depend on what kind of primitive it is...
|
|
if ( (draw.matIndex & TSDrawPrimitive::TypeMask) == TSDrawPrimitive::Triangles )
|
|
{
|
|
for ( j = 0; j < draw.numElements; j += 3 )
|
|
if ( addToHull( mIndices[start + j + 0] + firstVert,
|
|
mIndices[start + j + 1] + firstVert,
|
|
mIndices[start + j + 2] + firstVert ) && frame == 0 )
|
|
mPlaneMaterials.push_back( draw.matIndex & TSDrawPrimitive::MaterialMask );
|
|
}
|
|
else
|
|
{
|
|
AssertFatal( (draw.matIndex&TSDrawPrimitive::Strip) == TSDrawPrimitive::Strip,"TSMesh::buildConvexHull (2)" );
|
|
|
|
U32 idx0 = mIndices[start + 0] + firstVert;
|
|
U32 idx1;
|
|
U32 idx2 = mIndices[start + 1] + firstVert;
|
|
U32 * nextIdx = &idx1;
|
|
for ( j = 2; j < draw.numElements; j++ )
|
|
{
|
|
*nextIdx = idx2;
|
|
// nextIdx = (j%2)==0 ? &idx0 : &idx1;
|
|
nextIdx = (U32*) ( (dsize_t)nextIdx ^ (dsize_t)&idx0 ^ (dsize_t)&idx1 );
|
|
idx2 = mIndices[start + j] + firstVert;
|
|
if ( addToHull( idx0, idx1, idx2 ) && frame == 0 )
|
|
mPlaneMaterials.push_back( draw.matIndex & TSDrawPrimitive::MaterialMask );
|
|
}
|
|
}
|
|
}
|
|
// make sure all the verts on this frame are inside all the planes
|
|
for ( i = 0; i < vertsPerFrame; i++ )
|
|
for ( j = firstPlane; j < mPlaneNormals.size(); j++ )
|
|
if ( mDot( mVertexData.getBase(firstVert + i).vert(), mPlaneNormals[j] ) - mPlaneConstants[j] < 0.01 ) // .01 == a little slack
|
|
error = true;
|
|
|
|
if ( frame == 0 )
|
|
mPlanesPerFrame = mPlaneNormals.size();
|
|
|
|
if ( (frame + 1) * mPlanesPerFrame != mPlaneNormals.size() )
|
|
{
|
|
// eek, not all frames have same number of planes...
|
|
while ( (frame + 1) * mPlanesPerFrame > mPlaneNormals.size() )
|
|
{
|
|
// we're short, duplicate last plane till we match
|
|
U32 sz = mPlaneNormals.size();
|
|
mPlaneNormals.increment();
|
|
mPlaneNormals.last() = mPlaneNormals[sz-1];
|
|
mPlaneConstants.increment();
|
|
mPlaneConstants.last() = mPlaneConstants[sz-1];
|
|
}
|
|
while ( (frame + 1) * mPlanesPerFrame < mPlaneNormals.size() )
|
|
{
|
|
// harsh -- last frame has more than other frames
|
|
// duplicate last plane in each frame
|
|
for ( S32 k = frame - 1; k >= 0; k-- )
|
|
{
|
|
mPlaneNormals.insert( k * mPlanesPerFrame + mPlanesPerFrame );
|
|
mPlaneNormals[k * mPlanesPerFrame + mPlanesPerFrame] = mPlaneNormals[k * mPlanesPerFrame + mPlanesPerFrame - 1];
|
|
mPlaneConstants.insert( k * mPlanesPerFrame + mPlanesPerFrame );
|
|
mPlaneConstants[k * mPlanesPerFrame + mPlanesPerFrame] = mPlaneConstants[k * mPlanesPerFrame + mPlanesPerFrame - 1];
|
|
if ( k == 0 )
|
|
{
|
|
mPlaneMaterials.increment();
|
|
mPlaneMaterials.last() = mPlaneMaterials[mPlaneMaterials.size() - 2];
|
|
}
|
|
}
|
|
mPlanesPerFrame++;
|
|
}
|
|
}
|
|
AssertFatal( (frame + 1) * mPlanesPerFrame == mPlaneNormals.size(),"TSMesh::buildConvexHull (3)" );
|
|
}
|
|
return !error;
|
|
}
|
|
|
|
//-----------------------------------------------------
|
|
// TSMesh bounds methods
|
|
//-----------------------------------------------------
|
|
|
|
void TSMesh::computeBounds()
|
|
{
|
|
MatrixF mat(true);
|
|
computeBounds( mat, mBounds, -1, &mCenter, &mRadius );
|
|
}
|
|
|
|
void TSMesh::computeBounds( const MatrixF &transform, Box3F &bounds, S32 frame, Point3F *center, F32 *radius )
|
|
{
|
|
const Point3F *baseVert = NULL;
|
|
S32 stride = 0;
|
|
S32 numVerts = 0;
|
|
|
|
AssertFatal(!mVertexData.isReady() || (mVertexData.isReady() && mNumVerts == mVertexData.size() && mNumVerts == vertsPerFrame), "vertex number mismatch");
|
|
|
|
if(mVerts.size() == 0 && mVertexData.isReady() && mVertexData.size() > 0)
|
|
{
|
|
baseVert = &mVertexData.getBase(0).vert();
|
|
stride = mVertexData.vertSize();
|
|
|
|
if ( frame < 0 )
|
|
numVerts = mNumVerts;
|
|
else
|
|
{
|
|
baseVert = &mVertexData.getBase(frame * vertsPerFrame).vert();
|
|
numVerts = vertsPerFrame;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
baseVert = mVerts.address();
|
|
stride = sizeof(Point3F);
|
|
|
|
if ( frame < 0 )
|
|
numVerts = mVerts.size();
|
|
else
|
|
{
|
|
baseVert += frame * vertsPerFrame;
|
|
numVerts = vertsPerFrame;
|
|
}
|
|
}
|
|
computeBounds( baseVert, numVerts, stride, transform, bounds, center, radius );
|
|
}
|
|
|
|
void TSMesh::computeBounds( const Point3F *v, S32 numVerts, S32 stride, const MatrixF &transform, Box3F &bounds, Point3F *center, F32 *radius )
|
|
{
|
|
const U8 *_vb = reinterpret_cast<const U8 *>(v);
|
|
|
|
if ( !numVerts )
|
|
{
|
|
bounds.minExtents = Point3F::Zero;
|
|
bounds.maxExtents = Point3F::Zero;
|
|
if ( center )
|
|
*center = Point3F::Zero;
|
|
if ( radius )
|
|
*radius = 0;
|
|
return;
|
|
}
|
|
|
|
S32 i;
|
|
Point3F p;
|
|
transform.mulP( *v, &bounds.minExtents );
|
|
bounds.maxExtents = bounds.minExtents;
|
|
for ( i = 0; i < numVerts; i++ )
|
|
{
|
|
const Point3F &curVert = *reinterpret_cast<const Point3F *>(_vb + i * stride);
|
|
transform.mulP( curVert, &p );
|
|
bounds.maxExtents.setMax( p );
|
|
bounds.minExtents.setMin( p );
|
|
}
|
|
Point3F c;
|
|
if ( !center )
|
|
center = &c;
|
|
center->x = 0.5f * (bounds.minExtents.x + bounds.maxExtents.x);
|
|
center->y = 0.5f * (bounds.minExtents.y + bounds.maxExtents.y);
|
|
center->z = 0.5f * (bounds.minExtents.z + bounds.maxExtents.z);
|
|
if ( radius )
|
|
{
|
|
*radius = 0.0f;
|
|
for ( i = 0; i < numVerts; i++ )
|
|
{
|
|
const Point3F &curVert = *reinterpret_cast<const Point3F *>(_vb + i * stride);
|
|
transform.mulP( curVert, &p );
|
|
p -= *center;
|
|
*radius = getMax( *radius, mDot( p, p ) );
|
|
}
|
|
*radius = mSqrt( *radius );
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------
|
|
|
|
S32 TSMesh::getNumPolys() const
|
|
{
|
|
S32 count = 0;
|
|
for ( S32 i = 0; i < mPrimitives.size(); i++ )
|
|
{
|
|
switch (mPrimitives[i].matIndex & TSDrawPrimitive::TypeMask)
|
|
{
|
|
case TSDrawPrimitive::Triangles:
|
|
count += mPrimitives[i].numElements / 3;
|
|
break;
|
|
|
|
case TSDrawPrimitive::Fan:
|
|
count += mPrimitives[i].numElements - 2;
|
|
break;
|
|
|
|
case TSDrawPrimitive::Strip:
|
|
// Don't count degenerate triangles
|
|
for ( S32 j = mPrimitives[i].start;
|
|
j < mPrimitives[i].start+ mPrimitives[i].numElements-2;
|
|
j++ )
|
|
{
|
|
if ((mIndices[j] != mIndices[j+1]) &&
|
|
(mIndices[j] != mIndices[j+2]) &&
|
|
(mIndices[j+1] != mIndices[j+2]))
|
|
count++;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
return count;
|
|
}
|
|
|
|
//-----------------------------------------------------
|
|
|
|
TSMesh::TSMesh() : mMeshType( StandardMeshType )
|
|
{
|
|
VECTOR_SET_ASSOCIATION(mPlaneNormals );
|
|
VECTOR_SET_ASSOCIATION(mPlaneConstants );
|
|
VECTOR_SET_ASSOCIATION(mPlaneMaterials );
|
|
mParentMesh = -1;
|
|
|
|
mOptTree = NULL;
|
|
mOpMeshInterface = NULL;
|
|
mOpTris = NULL;
|
|
mOpPoints = NULL;
|
|
|
|
mVisibility = 1.0f;
|
|
|
|
|
|
mNumVerts = 0;
|
|
mVertSize = 0;
|
|
mVertOffset = 0;
|
|
mRadius = 0.0f;
|
|
mVertexFormat = NULL;
|
|
mPrimBufferOffset = 0;
|
|
numFrames = 0;
|
|
numMatFrames = 0;
|
|
vertsPerFrame = 0;
|
|
mPlanesPerFrame = 0;
|
|
mMergeBufferStart = 0;
|
|
mParentMeshObject = NULL;
|
|
}
|
|
|
|
//-----------------------------------------------------
|
|
// TSMesh destructor
|
|
//-----------------------------------------------------
|
|
|
|
TSMesh::~TSMesh()
|
|
{
|
|
SAFE_DELETE( mOptTree );
|
|
SAFE_DELETE( mOpMeshInterface );
|
|
SAFE_DELETE_ARRAY( mOpTris );
|
|
SAFE_DELETE_ARRAY( mOpPoints );
|
|
|
|
mNumVerts = 0;
|
|
}
|
|
|
|
//-----------------------------------------------------
|
|
// TSSkinMesh methods
|
|
//-----------------------------------------------------
|
|
|
|
void TSSkinMesh::updateSkinBuffer( const Vector<MatrixF> &transforms, U8* buffer )
|
|
{
|
|
PROFILE_SCOPE(TSSkinMesh_UpdateSkinBuffer);
|
|
|
|
AssertFatal(batchData.initialized, "Batch data not initialized. Call createSkinBatchData() before any skin update is called.");
|
|
|
|
if (TSShape::smUseHardwareSkinning || mNumVerts == 0)
|
|
return;
|
|
|
|
const MatrixF *matrices = NULL;
|
|
|
|
static Vector<MatrixF> sBoneTransforms;
|
|
sBoneTransforms.setSize(batchData.nodeIndex.size());
|
|
|
|
// set up bone transforms
|
|
PROFILE_START(TSSkinMesh_UpdateTransforms);
|
|
for (S32 i = 0; i < batchData.nodeIndex.size(); i++)
|
|
{
|
|
S32 node = batchData.nodeIndex[i];
|
|
sBoneTransforms[i].mul(transforms[node], batchData.initialTransforms[i]);
|
|
}
|
|
|
|
matrices = &sBoneTransforms[0];
|
|
PROFILE_END();
|
|
|
|
const Point3F *inVerts = batchData.initialVerts.address();
|
|
const Point3F *inNorms = batchData.initialNorms.address();
|
|
|
|
AssertFatal(inVerts, "Something went wrong, verts should be valid");
|
|
|
|
U8 *dest = buffer + mVertOffset;
|
|
if (!dest)
|
|
return;
|
|
|
|
Point3F srcVtx, srcNrm;
|
|
|
|
AssertFatal(batchData.vertexBatchOperations.size() == batchData.initialVerts.size(), "Assumption failed!");
|
|
|
|
Point3F skinnedVert;
|
|
Point3F skinnedNorm;
|
|
|
|
for (Vector<BatchData::BatchedVertex>::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<MatrixF> &transforms, Vector<MatrixF>& destTransforms )
|
|
{
|
|
// Update transforms for current mesh
|
|
destTransforms.setSize(batchData.nodeIndex.size());
|
|
|
|
for (int i = 0; i<batchData.nodeIndex.size(); i++)
|
|
{
|
|
S32 node = batchData.nodeIndex[i];
|
|
|
|
if (node >= 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<BatchData::BatchedVertex> 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 = mNorms;
|
|
batchData.initialVerts = mVerts;
|
|
}
|
|
|
|
// Build the batch operations
|
|
while( curVtx != endVtx )
|
|
{
|
|
const S32 vidx = *curVtx;
|
|
++curVtx;
|
|
|
|
const S32 midx = *curBone;
|
|
++curBone;
|
|
|
|
const F32 w = *curWeight;
|
|
++curWeight;
|
|
|
|
// Ignore empty weights
|
|
if ( vidx < 0 || midx < 0 || w == 0 )
|
|
continue;
|
|
|
|
if( !batchOperations.empty() &&
|
|
batchOperations.last().vertexIndex == vidx )
|
|
{
|
|
AssertFatal( batchOperations.last().transformCount > 0, "Not sure how this happened!" );
|
|
|
|
S32 opIdx = batchOperations.last().transformCount++;
|
|
|
|
// Limit the number of weights per bone (keep the N largest influences)
|
|
if ( opIdx >= TSSkinMesh::BatchData::maxBonePerVert )
|
|
{
|
|
if ( !issuedWeightWarning )
|
|
{
|
|
issuedWeightWarning = true;
|
|
Con::warnf( "At least one vertex has too many bone weights - limiting "
|
|
"to the largest %d influences (see maxBonePerVert in tsMesh.h).",
|
|
TSSkinMesh::BatchData::maxBonePerVert );
|
|
}
|
|
|
|
// Too many weights => find and replace the smallest one
|
|
S32 minIndex = 0;
|
|
F32 minWeight = batchOperations.last().transform[0].weight;
|
|
for ( S32 i = 1; i < batchOperations.last().transformCount; i++ )
|
|
{
|
|
if ( batchOperations.last().transform[i].weight < minWeight )
|
|
{
|
|
minWeight = batchOperations.last().transform[i].weight;
|
|
minIndex = i;
|
|
}
|
|
}
|
|
|
|
opIdx = minIndex;
|
|
batchOperations.last().transformCount = TSSkinMesh::BatchData::maxBonePerVert;
|
|
}
|
|
|
|
batchOperations.last().transform[opIdx].transformIndex = midx;
|
|
batchOperations.last().transform[opIdx].weight = w;
|
|
}
|
|
else
|
|
{
|
|
batchOperations.increment();
|
|
batchOperations.last().vertexIndex = vidx;
|
|
batchOperations.last().transformCount = 1;
|
|
|
|
batchOperations.last().transform[0].transformIndex = midx;
|
|
batchOperations.last().transform[0].weight = w;
|
|
}
|
|
//Con::printf( "[%d] transform idx %d, weight %1.5f", vidx, midx, w );
|
|
}
|
|
//Con::printf("End skin update");
|
|
|
|
// Normalize vertex weights (force weights for each vert to sum to 1)
|
|
if ( issuedWeightWarning )
|
|
{
|
|
for ( S32 i = 0; i < batchOperations.size(); i++ )
|
|
{
|
|
BatchData::BatchedVertex& batchOp = batchOperations[i];
|
|
|
|
// Sum weights for this vertex
|
|
F32 invTotalWeight = 0;
|
|
for ( S32 j = 0; j < batchOp.transformCount; j++ )
|
|
invTotalWeight += batchOp.transform[j].weight;
|
|
|
|
// Then normalize the vertex weights
|
|
invTotalWeight = 1.0f / invTotalWeight;
|
|
for ( S32 j = 0; j < batchOp.transformCount; j++ )
|
|
batchOp.transform[j].weight *= invTotalWeight;
|
|
}
|
|
}
|
|
|
|
batchData.vertexBatchOperations.set(batchOperations.address(), batchOperations.size());
|
|
|
|
U32 maxValue = 0;
|
|
for (U32 i = 0; i<batchData.vertexBatchOperations.size(); i++)
|
|
{
|
|
maxValue = batchData.vertexBatchOperations[i].transformCount > maxValue ? batchData.vertexBatchOperations[i].transformCount : maxValue;
|
|
}
|
|
maxBones = maxValue;
|
|
}
|
|
|
|
void TSSkinMesh::setupVertexTransforms()
|
|
{
|
|
AssertFatal(mVertexData.vertSize() == mVertSize, "vert size mismatch");
|
|
|
|
// Generate the bone transforms for the verts
|
|
for( Vector<BatchData::BatchedVertex>::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);
|
|
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,
|
|
const TSRenderState &rdata,
|
|
bool isSkinDirty,
|
|
const Vector<MatrixF> &transforms,
|
|
TSVertexBufferHandle &vertexBuffer,
|
|
const char *meshName )
|
|
{
|
|
PROFILE_SCOPE(TSSkinMesh_render);
|
|
|
|
if (mNumVerts == 0)
|
|
return;
|
|
|
|
// verify stuff first
|
|
AssertFatal(mVertexData.size() == mNumVerts, "Vert # mismatch");
|
|
AssertFatal((TSShape::smUseHardwareSkinning && vertexBuffer == mVB) || (!TSShape::smUseHardwareSkinning), "Vertex buffer mismatch");
|
|
|
|
// render...
|
|
innerRender(materials, rdata, vertexBuffer, mPB, meshName);
|
|
}
|
|
|
|
bool TSSkinMesh::buildPolyList( S32 frame, AbstractPolyList *polyList, U32 &surfaceKey, TSMaterialList *materials )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool TSSkinMesh::castRay( S32 frame, const Point3F &start, const Point3F &end, RayInfo *rayInfo, TSMaterialList *materials )
|
|
{
|
|
TORQUE_UNUSED(frame);
|
|
TORQUE_UNUSED(start);
|
|
TORQUE_UNUSED(end);
|
|
TORQUE_UNUSED(rayInfo);
|
|
TORQUE_UNUSED(materials);
|
|
|
|
return false;
|
|
}
|
|
|
|
bool TSSkinMesh::buildConvexHull()
|
|
{
|
|
return false; // no error, but we don't do anything either...
|
|
}
|
|
|
|
void TSSkinMesh::computeBounds( const MatrixF &transform, Box3F &bounds, S32 frame, Point3F *center, F32 *radius )
|
|
{
|
|
TORQUE_UNUSED(frame);
|
|
|
|
if (mVerts.size() != 0)
|
|
{
|
|
// Use unskinned verts
|
|
TSMesh::computeBounds(mVerts.address(), mVerts.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
|
|
{
|
|
Point3F *vertStart = reinterpret_cast<Point3F *>(mVertexData.address());
|
|
TSMesh::computeBounds( vertStart, mVertexData.size(), mVertexData.vertSize(), transform, bounds, center, radius );
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------
|
|
// encoded normals
|
|
//-----------------------------------------------------
|
|
|
|
const Point3F TSMesh::smU8ToNormalTable[] =
|
|
{
|
|
Point3F( 0.565061f, -0.270644f, -0.779396f ),
|
|
Point3F( -0.309804f, -0.731114f, 0.607860f ),
|
|
Point3F( -0.867412f, 0.472957f, 0.154619f ),
|
|
Point3F( -0.757488f, 0.498188f, -0.421925f ),
|
|
Point3F( 0.306834f, -0.915340f, 0.260778f ),
|
|
Point3F( 0.098754f, 0.639153f, -0.762713f ),
|
|
Point3F( 0.713706f, -0.558862f, -0.422252f ),
|
|
Point3F( -0.890431f, -0.407603f, -0.202466f ),
|
|
Point3F( 0.848050f, -0.487612f, -0.207475f ),
|
|
Point3F( -0.232226f, 0.776855f, 0.585293f ),
|
|
Point3F( -0.940195f, 0.304490f, -0.152706f ),
|
|
Point3F( 0.602019f, -0.491878f, -0.628991f ),
|
|
Point3F( -0.096835f, -0.494354f, -0.863850f ),
|
|
Point3F( 0.026630f, -0.323659f, -0.945799f ),
|
|
Point3F( 0.019208f, 0.909386f, 0.415510f ),
|
|
Point3F( 0.854440f, 0.491730f, 0.167731f ),
|
|
Point3F( -0.418835f, 0.866521f, -0.271512f ),
|
|
Point3F( 0.465024f, 0.409667f, 0.784809f ),
|
|
Point3F( -0.674391f, -0.691087f, -0.259992f ),
|
|
Point3F( 0.303858f, -0.869270f, -0.389922f ),
|
|
Point3F( 0.991333f, 0.090061f, -0.095640f ),
|
|
Point3F( -0.275924f, -0.369550f, 0.887298f ),
|
|
Point3F( 0.426545f, -0.465962f, 0.775202f ),
|
|
Point3F( -0.482741f, -0.873278f, -0.065920f ),
|
|
Point3F( 0.063616f, 0.932012f, -0.356800f ),
|
|
Point3F( 0.624786f, -0.061315f, 0.778385f ),
|
|
Point3F( -0.530300f, 0.416850f, 0.738253f ),
|
|
Point3F( 0.312144f, -0.757028f, -0.573999f ),
|
|
Point3F( 0.399288f, -0.587091f, -0.704197f ),
|
|
Point3F( -0.132698f, 0.482877f, 0.865576f ),
|
|
Point3F( 0.950966f, 0.306530f, 0.041268f ),
|
|
Point3F( -0.015923f, -0.144300f, 0.989406f ),
|
|
Point3F( -0.407522f, -0.854193f, 0.322925f ),
|
|
Point3F( -0.932398f, 0.220464f, 0.286408f ),
|
|
Point3F( 0.477509f, 0.876580f, 0.059936f ),
|
|
Point3F( 0.337133f, 0.932606f, -0.128796f ),
|
|
Point3F( -0.638117f, 0.199338f, 0.743687f ),
|
|
Point3F( -0.677454f, 0.445349f, 0.585423f ),
|
|
Point3F( -0.446715f, 0.889059f, -0.100099f ),
|
|
Point3F( -0.410024f, 0.909168f, 0.072759f ),
|
|
Point3F( 0.708462f, 0.702103f, -0.071641f ),
|
|
Point3F( -0.048801f, -0.903683f, -0.425411f ),
|
|
Point3F( -0.513681f, -0.646901f, 0.563606f ),
|
|
Point3F( -0.080022f, 0.000676f, -0.996793f ),
|
|
Point3F( 0.066966f, -0.991150f, -0.114615f ),
|
|
Point3F( -0.245220f, 0.639318f, -0.728793f ),
|
|
Point3F( 0.250978f, 0.855979f, 0.452006f ),
|
|
Point3F( -0.123547f, 0.982443f, -0.139791f ),
|
|
Point3F( -0.794825f, 0.030254f, -0.606084f ),
|
|
Point3F( -0.772905f, 0.547941f, 0.319967f ),
|
|
Point3F( 0.916347f, 0.369614f, -0.153928f ),
|
|
Point3F( -0.388203f, 0.105395f, 0.915527f ),
|
|
Point3F( -0.700468f, -0.709334f, 0.078677f ),
|
|
Point3F( -0.816193f, 0.390455f, 0.425880f ),
|
|
Point3F( -0.043007f, 0.769222f, -0.637533f ),
|
|
Point3F( 0.911444f, 0.113150f, 0.395560f ),
|
|
Point3F( 0.845801f, 0.156091f, -0.510153f ),
|
|
Point3F( 0.829801f, -0.029340f, 0.557287f ),
|
|
Point3F( 0.259529f, 0.416263f, 0.871418f ),
|
|
Point3F( 0.231128f, -0.845982f, 0.480515f ),
|
|
Point3F( -0.626203f, -0.646168f, 0.436277f ),
|
|
Point3F( -0.197047f, -0.065791f, 0.978184f ),
|
|
Point3F( -0.255692f, -0.637488f, -0.726794f ),
|
|
Point3F( 0.530662f, -0.844385f, -0.073567f ),
|
|
Point3F( -0.779887f, 0.617067f, -0.104899f ),
|
|
Point3F( 0.739908f, 0.113984f, 0.662982f ),
|
|
Point3F( -0.218801f, 0.930194f, -0.294729f ),
|
|
Point3F( -0.374231f, 0.818666f, 0.435589f ),
|
|
Point3F( -0.720250f, -0.028285f, 0.693137f ),
|
|
Point3F( 0.075389f, 0.415049f, 0.906670f ),
|
|
Point3F( -0.539724f, -0.106620f, 0.835063f ),
|
|
Point3F( -0.452612f, -0.754669f, -0.474991f ),
|
|
Point3F( 0.682822f, 0.581234f, -0.442629f ),
|
|
Point3F( 0.002435f, -0.618462f, -0.785811f ),
|
|
Point3F( -0.397631f, 0.110766f, -0.910835f ),
|
|
Point3F( 0.133935f, -0.985438f, 0.104754f ),
|
|
Point3F( 0.759098f, -0.608004f, 0.232595f ),
|
|
Point3F( -0.825239f, -0.256087f, 0.503388f ),
|
|
Point3F( 0.101693f, -0.565568f, 0.818408f ),
|
|
Point3F( 0.386377f, 0.793546f, -0.470104f ),
|
|
Point3F( -0.520516f, -0.840690f, 0.149346f ),
|
|
Point3F( -0.784549f, -0.479672f, 0.392935f ),
|
|
Point3F( -0.325322f, -0.927581f, -0.183735f ),
|
|
Point3F( -0.069294f, -0.428541f, 0.900861f ),
|
|
Point3F( 0.993354f, -0.115023f, -0.004288f ),
|
|
Point3F( -0.123896f, -0.700568f, 0.702747f ),
|
|
Point3F( -0.438031f, -0.120880f, -0.890795f ),
|
|
Point3F( 0.063314f, 0.813233f, 0.578484f ),
|
|
Point3F( 0.322045f, 0.889086f, -0.325289f ),
|
|
Point3F( -0.133521f, 0.875063f, -0.465228f ),
|
|
Point3F( 0.637155f, 0.564814f, 0.524422f ),
|
|
Point3F( 0.260092f, -0.669353f, 0.695930f ),
|
|
Point3F( 0.953195f, 0.040485f, -0.299634f ),
|
|
Point3F( -0.840665f, -0.076509f, 0.536124f ),
|
|
Point3F( -0.971350f, 0.202093f, 0.125047f ),
|
|
Point3F( -0.804307f, -0.396312f, -0.442749f ),
|
|
Point3F( -0.936746f, 0.069572f, 0.343027f ),
|
|
Point3F( 0.426545f, -0.465962f, 0.775202f ),
|
|
Point3F( 0.794542f, -0.227450f, 0.563000f ),
|
|
Point3F( -0.892172f, 0.091169f, -0.442399f ),
|
|
Point3F( -0.312654f, 0.541264f, 0.780564f ),
|
|
Point3F( 0.590603f, -0.735618f, -0.331743f ),
|
|
Point3F( -0.098040f, -0.986713f, 0.129558f ),
|
|
Point3F( 0.569646f, 0.283078f, -0.771603f ),
|
|
Point3F( 0.431051f, -0.407385f, -0.805129f ),
|
|
Point3F( -0.162087f, -0.938749f, -0.304104f ),
|
|
Point3F( 0.241533f, -0.359509f, 0.901341f ),
|
|
Point3F( -0.576191f, 0.614939f, 0.538380f ),
|
|
Point3F( -0.025110f, 0.085740f, 0.996001f ),
|
|
Point3F( -0.352693f, -0.198168f, 0.914515f ),
|
|
Point3F( -0.604577f, 0.700711f, 0.378802f ),
|
|
Point3F( 0.465024f, 0.409667f, 0.784809f ),
|
|
Point3F( -0.254684f, -0.030474f, -0.966544f ),
|
|
Point3F( -0.604789f, 0.791809f, 0.085259f ),
|
|
Point3F( -0.705147f, -0.399298f, 0.585943f ),
|
|
Point3F( 0.185691f, 0.017236f, -0.982457f ),
|
|
Point3F( 0.044588f, 0.973094f, 0.226052f ),
|
|
Point3F( -0.405463f, 0.642367f, 0.650357f ),
|
|
Point3F( -0.563959f, 0.599136f, -0.568319f ),
|
|
Point3F( 0.367162f, -0.072253f, -0.927347f ),
|
|
Point3F( 0.960429f, -0.213570f, -0.178783f ),
|
|
Point3F( -0.192629f, 0.906005f, 0.376893f ),
|
|
Point3F( -0.199718f, -0.359865f, -0.911378f ),
|
|
Point3F( 0.485072f, 0.121233f, -0.866030f ),
|
|
Point3F( 0.467163f, -0.874294f, 0.131792f ),
|
|
Point3F( -0.638953f, -0.716603f, 0.279677f ),
|
|
Point3F( -0.622710f, 0.047813f, -0.780990f ),
|
|
Point3F( 0.828724f, -0.054433f, -0.557004f ),
|
|
Point3F( 0.130241f, 0.991080f, 0.028245f ),
|
|
Point3F( 0.310995f, -0.950076f, -0.025242f ),
|
|
Point3F( 0.818118f, 0.275336f, 0.504850f ),
|
|
Point3F( 0.676328f, 0.387023f, 0.626733f ),
|
|
Point3F( -0.100433f, 0.495114f, -0.863004f ),
|
|
Point3F( -0.949609f, -0.240681f, -0.200786f ),
|
|
Point3F( -0.102610f, 0.261831f, -0.959644f ),
|
|
Point3F( -0.845732f, -0.493136f, 0.203850f ),
|
|
Point3F( 0.672617f, -0.738838f, 0.041290f ),
|
|
Point3F( 0.380465f, 0.875938f, 0.296613f ),
|
|
Point3F( -0.811223f, 0.262027f, -0.522742f ),
|
|
Point3F( -0.074423f, -0.775670f, -0.626736f ),
|
|
Point3F( -0.286499f, 0.755850f, -0.588735f ),
|
|
Point3F( 0.291182f, -0.276189f, -0.915933f ),
|
|
Point3F( -0.638117f, 0.199338f, 0.743687f ),
|
|
Point3F( 0.439922f, -0.864433f, -0.243359f ),
|
|
Point3F( 0.177649f, 0.206919f, 0.962094f ),
|
|
Point3F( 0.277107f, 0.948521f, 0.153361f ),
|
|
Point3F( 0.507629f, 0.661918f, -0.551523f ),
|
|
Point3F( -0.503110f, -0.579308f, -0.641313f ),
|
|
Point3F( 0.600522f, 0.736495f, -0.311364f ),
|
|
Point3F( -0.691096f, -0.715301f, -0.103592f ),
|
|
Point3F( -0.041083f, -0.858497f, 0.511171f ),
|
|
Point3F( 0.207773f, -0.480062f, -0.852274f ),
|
|
Point3F( 0.795719f, 0.464614f, 0.388543f ),
|
|
Point3F( -0.100433f, 0.495114f, -0.863004f ),
|
|
Point3F( 0.703249f, 0.065157f, -0.707951f ),
|
|
Point3F( -0.324171f, -0.941112f, 0.096024f ),
|
|
Point3F( -0.134933f, -0.940212f, 0.312722f ),
|
|
Point3F( -0.438240f, 0.752088f, -0.492249f ),
|
|
Point3F( 0.964762f, -0.198855f, 0.172311f ),
|
|
Point3F( -0.831799f, 0.196807f, 0.519015f ),
|
|
Point3F( -0.508008f, 0.819902f, 0.263986f ),
|
|
Point3F( 0.471075f, -0.001146f, 0.882092f ),
|
|
Point3F( 0.919512f, 0.246162f, -0.306435f ),
|
|
Point3F( -0.960050f, 0.279828f, -0.001187f ),
|
|
Point3F( 0.110232f, -0.847535f, -0.519165f ),
|
|
Point3F( 0.208229f, 0.697360f, 0.685806f ),
|
|
Point3F( -0.199680f, -0.560621f, 0.803637f ),
|
|
Point3F( 0.170135f, -0.679985f, -0.713214f ),
|
|
Point3F( 0.758371f, -0.494907f, 0.424195f ),
|
|
Point3F( 0.077734f, -0.755978f, 0.649965f ),
|
|
Point3F( 0.612831f, -0.672475f, 0.414987f ),
|
|
Point3F( 0.142776f, 0.836698f, -0.528726f ),
|
|
Point3F( -0.765185f, 0.635778f, 0.101382f ),
|
|
Point3F( 0.669873f, -0.419737f, 0.612447f ),
|
|
Point3F( 0.593549f, 0.194879f, 0.780847f ),
|
|
Point3F( 0.646930f, 0.752173f, 0.125368f ),
|
|
Point3F( 0.837721f, 0.545266f, -0.030127f ),
|
|
Point3F( 0.541505f, 0.768070f, 0.341820f ),
|
|
Point3F( 0.760679f, -0.365715f, -0.536301f ),
|
|
Point3F( 0.381516f, 0.640377f, 0.666605f ),
|
|
Point3F( 0.565794f, -0.072415f, -0.821361f ),
|
|
Point3F( -0.466072f, -0.401588f, 0.788356f ),
|
|
Point3F( 0.987146f, 0.096290f, 0.127560f ),
|
|
Point3F( 0.509709f, -0.688886f, -0.515396f ),
|
|
Point3F( -0.135132f, -0.988046f, -0.074192f ),
|
|
Point3F( 0.600499f, 0.476471f, -0.642166f ),
|
|
Point3F( -0.732326f, -0.275320f, -0.622815f ),
|
|
Point3F( -0.881141f, -0.470404f, 0.048078f ),
|
|
Point3F( 0.051548f, 0.601042f, 0.797553f ),
|
|
Point3F( 0.402027f, -0.763183f, 0.505891f ),
|
|
Point3F( 0.404233f, -0.208288f, 0.890624f ),
|
|
Point3F( -0.311793f, 0.343843f, 0.885752f ),
|
|
Point3F( 0.098132f, -0.937014f, 0.335223f ),
|
|
Point3F( 0.537158f, 0.830585f, -0.146936f ),
|
|
Point3F( 0.725277f, 0.298172f, -0.620538f ),
|
|
Point3F( -0.882025f, 0.342976f, -0.323110f ),
|
|
Point3F( -0.668829f, 0.424296f, -0.610443f ),
|
|
Point3F( -0.408835f, -0.476442f, -0.778368f ),
|
|
Point3F( 0.809472f, 0.397249f, -0.432375f ),
|
|
Point3F( -0.909184f, -0.205938f, -0.361903f ),
|
|
Point3F( 0.866930f, -0.347934f, -0.356895f ),
|
|
Point3F( 0.911660f, -0.141281f, -0.385897f ),
|
|
Point3F( -0.431404f, -0.844074f, -0.318480f ),
|
|
Point3F( -0.950593f, -0.073496f, 0.301614f ),
|
|
Point3F( -0.719716f, 0.626915f, -0.298305f ),
|
|
Point3F( -0.779887f, 0.617067f, -0.104899f ),
|
|
Point3F( -0.475899f, -0.542630f, 0.692151f ),
|
|
Point3F( 0.081952f, -0.157248f, -0.984153f ),
|
|
Point3F( 0.923990f, -0.381662f, -0.024025f ),
|
|
Point3F( -0.957998f, 0.120979f, -0.260008f ),
|
|
Point3F( 0.306601f, 0.227975f, -0.924134f ),
|
|
Point3F( -0.141244f, 0.989182f, 0.039601f ),
|
|
Point3F( 0.077097f, 0.186288f, -0.979466f ),
|
|
Point3F( -0.630407f, -0.259801f, 0.731499f ),
|
|
Point3F( 0.718150f, 0.637408f, 0.279233f ),
|
|
Point3F( 0.340946f, 0.110494f, 0.933567f ),
|
|
Point3F( -0.396671f, 0.503020f, -0.767869f ),
|
|
Point3F( 0.636943f, -0.245005f, 0.730942f ),
|
|
Point3F( -0.849605f, -0.518660f, -0.095724f ),
|
|
Point3F( -0.388203f, 0.105395f, 0.915527f ),
|
|
Point3F( -0.280671f, -0.776541f, -0.564099f ),
|
|
Point3F( -0.601680f, 0.215451f, -0.769131f ),
|
|
Point3F( -0.660112f, -0.632371f, -0.405412f ),
|
|
Point3F( 0.921096f, 0.284072f, 0.266242f ),
|
|
Point3F( 0.074850f, -0.300846f, 0.950731f ),
|
|
Point3F( 0.943952f, -0.067062f, 0.323198f ),
|
|
Point3F( -0.917838f, -0.254589f, 0.304561f ),
|
|
Point3F( 0.889843f, -0.409008f, 0.202219f ),
|
|
Point3F( -0.565849f, 0.753721f, -0.334246f ),
|
|
Point3F( 0.791460f, 0.555918f, -0.254060f ),
|
|
Point3F( 0.261936f, 0.703590f, -0.660568f ),
|
|
Point3F( -0.234406f, 0.952084f, 0.196444f ),
|
|
Point3F( 0.111205f, 0.979492f, -0.168014f ),
|
|
Point3F( -0.869844f, -0.109095f, -0.481113f ),
|
|
Point3F( -0.337728f, -0.269701f, -0.901777f ),
|
|
Point3F( 0.366793f, 0.408875f, -0.835634f ),
|
|
Point3F( -0.098749f, 0.261316f, 0.960189f ),
|
|
Point3F( -0.272379f, -0.847100f, 0.456324f ),
|
|
Point3F( -0.319506f, 0.287444f, -0.902935f ),
|
|
Point3F( 0.873383f, -0.294109f, 0.388203f ),
|
|
Point3F( -0.088950f, 0.710450f, 0.698104f ),
|
|
Point3F( 0.551238f, -0.786552f, 0.278340f ),
|
|
Point3F( 0.724436f, -0.663575f, -0.186712f ),
|
|
Point3F( 0.529741f, -0.606539f, 0.592861f ),
|
|
Point3F( -0.949743f, -0.282514f, 0.134809f ),
|
|
Point3F( 0.155047f, 0.419442f, -0.894443f ),
|
|
Point3F( -0.562653f, -0.329139f, -0.758346f ),
|
|
Point3F( 0.816407f, -0.576953f, 0.024576f ),
|
|
Point3F( 0.178550f, -0.950242f, -0.255266f ),
|
|
Point3F( 0.479571f, 0.706691f, 0.520192f ),
|
|
Point3F( 0.391687f, 0.559884f, -0.730145f ),
|
|
Point3F( 0.724872f, -0.205570f, -0.657496f ),
|
|
Point3F( -0.663196f, -0.517587f, -0.540624f ),
|
|
Point3F( -0.660054f, -0.122486f, -0.741165f ),
|
|
Point3F( -0.531989f, 0.374711f, -0.759328f ),
|
|
Point3F( 0.194979f, -0.059120f, 0.979024f )
|
|
};
|
|
|
|
U8 TSMesh::encodeNormal( const Point3F &normal )
|
|
{
|
|
U8 bestIndex = 0;
|
|
F32 bestDot = -10E30f;
|
|
for ( U32 i = 0; i < 256; i++ )
|
|
{
|
|
F32 dot = mDot( normal, smU8ToNormalTable[i] );
|
|
if ( dot > bestDot )
|
|
{
|
|
bestIndex = i;
|
|
bestDot = dot;
|
|
}
|
|
}
|
|
return bestIndex;
|
|
}
|
|
|
|
//-----------------------------------------------------
|
|
// TSMesh assemble from/ dissemble to memory buffer
|
|
//-----------------------------------------------------
|
|
|
|
#define tsalloc TSShape::smTSAlloc
|
|
|
|
TSMesh* TSMesh::assembleMesh( U32 meshType, bool skip )
|
|
{
|
|
static TSMesh tempStandardMesh;
|
|
static TSSkinMesh tempSkinMesh;
|
|
static TSDecalMesh tempDecalMesh;
|
|
static TSSortedMesh tempSortedMesh;
|
|
|
|
bool justSize = skip || !tsalloc.allocShape32(0); // if this returns NULL, we're just sizing memory block
|
|
|
|
// a little funny business because we pretend decals are derived from meshes
|
|
S32 * ret = NULL;
|
|
TSMesh * mesh = NULL;
|
|
TSDecalMesh * decal = NULL;
|
|
|
|
if ( justSize )
|
|
{
|
|
switch ( meshType )
|
|
{
|
|
case StandardMeshType :
|
|
{
|
|
ret = (S32*)&tempStandardMesh;
|
|
mesh = &tempStandardMesh;
|
|
tsalloc.allocShape32( sizeof(TSMesh) >> 2 );
|
|
break;
|
|
}
|
|
case SkinMeshType :
|
|
{
|
|
ret = (S32*)&tempSkinMesh;
|
|
mesh = &tempSkinMesh;
|
|
tsalloc.allocShape32( sizeof(TSSkinMesh) >> 2 );
|
|
break;
|
|
}
|
|
case DecalMeshType :
|
|
{
|
|
ret = (S32*)&tempDecalMesh;
|
|
decal = &tempDecalMesh;
|
|
tsalloc.allocShape32( sizeof(TSDecalMesh) >> 2 );
|
|
break;
|
|
}
|
|
case SortedMeshType :
|
|
{
|
|
ret = (S32*)&tempSortedMesh;
|
|
mesh = &tempSortedMesh;
|
|
tsalloc.allocShape32( sizeof(TSSortedMesh) >> 2 );
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
switch ( meshType )
|
|
{
|
|
case StandardMeshType :
|
|
{
|
|
ret = tsalloc.allocShape32( sizeof(TSMesh) >> 2 );
|
|
constructInPlace( (TSMesh*)ret );
|
|
mesh = (TSMesh*)ret;
|
|
break;
|
|
}
|
|
case SkinMeshType :
|
|
{
|
|
ret = tsalloc.allocShape32( sizeof(TSSkinMesh) >> 2 );
|
|
constructInPlace( (TSSkinMesh*)ret );
|
|
mesh = (TSSkinMesh*)ret;
|
|
break;
|
|
}
|
|
case DecalMeshType :
|
|
{
|
|
ret = tsalloc.allocShape32( sizeof(TSDecalMesh) >> 2 );
|
|
constructInPlace((TSDecalMesh*)ret);
|
|
decal = (TSDecalMesh*)ret;
|
|
break;
|
|
}
|
|
case SortedMeshType :
|
|
{
|
|
ret = tsalloc.allocShape32( sizeof(TSSortedMesh) >> 2 );
|
|
constructInPlace( (TSSortedMesh*)ret );
|
|
mesh = (TSSortedMesh*)ret;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
tsalloc.setSkipMode( skip );
|
|
|
|
if ( mesh )
|
|
mesh->assemble( skip );
|
|
|
|
if ( decal )
|
|
decal->assemble( skip );
|
|
|
|
tsalloc.setSkipMode( false );
|
|
|
|
return (TSMesh*)ret;
|
|
}
|
|
|
|
void TSMesh::convertToTris( const TSDrawPrimitive *primitivesIn,
|
|
const S32 *indicesIn,
|
|
S32 numPrimIn,
|
|
S32 &numPrimOut,
|
|
S32 &numIndicesOut,
|
|
TSDrawPrimitive *primitivesOut,
|
|
S32 *indicesOut ) const
|
|
{
|
|
S32 prevMaterial = -99999;
|
|
TSDrawPrimitive * newDraw = NULL;
|
|
numPrimOut = 0;
|
|
numIndicesOut = 0;
|
|
for ( S32 i = 0; i < numPrimIn; i++ )
|
|
{
|
|
S32 newMat = primitivesIn[i].matIndex;
|
|
newMat &= ~TSDrawPrimitive::TypeMask;
|
|
|
|
U32 start = primitivesIn[i].start;
|
|
U32 prevStart = (i > 0) ? primitivesIn[i-1].start : start;
|
|
U32 numElements = primitivesIn[i].numElements;
|
|
|
|
// Add a new primitive if changing materials, or if this primitive
|
|
// indexes vertices in a different 16-bit range
|
|
if ( ( newMat != prevMaterial ) ||
|
|
((indicesIn[prevStart] ^ indicesIn[start]) & 0xFFFF0000) )
|
|
{
|
|
if ( primitivesOut )
|
|
{
|
|
newDraw = &primitivesOut[numPrimOut];
|
|
newDraw->start = numIndicesOut;
|
|
newDraw->numElements = 0;
|
|
newDraw->matIndex = newMat | TSDrawPrimitive::Triangles;
|
|
}
|
|
numPrimOut++;
|
|
prevMaterial = newMat;
|
|
}
|
|
|
|
// gonna depend on what kind of primitive it is...
|
|
if ( (primitivesIn[i].matIndex & TSDrawPrimitive::TypeMask) == TSDrawPrimitive::Triangles)
|
|
{
|
|
for ( S32 j = 0; j < numElements; j += 3 )
|
|
{
|
|
if ( indicesOut )
|
|
{
|
|
indicesOut[numIndicesOut + 0] = indicesIn[start + j + 0];
|
|
indicesOut[numIndicesOut + 1] = indicesIn[start + j + 1];
|
|
indicesOut[numIndicesOut + 2] = indicesIn[start + j + 2];
|
|
}
|
|
if ( newDraw )
|
|
newDraw->numElements += 3;
|
|
|
|
numIndicesOut += 3;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
U32 idx0 = indicesIn[start + 0];
|
|
U32 idx1;
|
|
U32 idx2 = indicesIn[start + 1];
|
|
U32 * nextIdx = &idx1;
|
|
for ( S32 j = 2; j < numElements; j++ )
|
|
{
|
|
*nextIdx = idx2;
|
|
nextIdx = (U32*) ( (dsize_t)nextIdx ^ (dsize_t)&idx0 ^ (dsize_t)&idx1);
|
|
idx2 = indicesIn[start + j];
|
|
if ( idx0 == idx1 || idx1 == idx2 || idx2 == idx0 )
|
|
continue;
|
|
|
|
if ( indicesOut )
|
|
{
|
|
indicesOut[numIndicesOut+0] = idx0;
|
|
indicesOut[numIndicesOut+1] = idx1;
|
|
indicesOut[numIndicesOut+2] = idx2;
|
|
}
|
|
|
|
if ( newDraw )
|
|
newDraw->numElements += 3;
|
|
numIndicesOut += 3;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void unwindStrip( const S32 * indices, S32 numElements, Vector<S32> &triIndices )
|
|
{
|
|
U32 idx0 = indices[0];
|
|
U32 idx1;
|
|
U32 idx2 = indices[1];
|
|
U32 * nextIdx = &idx1;
|
|
for ( S32 j = 2; j < numElements; j++ )
|
|
{
|
|
*nextIdx = idx2;
|
|
nextIdx = (U32*) ( (dsize_t)nextIdx ^ (dsize_t)&idx0 ^ (dsize_t)&idx1);
|
|
idx2 = indices[j];
|
|
if ( idx0 == idx1 || idx1 == idx2 || idx2 == idx0 )
|
|
continue;
|
|
|
|
triIndices.push_back( idx0 );
|
|
triIndices.push_back( idx1 );
|
|
triIndices.push_back( idx2 );
|
|
}
|
|
}
|
|
|
|
void TSMesh::convertToSingleStrip( const TSDrawPrimitive *primitivesIn,
|
|
const S32 *indicesIn,
|
|
S32 numPrimIn,
|
|
S32 &numPrimOut,
|
|
S32 &numIndicesOut,
|
|
TSDrawPrimitive *primitivesOut,
|
|
S32 *indicesOut ) const
|
|
{
|
|
S32 prevMaterial = -99999;
|
|
TSDrawPrimitive * newDraw = NULL;
|
|
TSDrawPrimitive * newTris = NULL;
|
|
Vector<S32> triIndices;
|
|
S32 curDrawOut = 0;
|
|
numPrimOut = 0;
|
|
numIndicesOut = 0;
|
|
for ( S32 i = 0; i < numPrimIn; i++ )
|
|
{
|
|
S32 newMat = primitivesIn[i].matIndex;
|
|
|
|
U32 start = primitivesIn[i].start;
|
|
U32 prevStart = (i > 0) ? primitivesIn[i-1].start : start;
|
|
U32 numElements = primitivesIn[i].numElements;
|
|
|
|
// Add a new primitive if changing materials, or if this primitive
|
|
// indexes vertices in a different 16-bit range
|
|
if ( ( newMat != prevMaterial ) ||
|
|
((indicesIn[prevStart] ^ indicesIn[start]) & 0xFFFF0000) )
|
|
{
|
|
// before adding the new primitive, transfer triangle indices
|
|
if ( triIndices.size() )
|
|
{
|
|
if ( newTris && indicesOut )
|
|
{
|
|
newTris->start = numIndicesOut;
|
|
newTris->numElements = triIndices.size();
|
|
dMemcpy(&indicesOut[numIndicesOut],triIndices.address(),triIndices.size()*sizeof(U32));
|
|
}
|
|
numIndicesOut += triIndices.size();
|
|
triIndices.clear();
|
|
newTris = NULL;
|
|
}
|
|
|
|
if ( primitivesOut )
|
|
{
|
|
newDraw = &primitivesOut[numPrimOut];
|
|
newDraw->start = numIndicesOut;
|
|
newDraw->numElements = 0;
|
|
newDraw->matIndex = newMat;
|
|
}
|
|
|
|
numPrimOut++;
|
|
curDrawOut = 0;
|
|
prevMaterial = newMat;
|
|
}
|
|
|
|
// gonna depend on what kind of primitive it is...
|
|
// from above we know it's the same kind as the one we're building...
|
|
if ( (primitivesIn[i].matIndex & TSDrawPrimitive::TypeMask) == TSDrawPrimitive::Triangles)
|
|
{
|
|
// triangles primitive...add to it
|
|
for ( S32 j = 0; j < numElements; j += 3 )
|
|
{
|
|
if ( indicesOut )
|
|
{
|
|
indicesOut[numIndicesOut + 0] = indicesIn[start + j + 0];
|
|
indicesOut[numIndicesOut + 1] = indicesIn[start + j + 1];
|
|
indicesOut[numIndicesOut + 2] = indicesIn[start + j + 2];
|
|
}
|
|
|
|
if ( newDraw )
|
|
newDraw->numElements += 3;
|
|
|
|
numIndicesOut += 3;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// strip primitive...
|
|
// if numElements less than smSmallestStripSize, add to triangles...
|
|
if ( numElements < smMinStripSize + 2 )
|
|
{
|
|
// put triangle indices aside until material changes...
|
|
if ( triIndices.empty() )
|
|
{
|
|
// set up for new triangle primitive and add it if we are copying data right now
|
|
if ( primitivesOut )
|
|
{
|
|
newTris = &primitivesOut[numPrimOut];
|
|
newTris->matIndex = newMat;
|
|
newTris->matIndex &= ~(TSDrawPrimitive::Triangles|TSDrawPrimitive::Strip);
|
|
newTris->matIndex |= TSDrawPrimitive::Triangles;
|
|
}
|
|
|
|
numPrimOut++;
|
|
}
|
|
unwindStrip( indicesIn + start, numElements, triIndices );
|
|
}
|
|
else
|
|
{
|
|
// strip primitive...add to it
|
|
if ( indicesOut )
|
|
{
|
|
if ( curDrawOut & 1 )
|
|
{
|
|
indicesOut[numIndicesOut + 0] = indicesOut[numIndicesOut - 1];
|
|
indicesOut[numIndicesOut + 1] = indicesOut[numIndicesOut - 1];
|
|
indicesOut[numIndicesOut + 2] = indicesIn[start];
|
|
dMemcpy(indicesOut+numIndicesOut+3,indicesIn+start,numElements*sizeof(U32));
|
|
}
|
|
else if ( curDrawOut )
|
|
{
|
|
indicesOut[numIndicesOut + 0] = indicesOut[numIndicesOut - 1];
|
|
indicesOut[numIndicesOut + 1] = indicesIn[start];
|
|
dMemcpy(indicesOut+numIndicesOut+2,indicesIn+start,numElements*sizeof(U32));
|
|
}
|
|
else
|
|
dMemcpy(indicesOut+numIndicesOut,indicesIn+start,numElements*sizeof(U32));
|
|
}
|
|
S32 added = numElements;
|
|
added += curDrawOut ? (curDrawOut&1 ? 3 : 2) : 0;
|
|
if ( newDraw )
|
|
newDraw->numElements += added;
|
|
|
|
numIndicesOut += added;
|
|
curDrawOut += added;
|
|
}
|
|
}
|
|
}
|
|
// spit out tris before leaving
|
|
// before adding the new primitive, transfer triangle indices
|
|
if ( triIndices.size() )
|
|
{
|
|
if ( newTris && indicesOut )
|
|
{
|
|
newTris->start = numIndicesOut;
|
|
newTris->numElements = triIndices.size();
|
|
dMemcpy(&indicesOut[numIndicesOut],triIndices.address(),triIndices.size()*sizeof(U32));
|
|
}
|
|
|
|
numIndicesOut += triIndices.size();
|
|
triIndices.clear();
|
|
newTris = NULL;
|
|
}
|
|
}
|
|
|
|
// this method does none of the converting that the above methods do, except that small strips are converted
|
|
// to triangle lists...
|
|
void TSMesh::leaveAsMultipleStrips( const TSDrawPrimitive *primitivesIn,
|
|
const S32 *indicesIn,
|
|
S32 numPrimIn,
|
|
S32 &numPrimOut,
|
|
S32 &numIndicesOut,
|
|
TSDrawPrimitive *primitivesOut,
|
|
S32 *indicesOut ) const
|
|
{
|
|
S32 prevMaterial = -99999;
|
|
TSDrawPrimitive * newDraw = NULL;
|
|
Vector<S32> triIndices;
|
|
numPrimOut = 0;
|
|
numIndicesOut = 0;
|
|
for ( S32 i = 0; i < numPrimIn; i++ )
|
|
{
|
|
S32 newMat = primitivesIn[i].matIndex;
|
|
|
|
U32 start = primitivesIn[i].start;
|
|
U32 prevStart = (i > 0) ? primitivesIn[i-1].start : start;
|
|
U32 numElements = primitivesIn[i].numElements;
|
|
|
|
// Add a new primitive if changing materials, or if this primitive
|
|
// indexes vertices in a different 16-bit range
|
|
if ( triIndices.size() &&
|
|
(( newMat != prevMaterial ) ||
|
|
((indicesIn[prevStart] ^ indicesIn[start]) & 0xFFFF0000) ))
|
|
{
|
|
// material just changed and we have triangles lying around
|
|
// add primitive and indices for triangles and clear triIndices
|
|
if ( indicesOut )
|
|
{
|
|
TSDrawPrimitive * newTris = &primitivesOut[numPrimOut];
|
|
newTris->matIndex = prevMaterial;
|
|
newTris->matIndex &= ~(TSDrawPrimitive::Triangles|TSDrawPrimitive::Strip);
|
|
newTris->matIndex |= TSDrawPrimitive::Triangles;
|
|
newTris->start = numIndicesOut;
|
|
newTris->numElements = triIndices.size();
|
|
dMemcpy(&indicesOut[numIndicesOut],triIndices.address(),triIndices.size()*sizeof(U32));
|
|
}
|
|
numPrimOut++;
|
|
numIndicesOut += triIndices.size();
|
|
triIndices.clear();
|
|
}
|
|
|
|
// this is a little convoluted because this code was adapted from convertToSingleStrip
|
|
// but we will need a new primitive only if it is a triangle primitive coming in
|
|
// or we have more elements than the min strip size...
|
|
if ( (primitivesIn[i].matIndex & TSDrawPrimitive::TypeMask) == TSDrawPrimitive::Triangles || numElements>=smMinStripSize+2)
|
|
{
|
|
if ( primitivesOut )
|
|
{
|
|
newDraw = &primitivesOut[numPrimOut];
|
|
newDraw->start = numIndicesOut;
|
|
newDraw->numElements = 0;
|
|
newDraw->matIndex = newMat;
|
|
}
|
|
numPrimOut++;
|
|
}
|
|
prevMaterial = newMat;
|
|
|
|
// gonna depend on what kind of primitive it is...
|
|
// from above we know it's the same kind as the one we're building...
|
|
if ( (primitivesIn[i].matIndex & TSDrawPrimitive::TypeMask) == TSDrawPrimitive::Triangles)
|
|
{
|
|
// triangles primitive...add to it
|
|
for ( S32 j = 0; j < numElements; j += 3 )
|
|
{
|
|
if ( indicesOut )
|
|
{
|
|
indicesOut[numIndicesOut + 0] = indicesIn[start + j + 0];
|
|
indicesOut[numIndicesOut + 1] = indicesIn[start + j + 1];
|
|
indicesOut[numIndicesOut + 2] = indicesIn[start + j + 2];
|
|
}
|
|
if ( newDraw )
|
|
newDraw->numElements += 3;
|
|
|
|
numIndicesOut += 3;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// strip primitive...
|
|
// if numElements less than smSmallestStripSize, add to triangles...
|
|
if ( numElements < smMinStripSize + 2 )
|
|
// put triangle indices aside until material changes...
|
|
unwindStrip( indicesIn + start, numElements, triIndices );
|
|
else
|
|
{
|
|
// strip primitive...add to it
|
|
if ( indicesOut )
|
|
dMemcpy(indicesOut+numIndicesOut,indicesIn+start,numElements*sizeof(U32));
|
|
if ( newDraw )
|
|
newDraw->numElements = numElements;
|
|
|
|
numIndicesOut += numElements;
|
|
}
|
|
}
|
|
}
|
|
// spit out tris before leaving
|
|
if ( triIndices.size() )
|
|
{
|
|
// material just changed and we have triangles lying around
|
|
// add primitive and indices for triangles and clear triIndices
|
|
if ( indicesOut )
|
|
{
|
|
TSDrawPrimitive *newTris = &primitivesOut[numPrimOut];
|
|
newTris->matIndex = prevMaterial;
|
|
newTris->matIndex &= ~(TSDrawPrimitive::Triangles|TSDrawPrimitive::Strip);
|
|
newTris->matIndex |= TSDrawPrimitive::Triangles;
|
|
newTris->start = numIndicesOut;
|
|
newTris->numElements = triIndices.size();
|
|
dMemcpy(&indicesOut[numIndicesOut],triIndices.address(),triIndices.size()*sizeof(U32));
|
|
}
|
|
numPrimOut++;
|
|
numIndicesOut += triIndices.size();
|
|
triIndices.clear();
|
|
}
|
|
}
|
|
|
|
// This method retrieves data that is shared (or possibly shared) between different meshes.
|
|
// This adds an extra step to the copying of data from the memory buffer to the shape data buffer.
|
|
// If we have no parentMesh, then we either return a pointer to the data in the memory buffer
|
|
// (in the case that we skip this mesh) or copy the data into the shape data buffer and return
|
|
// that pointer (in the case that we don't skip this mesh).
|
|
// If we do have a parent mesh, then we return a pointer to the data in the shape buffer,
|
|
// copying the data in there ourselves if our parent didn't already do it (i.e., if it was skipped).
|
|
S32 * TSMesh::getSharedData32( S32 parentMesh, S32 size, S32 **source, bool skip )
|
|
{
|
|
S32 * ptr;
|
|
if( parentMesh < 0 )
|
|
ptr = skip ? tsalloc.getPointer32( size ) : tsalloc.copyToShape32( size );
|
|
else
|
|
{
|
|
ptr = source[parentMesh];
|
|
// if we skipped the previous mesh (and we're not skipping this one) then
|
|
// we still need to copy points into the shape...
|
|
if ( !smDataCopied[parentMesh] && !skip )
|
|
{
|
|
S32 * tmp = ptr;
|
|
ptr = tsalloc.allocShape32( size );
|
|
if ( ptr && tmp )
|
|
dMemcpy(ptr, tmp, size * sizeof(S32) );
|
|
}
|
|
}
|
|
return ptr;
|
|
}
|
|
|
|
S8 * TSMesh::getSharedData8( S32 parentMesh, S32 size, S8 **source, bool skip )
|
|
{
|
|
S8 * ptr;
|
|
if( parentMesh < 0 )
|
|
ptr = skip ? tsalloc.getPointer8( size ) : tsalloc.copyToShape8( size );
|
|
else
|
|
{
|
|
ptr = source[parentMesh];
|
|
// if we skipped the previous mesh (and we're not skipping this one) then
|
|
// we still need to copy points into the shape...
|
|
if ( !smDataCopied[parentMesh] && !skip )
|
|
{
|
|
S8 * tmp = ptr;
|
|
ptr = tsalloc.allocShape8( size );
|
|
if ( ptr && tmp )
|
|
dMemcpy( ptr, tmp, size * sizeof(S32) );
|
|
}
|
|
}
|
|
return ptr;
|
|
}
|
|
|
|
void TSMesh::dumpPrimitives(U32 startVertex, U32 startIndex, GFXPrimitive *piArray, U16* ibIndices)
|
|
{
|
|
// go through and create PrimitiveInfo array
|
|
GFXPrimitive pInfo;
|
|
|
|
U32 primitivesSize = mPrimitives.size();
|
|
for (U32 i = 0; i < primitivesSize; i++)
|
|
{
|
|
const TSDrawPrimitive & draw = mPrimitives[i];
|
|
|
|
GFXPrimitiveType drawType = getDrawType(draw.matIndex >> 30);
|
|
|
|
switch (drawType)
|
|
{
|
|
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 = (mIndices[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;
|
|
|
|
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 = (mIndices[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;
|
|
|
|
default:
|
|
AssertFatal(false, "WTF?!");
|
|
}
|
|
|
|
*piArray++ = pInfo;
|
|
}
|
|
|
|
dCopyArray(ibIndices, mIndices.address(), mIndices.size());
|
|
}
|
|
|
|
void TSMesh::assemble( bool skip )
|
|
{
|
|
tsalloc.checkGuard();
|
|
|
|
numFrames = tsalloc.get32();
|
|
numMatFrames = tsalloc.get32();
|
|
mParentMesh = tsalloc.get32();
|
|
tsalloc.get32( (S32*)&mBounds, 6 );
|
|
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(mParentMesh, 3 * numVerts, (S32**)smVertsList.address(), skip );
|
|
mVerts.set( (Point3F*)ptr32, numVerts );
|
|
|
|
S32 numTVerts = tsalloc.get32();
|
|
ptr32 = getSharedData32(mParentMesh, 2 * numTVerts, (S32**)smTVertsList.address(), skip );
|
|
mTverts.set( (Point2F*)ptr32, numTVerts );
|
|
|
|
if ( TSShape::smReadVersion > 25 )
|
|
{
|
|
numTVerts = tsalloc.get32();
|
|
ptr32 = getSharedData32(mParentMesh, 2 * numTVerts, (S32**)smTVerts2List.address(), skip );
|
|
mTverts2.set( (Point2F*)ptr32, numTVerts );
|
|
|
|
S32 numVColors = tsalloc.get32();
|
|
ptr32 = getSharedData32(mParentMesh, numVColors, (S32**)smColorsList.address(), skip );
|
|
mColors.set( (ColorI*)ptr32, numVColors );
|
|
}
|
|
|
|
S8 *ptr8;
|
|
if ( TSShape::smReadVersion > 21 && TSMesh::smUseEncodedNormals)
|
|
{
|
|
// we have encoded normals and we want to use them...
|
|
if (mParentMesh < 0 )
|
|
tsalloc.getPointer32( numVerts * 3 ); // adva nce past norms, don't use
|
|
mNorms.set( NULL, 0 );
|
|
|
|
ptr8 = getSharedData8(mParentMesh, numVerts, (S8**)smEncodedNormsList.address(), skip );
|
|
mEncodedNorms.set( ptr8, numVerts );
|
|
}
|
|
else if ( TSShape::smReadVersion > 21 )
|
|
{
|
|
// we have encoded normals but we don't want to use them...
|
|
ptr32 = getSharedData32(mParentMesh, 3 * numVerts, (S32**)smNormsList.address(), skip );
|
|
mNorms.set( (Point3F*)ptr32, numVerts );
|
|
|
|
if (mParentMesh < 0 )
|
|
tsalloc.getPointer8( numVerts ); // advance past encoded normls, don't use
|
|
mEncodedNorms.set( NULL, 0 );
|
|
}
|
|
else
|
|
{
|
|
// no encoded normals...
|
|
ptr32 = getSharedData32(mParentMesh, 3 * numVerts, (S32**)smNormsList.address(), skip );
|
|
mNorms.set( (Point3F*)ptr32, numVerts );
|
|
mEncodedNorms.set( NULL, 0 );
|
|
}
|
|
|
|
// copy the primitives and indices...how we do this depends on what
|
|
// form we want them in when copied...just get pointers to data for now
|
|
S32 szPrimIn, szIndIn;
|
|
TSDrawPrimitive *primIn;
|
|
S32 *indIn;
|
|
bool deleteInputArrays = false;
|
|
|
|
if (TSShape::smReadVersion > 25)
|
|
{
|
|
// mesh primitives (start, numElements) and indices are stored as 32 bit values
|
|
szPrimIn = tsalloc.get32();
|
|
primIn = (TSDrawPrimitive*)tsalloc.getPointer32(szPrimIn*3);
|
|
szIndIn = tsalloc.get32();
|
|
indIn = tsalloc.getPointer32(szIndIn);
|
|
}
|
|
else
|
|
{
|
|
// mesh primitives (start, numElements) indices are stored as 16 bit values
|
|
szPrimIn = tsalloc.get32();
|
|
S16 *prim16 = tsalloc.getPointer16(szPrimIn*2); // primitive: start, numElements
|
|
S32 *prim32 = tsalloc.getPointer32(szPrimIn); // primitive: matIndex
|
|
szIndIn = tsalloc.get32();
|
|
|
|
// warn about non-addressable indices
|
|
if ( !skip && szIndIn >= 0x10000 )
|
|
{
|
|
Con::warnf("Mesh contains non-addressable indices, and may not render "
|
|
"correctly. Either split this mesh into pieces of no more than 65k "
|
|
"unique verts prior to export, or use COLLADA.");
|
|
}
|
|
|
|
S16 *ind16 = tsalloc.getPointer16(szIndIn);
|
|
|
|
// need to copy to temporary arrays
|
|
deleteInputArrays = true;
|
|
primIn = new TSDrawPrimitive[szPrimIn];
|
|
for (S32 i = 0; i < szPrimIn; i++)
|
|
{
|
|
primIn[i].start = prim16[i*2];
|
|
primIn[i].numElements = prim16[i*2+1];
|
|
primIn[i].matIndex = prim32[i];
|
|
}
|
|
|
|
indIn = new S32[szIndIn];
|
|
dCopyArray(indIn, ind16, szIndIn);
|
|
}
|
|
|
|
// count the number of output primitives and indices
|
|
S32 szPrimOut = szPrimIn, szIndOut = szIndIn;
|
|
if (smUseTriangles)
|
|
convertToTris(primIn, indIn, szPrimIn, szPrimOut, szIndOut, NULL, NULL);
|
|
else if (smUseOneStrip)
|
|
convertToSingleStrip(primIn, indIn, szPrimIn, szPrimOut, szIndOut, NULL, NULL);
|
|
else
|
|
leaveAsMultipleStrips(primIn, indIn, szPrimIn, szPrimOut, szIndOut, NULL, NULL);
|
|
|
|
// allocate enough space for the new primitives and indices (all 32 bits)
|
|
TSDrawPrimitive *primOut = (TSDrawPrimitive*)tsalloc.allocShape32(3*szPrimOut);
|
|
S32 *indOut = tsalloc.allocShape32(szIndOut);
|
|
|
|
// copy output primitives and indices
|
|
S32 chkPrim = szPrimOut, chkInd = szIndOut;
|
|
if (smUseTriangles)
|
|
convertToTris(primIn, indIn, szPrimIn, chkPrim, chkInd, primOut, indOut);
|
|
else if (smUseOneStrip)
|
|
convertToSingleStrip(primIn, indIn, szPrimIn, chkPrim, chkInd, primOut, indOut);
|
|
else
|
|
leaveAsMultipleStrips(primIn, indIn, szPrimIn, chkPrim, chkInd, primOut, indOut);
|
|
AssertFatal(chkPrim==szPrimOut && chkInd==szIndOut,"TSMesh::primitive conversion");
|
|
|
|
// store output
|
|
mPrimitives.set(primOut, szPrimOut);
|
|
mIndices.set(indOut, szIndOut);
|
|
|
|
// delete temporary arrays if necessary
|
|
if (deleteInputArrays)
|
|
{
|
|
delete [] primIn;
|
|
delete [] indIn;
|
|
}
|
|
|
|
S32 sz = tsalloc.get32();
|
|
tsalloc.getPointer16( sz ); // skip deprecated merge indices
|
|
tsalloc.align32();
|
|
|
|
vertsPerFrame = tsalloc.get32();
|
|
U32 flags = (U32)tsalloc.get32();
|
|
if ( mEncodedNorms.size() )
|
|
flags |= UseEncodedNormals;
|
|
|
|
setFlags( flags );
|
|
|
|
// Set color & tvert2 flags if we have an old version
|
|
if (TSShape::smReadVersion < 27)
|
|
{
|
|
if (mColors.size() > 0) setFlags(HasColor);
|
|
if (mTverts2.size() > 0) setFlags(HasTVert2);
|
|
mNumVerts = mVerts.size();
|
|
}
|
|
|
|
tsalloc.checkGuard();
|
|
|
|
if ( tsalloc.allocShape32( 0 ) && TSShape::smReadVersion < 19 )
|
|
computeBounds(); // only do this if we copied the data...
|
|
|
|
createTangents(mVerts, mNorms);
|
|
}
|
|
|
|
void TSMesh::disassemble()
|
|
{
|
|
tsalloc.setGuard();
|
|
|
|
tsalloc.set32( numFrames );
|
|
tsalloc.set32( numMatFrames );
|
|
tsalloc.set32(mParentMesh);
|
|
tsalloc.copyToBuffer32( (S32*)&mBounds, 6 );
|
|
tsalloc.copyToBuffer32( (S32*)&mCenter, 3 );
|
|
tsalloc.set32( (S32)mRadius );
|
|
|
|
bool shouldMakeEditable = TSShape::smVersion < 27 || mVertSize == 0;
|
|
|
|
// Re-create the vectors
|
|
if (shouldMakeEditable)
|
|
{
|
|
makeEditable();
|
|
|
|
// No Offset
|
|
if (TSShape::smVersion >= 27)
|
|
{
|
|
tsalloc.set32(0);
|
|
tsalloc.set32(0);
|
|
tsalloc.set32(0);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Offsetted
|
|
tsalloc.set32(mVertOffset);
|
|
tsalloc.set32(mNumVerts);
|
|
tsalloc.set32(mVertSize);
|
|
AssertFatal(mNumVerts >= vertsPerFrame, "invalid mNumVerts");
|
|
}
|
|
|
|
if (TSShape::smVersion >= 27 && mVertexData.isReady())
|
|
{
|
|
// 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(mVerts.size());
|
|
if (mParentMesh < 0)
|
|
tsalloc.copyToBuffer32((S32*)mVerts.address(), 3 * mVerts.size()); // if no parent mesh, then save off our verts
|
|
|
|
// tverts...
|
|
tsalloc.set32(mTverts.size());
|
|
if (mParentMesh < 0)
|
|
tsalloc.copyToBuffer32((S32*)mTverts.address(), 2 * mTverts.size()); // if no parent mesh, then save off our tverts
|
|
|
|
if (TSShape::smVersion > 25)
|
|
{
|
|
// tverts2...
|
|
tsalloc.set32(mTverts2.size());
|
|
if (mParentMesh < 0)
|
|
tsalloc.copyToBuffer32((S32*)mTverts2.address(), 2 * mTverts2.size()); // if no parent mesh, then save off our tverts
|
|
|
|
// colors
|
|
tsalloc.set32(mColors.size());
|
|
if (mParentMesh < 0)
|
|
tsalloc.copyToBuffer32((S32*)mColors.address(), mColors.size()); // if no parent mesh, then save off our tverts
|
|
}
|
|
|
|
// norms...
|
|
if (mParentMesh < 0) // if no parent mesh, then save off our norms
|
|
tsalloc.copyToBuffer32((S32*)mNorms.address(), 3 * mNorms.size()); // norms.size()==verts.size() or error...
|
|
|
|
// encoded norms...
|
|
if (mParentMesh < 0)
|
|
{
|
|
// if no parent mesh, compute encoded normals and copy over
|
|
for (S32 i = 0; i < mNorms.size(); i++)
|
|
{
|
|
U8 normIdx = mEncodedNorms.size() ? mEncodedNorms[i] : encodeNormal(mNorms[i]);
|
|
tsalloc.copyToBuffer8((S8*)&normIdx, 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
// optimize triangle draw order during disassemble
|
|
{
|
|
FrameTemp<TriListOpt::IndexType> tmpIdxs(mIndices.size());
|
|
for ( S32 i = 0; i < mPrimitives.size(); i++ )
|
|
{
|
|
const TSDrawPrimitive& prim = mPrimitives[i];
|
|
|
|
// only optimize triangle lists (strips and fans are assumed to be already optimized)
|
|
if ( (prim.matIndex & TSDrawPrimitive::TypeMask) == TSDrawPrimitive::Triangles )
|
|
{
|
|
TriListOpt::OptimizeTriangleOrdering(mVerts.size(), prim.numElements,
|
|
mIndices.address() + prim.start, tmpIdxs.address());
|
|
dCopyArray(mIndices.address() + prim.start, tmpIdxs.address(),
|
|
prim.numElements);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (TSShape::smVersion > 25)
|
|
{
|
|
// primitives...
|
|
tsalloc.set32(mPrimitives.size() );
|
|
tsalloc.copyToBuffer32((S32*)mPrimitives.address(),3* mPrimitives.size());
|
|
|
|
// indices...
|
|
tsalloc.set32(mIndices.size());
|
|
tsalloc.copyToBuffer32((S32*)mIndices.address(),mIndices.size());
|
|
}
|
|
else
|
|
{
|
|
// primitives
|
|
tsalloc.set32(mPrimitives.size() );
|
|
for (S32 i=0; i<mPrimitives.size(); i++)
|
|
{
|
|
S16 start = (S16)mPrimitives[i].start;
|
|
S16 numElements = (S16)mPrimitives[i].numElements;
|
|
|
|
tsalloc.copyToBuffer16(&start, 1);
|
|
tsalloc.copyToBuffer16(&numElements, 1);
|
|
tsalloc.copyToBuffer32(&(mPrimitives[i].matIndex), 1);
|
|
}
|
|
|
|
// indices
|
|
tsalloc.set32(mIndices.size());
|
|
Vector<S16> s16_indices(mIndices.size());
|
|
for (S32 i=0; i<mIndices.size(); i++)
|
|
s16_indices.push_back((S16)mIndices[i]);
|
|
tsalloc.copyToBuffer16(s16_indices.address(), s16_indices.size());
|
|
}
|
|
|
|
// merge indices...DEPRECATED
|
|
tsalloc.set32( 0 );
|
|
|
|
// small stuff...
|
|
tsalloc.set32( vertsPerFrame );
|
|
tsalloc.set32( getFlags() );
|
|
|
|
tsalloc.setGuard();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// TSSkinMesh assemble from/ dissemble to memory buffer
|
|
//-----------------------------------------------------------------------------
|
|
void TSSkinMesh::assemble( bool skip )
|
|
{
|
|
// avoid a crash on computeBounds...
|
|
batchData.initialVerts.set( NULL, 0 );
|
|
|
|
TSMesh::assemble( skip );
|
|
|
|
if (TSShape::smReadVersion >= 27)
|
|
{
|
|
maxBones = tsalloc.get32();
|
|
}
|
|
else
|
|
{
|
|
maxBones = -1;
|
|
}
|
|
|
|
S32 sz;
|
|
S32 * ptr32;
|
|
|
|
if (TSShape::smReadVersion < 27)
|
|
{
|
|
sz = tsalloc.get32();
|
|
S32 numVerts = sz;
|
|
ptr32 = getSharedData32(mParentMesh, 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 (mParentMesh < 0)
|
|
tsalloc.getPointer32(numVerts * 3); // advance past norms, don't use
|
|
batchData.initialNorms.set(NULL, 0);
|
|
|
|
ptr8 = getSharedData8(mParentMesh, numVerts, (S8**)smEncodedNormsList.address(), skip);
|
|
mEncodedNorms.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(mParentMesh, 3 * numVerts, (S32**)smNormsList.address(), skip);
|
|
batchData.initialNorms.set((Point3F*)ptr32, numVerts);
|
|
|
|
if (mParentMesh < 0)
|
|
tsalloc.getPointer8(numVerts); // advance past encoded normls, don't use
|
|
|
|
mEncodedNorms.set(NULL, 0);
|
|
}
|
|
else
|
|
{
|
|
// no encoded normals...
|
|
ptr32 = getSharedData32(mParentMesh, 3 * numVerts, (S32**)smNormsList.address(), skip);
|
|
batchData.initialNorms.set((Point3F*)ptr32, numVerts);
|
|
mEncodedNorms.set(NULL, 0);
|
|
}
|
|
|
|
// Sometimes we'll have a mesh with 0 verts but initialVerts is set,
|
|
// so set these accordingly
|
|
if (mVerts.size() == 0)
|
|
{
|
|
mVerts = batchData.initialVerts;
|
|
}
|
|
|
|
if (mNorms.size() == 0)
|
|
{
|
|
mNorms = batchData.initialNorms;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Set from the mesh data
|
|
batchData.initialVerts = mVerts;
|
|
batchData.initialNorms = mNorms;
|
|
}
|
|
|
|
sz = tsalloc.get32();
|
|
ptr32 = getSharedData32(mParentMesh, 16 * sz, (S32**)smInitTransformList.address(), skip );
|
|
batchData.initialTransforms.set( ptr32, sz );
|
|
|
|
sz = tsalloc.get32();
|
|
ptr32 = getSharedData32(mParentMesh, sz, (S32**)smVertexIndexList.address(), skip );
|
|
vertexIndex.set( ptr32, sz );
|
|
|
|
ptr32 = getSharedData32(mParentMesh, sz, (S32**)smBoneIndexList.address(), skip );
|
|
boneIndex.set( ptr32, sz );
|
|
|
|
ptr32 = getSharedData32(mParentMesh, sz, (S32**)smWeightList.address(), skip );
|
|
weight.set( (F32*)ptr32, sz );
|
|
|
|
sz = tsalloc.get32();
|
|
ptr32 = getSharedData32(mParentMesh, sz, (S32**)smNodeIndexList.address(), skip );
|
|
batchData.nodeIndex.set( ptr32, sz );
|
|
|
|
tsalloc.checkGuard();
|
|
|
|
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]);
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// disassemble
|
|
//-----------------------------------------------------------------------------
|
|
void TSSkinMesh::disassemble()
|
|
{
|
|
TSMesh::disassemble();
|
|
|
|
if (TSShape::smVersion >= 27)
|
|
{
|
|
AssertFatal(maxBones != 0, "Skin mesh with no bones? No way!");
|
|
tsalloc.set32(maxBones);
|
|
}
|
|
|
|
if (TSShape::smVersion < 27)
|
|
{
|
|
tsalloc.set32(batchData.initialVerts.size());
|
|
// if we have no parent mesh, then save off our verts & norms
|
|
if (mParentMesh < 0)
|
|
{
|
|
tsalloc.copyToBuffer32((S32*)mVerts.address(), 3 * mVerts.size());
|
|
|
|
// no longer do this here...let tsmesh handle this
|
|
tsalloc.copyToBuffer32((S32*)mNorms.address(), 3 * mNorms.size());
|
|
|
|
// if no parent mesh, compute encoded normals and copy over
|
|
for (S32 i = 0; i < mNorms.size(); i++)
|
|
{
|
|
U8 normIdx = mEncodedNorms.size() ? mEncodedNorms[i] : encodeNormal(mNorms[i]);
|
|
tsalloc.copyToBuffer8((S8*)&normIdx, 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
tsalloc.set32( batchData.initialTransforms.size() );
|
|
if (mParentMesh < 0 )
|
|
tsalloc.copyToBuffer32( (S32*)batchData.initialTransforms.address(), batchData.initialTransforms.size() * 16 );
|
|
|
|
if (!mVertexData.isReady())
|
|
{
|
|
tsalloc.set32(vertexIndex.size());
|
|
|
|
tsalloc.copyToBuffer32((S32*)vertexIndex.address(), vertexIndex.size());
|
|
|
|
tsalloc.copyToBuffer32((S32*)boneIndex.address(), boneIndex.size());
|
|
|
|
tsalloc.copyToBuffer32((S32*)weight.address(), weight.size());
|
|
}
|
|
else
|
|
{
|
|
tsalloc.set32(0);
|
|
}
|
|
|
|
if (TSShape::smVersion < 27)
|
|
{
|
|
if (mParentMesh < 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() );
|
|
if (mParentMesh < 0 )
|
|
tsalloc.copyToBuffer32( (S32*)batchData.nodeIndex.address(), batchData.nodeIndex.size() );
|
|
|
|
tsalloc.setGuard();
|
|
}
|
|
|
|
TSSkinMesh::TSSkinMesh()
|
|
{
|
|
mMeshType = SkinMeshType;
|
|
batchData.initialized = false;
|
|
maxBones = -1;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// find tangent vector
|
|
//-----------------------------------------------------------------------------
|
|
inline void TSMesh::findTangent( U32 index1,
|
|
U32 index2,
|
|
U32 index3,
|
|
Point3F *tan0,
|
|
Point3F *tan1,
|
|
const Vector<Point3F> &_verts)
|
|
{
|
|
const Point3F &v1 = _verts[index1];
|
|
const Point3F &v2 = _verts[index2];
|
|
const Point3F &v3 = _verts[index3];
|
|
|
|
const Point2F &w1 = mTverts[index1];
|
|
const Point2F &w2 = mTverts[index2];
|
|
const Point2F &w3 = mTverts[index3];
|
|
|
|
F32 x1 = v2.x - v1.x;
|
|
F32 x2 = v3.x - v1.x;
|
|
F32 y1 = v2.y - v1.y;
|
|
F32 y2 = v3.y - v1.y;
|
|
F32 z1 = v2.z - v1.z;
|
|
F32 z2 = v3.z - v1.z;
|
|
|
|
F32 s1 = w2.x - w1.x;
|
|
F32 s2 = w3.x - w1.x;
|
|
F32 t1 = w2.y - w1.y;
|
|
F32 t2 = w3.y - w1.y;
|
|
|
|
F32 denom = (s1 * t2 - s2 * t1);
|
|
|
|
if( mFabs( denom ) < 0.0001f )
|
|
{
|
|
// handle degenerate triangles from strips
|
|
if (denom<0) denom = -0.0001f;
|
|
else denom = 0.0001f;
|
|
}
|
|
F32 r = 1.0f / denom;
|
|
|
|
Point3F sdir( (t2 * x1 - t1 * x2) * r,
|
|
(t2 * y1 - t1 * y2) * r,
|
|
(t2 * z1 - t1 * z2) * r );
|
|
|
|
Point3F tdir( (s1 * x2 - s2 * x1) * r,
|
|
(s1 * y2 - s2 * y1) * r,
|
|
(s1 * z2 - s2 * z1) * r );
|
|
|
|
|
|
tan0[index1] += sdir;
|
|
tan1[index1] += tdir;
|
|
|
|
tan0[index2] += sdir;
|
|
tan1[index2] += tdir;
|
|
|
|
tan0[index3] += sdir;
|
|
tan1[index3] += tdir;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// create array of tangent vectors
|
|
//-----------------------------------------------------------------------------
|
|
void TSMesh::createTangents(const Vector<Point3F> &_verts, const Vector<Point3F> &_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 )
|
|
return;
|
|
|
|
if( numVerts != numNorms)
|
|
return;
|
|
|
|
Vector<Point3F> tan0;
|
|
tan0.setSize( numVerts * 2 );
|
|
|
|
Point3F *tan1 = tan0.address() + numVerts;
|
|
dMemset( tan0.address(), 0, sizeof(Point3F) * 2 * numVerts );
|
|
|
|
U32 numPrimatives = mPrimitives.size();
|
|
|
|
for (S32 i = 0; i < numPrimatives; i++ )
|
|
{
|
|
const TSDrawPrimitive & draw = mPrimitives[i];
|
|
GFXPrimitiveType drawType = getDrawType( draw.matIndex >> 30 );
|
|
|
|
U32 p1Index = 0;
|
|
U32 p2Index = 0;
|
|
|
|
U32 *baseIdx = &mIndices[draw.start];
|
|
|
|
const U32 numElements = (U32)draw.numElements;
|
|
|
|
switch( drawType )
|
|
{
|
|
case GFXTriangleList:
|
|
{
|
|
for( U32 j = 0; j < numElements; j += 3 )
|
|
findTangent( baseIdx[j], baseIdx[j + 1], baseIdx[j + 2], tan0.address(), tan1, _verts );
|
|
break;
|
|
}
|
|
|
|
case GFXTriangleStrip:
|
|
{
|
|
p1Index = baseIdx[0];
|
|
p2Index = baseIdx[1];
|
|
for( U32 j = 2; j < numElements; j++ )
|
|
{
|
|
findTangent( p1Index, p2Index, baseIdx[j], tan0.address(), tan1, _verts );
|
|
p1Index = p2Index;
|
|
p2Index = baseIdx[j];
|
|
}
|
|
break;
|
|
}
|
|
|
|
default:
|
|
AssertFatal( false, "TSMesh::createTangents: unknown primitive type!" );
|
|
}
|
|
}
|
|
|
|
mTangents.setSize( numVerts );
|
|
|
|
// fill out final info from accumulated basis data
|
|
for( U32 i = 0; i < numVerts; i++ )
|
|
{
|
|
const Point3F &n = _norms[i];
|
|
const Point3F &t = tan0[i];
|
|
const Point3F &b = tan1[i];
|
|
|
|
Point3F tempPt = t - n * mDot( n, t );
|
|
tempPt.normalize();
|
|
mTangents[i] = tempPt;
|
|
|
|
Point3F cp;
|
|
mCross( n, t, &cp );
|
|
|
|
mTangents[i].w = (mDot( cp, b ) < 0.0f) ? -1.0f : 1.0f;
|
|
}
|
|
}
|
|
|
|
void TSMesh::convertToVertexData()
|
|
{
|
|
if (!mVertexData.isReady())
|
|
{
|
|
_convertToVertexData(mVertexData, mVerts, mNorms);
|
|
}
|
|
}
|
|
|
|
void TSSkinMesh::convertToVertexData()
|
|
{
|
|
if (!mVertexData.isReady())
|
|
{
|
|
// Batch data required here
|
|
createSkinBatchData();
|
|
|
|
// Dump verts to buffer
|
|
_convertToVertexData(mVertexData, batchData.initialVerts, batchData.initialNorms);
|
|
|
|
// Setup bones too
|
|
setupVertexTransforms();
|
|
}
|
|
}
|
|
|
|
void TSMesh::copySourceVertexDataFrom(const TSMesh* srcMesh)
|
|
{
|
|
mVerts = srcMesh->mVerts;
|
|
mTverts = srcMesh->mTverts;
|
|
mNorms = srcMesh->mNorms;
|
|
mColors = srcMesh->mColors;
|
|
mTverts2 = srcMesh->mTverts2;
|
|
|
|
if (mVerts.size() == 0)
|
|
{
|
|
bool hasTVert2 = srcMesh->getHasTVert2();
|
|
bool hasColor = srcMesh->getHasColor();
|
|
|
|
mVerts.setSize(srcMesh->mNumVerts);
|
|
mTverts.setSize(srcMesh->mNumVerts);
|
|
mNorms.setSize(srcMesh->mNumVerts);
|
|
|
|
if (hasColor)
|
|
mColors.setSize(mNumVerts);
|
|
if (hasTVert2)
|
|
mTverts2.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);
|
|
mVerts[i] = cv.vert();
|
|
mTverts[i] = cv.tvert();
|
|
mNorms[i] = cv.normal();
|
|
|
|
if (hasColor)
|
|
cvc.color().getColor(&mColors[i]);
|
|
if (hasTVert2)
|
|
mTverts2[i] = cvc.tvert2();
|
|
}
|
|
}
|
|
}
|
|
|
|
void TSSkinMesh::copySourceVertexDataFrom(const TSMesh* srcMesh)
|
|
{
|
|
TSMesh::copySourceVertexDataFrom(srcMesh);
|
|
|
|
if (srcMesh->getMeshType() == TSMesh::SkinMeshType)
|
|
{
|
|
const TSSkinMesh* srcSkinMesh = static_cast<const TSSkinMesh*>(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 : mVerts.size();
|
|
}
|
|
|
|
void TSMesh::_convertToVertexData(TSMeshVertexArray &outArray, const Vector<Point3F> &_verts, const Vector<Point3F> &_norms)
|
|
{
|
|
// Update tangents list
|
|
createTangents(mVerts, mNorms);
|
|
|
|
AssertFatal(_verts.size() == mNumVerts, "vert count mismatch");
|
|
AssertFatal(!getHasColor() || mColors.size() == _verts.size(), "Vector of color elements should be the same size as other vectors");
|
|
AssertFatal(!getHasTVert2() || mTverts2.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() == mTangents.size(),
|
|
"Vectors: verts, norms, tangents must all be the same size");
|
|
AssertFatal(mVertSize == outArray.vertSize(), "Size inconsistency");
|
|
|
|
if (mNumVerts == 0)
|
|
return;
|
|
|
|
bool needWeightSet = outArray.getBoneOffset() != 0;
|
|
|
|
bool hasColor = getHasColor();
|
|
bool hasTVert2 = getHasTVert2();
|
|
|
|
dMemset(&outArray.getBase(0), '\0', mVertSize * mNumVerts);
|
|
|
|
for (U32 i = 0; i < mNumVerts; i++)
|
|
{
|
|
__TSMeshVertexBase &v = outArray.getBase(i);
|
|
v.vert(_verts[i]);
|
|
v.normal(_norms[i]);
|
|
v.tangent(mTangents[i]);
|
|
|
|
if (i < mTverts.size())
|
|
v.tvert(mTverts[i]);
|
|
|
|
if (hasTVert2 || hasColor)
|
|
{
|
|
__TSMeshVertex_3xUVColor &vc = outArray.getColor(i);
|
|
if (hasTVert2 && i < mTverts2.size())
|
|
vc.tvert2(mTverts2[i]);
|
|
if (hasColor && i < mColors.size())
|
|
vc.color(mColors[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 hasVerts = mVerts.size() != 0;
|
|
|
|
if(mVertexData.isReady() && !hasVerts)
|
|
{
|
|
copySourceVertexDataFrom(this);
|
|
}
|
|
|
|
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 hasVerts = mVerts.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 (mVerts.size() == 0)
|
|
return;
|
|
|
|
if (mColors.empty())
|
|
clearFlags(HasColor);
|
|
else
|
|
setFlags(HasColor);
|
|
|
|
if (mTverts2.empty())
|
|
clearFlags(HasTVert2);
|
|
else
|
|
setFlags(HasTVert2);
|
|
|
|
mVerts.free_memory();
|
|
mNorms.free_memory();
|
|
mTangents.free_memory();
|
|
mTverts.free_memory();
|
|
mTverts2.free_memory();
|
|
mColors.free_memory();
|
|
}
|
|
|
|
void TSMesh::updateMeshFlags()
|
|
{
|
|
// Make sure flags are correct
|
|
if (mColors.empty())
|
|
clearFlags(HasColor);
|
|
else
|
|
setFlags(HasColor);
|
|
|
|
if (mTverts2.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; i<numBones; i++)
|
|
{
|
|
fmt.addElement(GFXSemantic::BLENDINDICES, GFXDeclType_UByte4, i);
|
|
fmt.addElement(GFXSemantic::BLENDWEIGHT, GFXDeclType_Float4, i);
|
|
}
|
|
}
|
|
|
|
void TSBasicVertexFormat::calculateSize()
|
|
{
|
|
GFXVertexFormat fmt;
|
|
vertexSize = 0;
|
|
getFormat(fmt);
|
|
vertexSize = fmt.getSizeInBytes();
|
|
}
|
|
|
|
void TSBasicVertexFormat::writeAlloc(TSShapeAlloc* alloc)
|
|
{
|
|
alloc->set16(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() || (colorOffset != -1);
|
|
hasTexcoord2 = mesh->getHasTVert2() || (texCoordOffset != -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);
|
|
}
|
|
}
|