tsMesh.cpp

Engine/source/ts/tsMesh.cpp

More...

Public Defines

define
getDrawType(a) ([])
define

Public Functions

unwindStrip(const S32 * indices, S32 numElements, Vector< S32 > & triIndices)

Detailed Description

Public Defines

getDrawType(a) ([])
tsalloc() 

Public Variables

GFXPrimitiveType drawTypes []
Vector< Point3F > gNormalStore 

Public Functions

tsForceFaceCamera(MatrixF * mat, const Point3F * objScale)

unwindStrip(const S32 * indices, S32 numElements, Vector< S32 > & triIndices)

   1
   2//-----------------------------------------------------------------------------
   3// Copyright (c) 2012 GarageGames, LLC
   4//
   5// Permission is hereby granted, free of charge, to any person obtaining a copy
   6// of this software and associated documentation files (the "Software"), to
   7// deal in the Software without restriction, including without limitation the
   8// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
   9// sell copies of the Software, and to permit persons to whom the Software is
  10// furnished to do so, subject to the following conditions:
  11//
  12// The above copyright notice and this permission notice shall be included in
  13// all copies or substantial portions of the Software.
  14//
  15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  20// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  21// IN THE SOFTWARE.
  22//-----------------------------------------------------------------------------
  23
  24#include "platform/platform.h"
  25#include "ts/tsMesh.h"
  26
  27#include "ts/tsMeshIntrinsics.h"
  28#include "ts/tsDecal.h"
  29#include "ts/tsSortedMesh.h"
  30#include "ts/tsShape.h"
  31#include "ts/tsShapeInstance.h"
  32#include "ts/tsRenderState.h"
  33#include "ts/tsMaterialList.h"
  34#include "ts/instancingMatHook.h"
  35#include "math/mMath.h"
  36#include "math/mathIO.h"
  37#include "math/mathUtils.h"
  38#include "console/console.h"
  39#include "scene/sceneObject.h"
  40#include "core/bitRender.h"
  41#include "collision/convex.h"
  42#include "collision/optimizedPolyList.h"
  43#include "core/frameAllocator.h"
  44#include "platform/profiler.h"
  45#include "materials/sceneData.h"
  46#include "materials/materialManager.h"
  47#include "scene/sceneManager.h"
  48#include "scene/sceneRenderState.h"
  49#include "materials/matInstance.h"
  50#include "materials/materialFeatureTypes.h"
  51#include "renderInstance/renderPassManager.h"
  52#include "materials/customMaterialDefinition.h"
  53#include "gfx/util/triListOpt.h"
  54#include "util/triRayCheck.h"
  55
  56#include "opcode/Opcode.h"
  57
  58GFXPrimitiveType drawTypes[] = { GFXTriangleList, GFXTriangleStrip };
  59#define getDrawType(a) (drawTypes[a])
  60
  61
  62// structures used to share data between detail levels...
  63// used (and valid) during load only
  64Vector<Point3F*> TSMesh::smVertsList;
  65Vector<Point3F*> TSMesh::smNormsList;
  66Vector<U8*>      TSMesh::smEncodedNormsList;
  67Vector<Point2F*> TSMesh::smTVertsList;
  68Vector<Point2F*> TSMesh::smTVerts2List;
  69Vector<ColorI*> TSMesh::smColorsList;
  70
  71Vector<bool>     TSMesh::smDataCopied;
  72
  73Vector<MatrixF*> TSSkinMesh::smInitTransformList;
  74Vector<S32*>     TSSkinMesh::smVertexIndexList;
  75Vector<S32*>     TSSkinMesh::smBoneIndexList;
  76Vector<F32*>     TSSkinMesh::smWeightList;
  77Vector<S32*>     TSSkinMesh::smNodeIndexList;
  78
  79bool TSSkinMesh::smDebugSkinVerts = false;
  80
  81Vector<Point3F> gNormalStore;
  82
  83bool TSMesh::smUseTriangles = false; // convert all primitives to triangle lists on load
  84bool TSMesh::smUseOneStrip  = true; // join triangle strips into one long strip on load
  85S32  TSMesh::smMinStripSize = 1;     // smallest number of _faces_ allowed per strip (all else put in tri list)
  86bool TSMesh::smUseEncodedNormals = false;
  87
  88const F32 TSMesh::VISIBILITY_EPSILON = 0.0001f;
  89
  90S32 TSMesh::smMaxInstancingVerts = 200;
  91MatrixF TSMesh::smDummyNodeTransform(1);
  92
  93// quick function to force object to face camera -- currently throws out roll :(
  94void tsForceFaceCamera( MatrixF *mat, const Point3F *objScale )
  95{
  96   Point4F p;
  97   mat->getColumn( 3, &p );
  98   mat->identity();
  99   mat->setColumn( 3, p );
 100
 101   if ( objScale )
 102   {
 103      MatrixF scale( true );
 104      scale.scale( *objScale );
 105      mat->mul( scale );
 106   }
 107}
 108
 109//-----------------------------------------------------
 110// TSMesh render methods
 111//-----------------------------------------------------
 112
 113void TSMesh::render( TSVertexBufferHandle &instanceVB )
 114{
 115   innerRender(instanceVB, mPB);
 116}
 117
 118void TSMesh::innerRender( TSVertexBufferHandle &vb, GFXPrimitiveBufferHandle &pb )
 119{
 120   if ( !vb.isValid() || !pb.isValid() )
 121      return;
 122
 123   GFX->setVertexBuffer( vb );
 124   GFX->setPrimitiveBuffer( pb );
 125   
 126   for( U32 p = 0; p < mPrimitives.size(); p++ )
 127      GFX->drawPrimitive( p );
 128}
 129
 130
 131void TSMesh::render( TSMaterialList *materials, 
 132                     const TSRenderState &rdata, 
 133                     bool isSkinDirty,
 134                     const Vector<MatrixF> &transforms, 
 135                     TSVertexBufferHandle &vertexBuffer,
 136                     const char *meshName)
 137{
 138   // These are only used by TSSkinMesh.
 139   TORQUE_UNUSED( isSkinDirty );   
 140   TORQUE_UNUSED( transforms );
 141
 142   // Pass our shared VB.
 143   innerRender(materials, rdata, vertexBuffer, mPB, meshName);
 144}
 145
 146void TSMesh::innerRender( TSMaterialList *materials, const TSRenderState &rdata, TSVertexBufferHandle &vb, GFXPrimitiveBufferHandle &pb, const char *meshName )
 147{
 148   PROFILE_SCOPE( TSMesh_InnerRender );
 149
 150   if( vertsPerFrame <= 0 ) 
 151      return;
 152
 153   F32 meshVisibility = rdata.getFadeOverride() * mVisibility;
 154   if ( meshVisibility < VISIBILITY_EPSILON )
 155      return;
 156
 157   const SceneRenderState *state = rdata.getSceneState();
 158   RenderPassManager *renderPass = state->getRenderPass();
 159
 160   MeshRenderInst *coreRI = renderPass->allocInst<MeshRenderInst>();
 161   coreRI->type = RenderPassManager::RIT_Mesh;
 162#ifdef TORQUE_ENABLE_GFXDEBUGEVENTS
 163   coreRI->meshName = meshName;
 164#endif
 165
 166   // Pass accumulation texture along.
 167   coreRI->accuTex = rdata.getAccuTex();
 168
 169   const MatrixF &objToWorld = GFX->getWorldMatrix();
 170
 171   // Sort by the center point or the bounds.
 172   if ( rdata.useOriginSort() )
 173      coreRI->sortDistSq = ( objToWorld.getPosition() - state->getCameraPosition() ).lenSquared();
 174   else
 175   {
 176      Box3F rBox = mBounds;
 177      objToWorld.mul( rBox );
 178      coreRI->sortDistSq = rBox.getSqDistanceToPoint( state->getCameraPosition() );      
 179   }
 180
 181   if (getFlags(Billboard))
 182   {
 183      Point3F camPos = state->getDiffuseCameraPosition();
 184      Point3F objPos;
 185      objToWorld.getColumn(3, &objPos);
 186      Point3F targetVector = camPos - objPos;
 187      if(getFlags(BillboardZAxis))
 188         targetVector.z = 0.0f;
 189      targetVector.normalize();
 190      MatrixF orient = MathUtils::createOrientFromDir(targetVector);
 191      orient.setPosition(objPos);
 192      orient.scale(objToWorld.getScale());
 193
 194      coreRI->objectToWorld = renderPass->allocUniqueXform( orient );
 195   }
 196   else
 197      coreRI->objectToWorld = renderPass->allocUniqueXform( objToWorld );
 198
 199   coreRI->worldToCamera = renderPass->allocSharedXform(RenderPassManager::View);
 200   coreRI->projection = renderPass->allocSharedXform(RenderPassManager::Projection);
 201
 202   AssertFatal( vb.isValid(), "TSMesh::innerRender() - Got invalid vertex buffer!" );
 203   AssertFatal( pb.isValid(), "TSMesh::innerRender() - Got invalid primitive buffer!" );
 204
 205   coreRI->vertBuff = &vb;
 206   coreRI->primBuff = &pb;
 207   coreRI->defaultKey2 = (uintptr_t) coreRI->vertBuff;
 208
 209   coreRI->materialHint = rdata.getMaterialHint();
 210
 211   coreRI->mCustomShaderData = rdata.getCustomShaderBinding();
 212
 213   coreRI->visibility = meshVisibility;  
 214   coreRI->cubemap = rdata.getCubemap();
 215
 216   if ( getMeshType() == TSMesh::SkinMeshType )
 217   {
 218      rdata.getNodeTransforms(&coreRI->mNodeTransforms, &coreRI->mNodeTransformCount);
 219   }
 220   else
 221   {
 222      coreRI->mNodeTransforms = &TSMesh::smDummyNodeTransform;
 223      coreRI->mNodeTransformCount = 1;
 224   }
 225
 226   // NOTICE: SFXBB is removed and refraction is disabled!
 227   //coreRI->backBuffTex = GFX->getSfxBackBuffer();
 228
 229   for ( S32 i = 0; i < mPrimitives.size(); i++ )
 230   {
 231      const TSDrawPrimitive &draw = mPrimitives[i];
 232
 233      // We need to have a material.
 234      if ( draw.matIndex & TSDrawPrimitive::NoMaterial )
 235         continue;
 236
 237#ifdef TORQUE_DEBUG_BREAK_INSPECT
 238      // for inspection if you happen to be running in a debugger and can't do bit 
 239      // operations in your head.
 240      S32 triangles = draw.matIndex & TSDrawPrimitive::Triangles;
 241      S32 strip = draw.matIndex & TSDrawPrimitive::Strip;
 242      S32 fan = draw.matIndex & TSDrawPrimitive::Fan;
 243      S32 indexed = draw.matIndex & TSDrawPrimitive::Indexed;
 244      S32 type = draw.matIndex & TSDrawPrimitive::TypeMask;
 245      TORQUE_UNUSED(triangles);
 246      TORQUE_UNUSED(strip);
 247      TORQUE_UNUSED(fan);
 248      TORQUE_UNUSED(indexed);
 249      TORQUE_UNUSED(type);
 250      //define TORQUE_DEBUG_BREAK_INSPECT, and insert debug break here to inspect the above elements at runtime
 251#endif
 252
 253      const U32 matIndex = draw.matIndex & TSDrawPrimitive::MaterialMask;
 254      BaseMatInstance *matInst = materials->getMaterialInst( matIndex );
 255
 256#ifndef TORQUE_OS_MAC
 257
 258      // Get the instancing material if this mesh qualifies.
 259      if (mMeshType != SkinMeshType && pb->mPrimitiveArray[i].numVertices < smMaxInstancingVerts )
 260         if (matInst && !matInst->getFeatures().hasFeature(MFT_HardwareSkinning))
 261            matInst = InstancingMaterialHook::getInstancingMat( matInst );
 262
 263#endif
 264
 265      // If we don't have a material instance after the overload then
 266      // there is nothing to render... skip this primitive.
 267      matInst = state->getOverrideMaterial( matInst );
 268      if ( !matInst || !matInst->isValid())
 269         continue;
 270
 271      // If the material needs lights then gather them
 272      // here once and set them on the core render inst.
 273      if ( matInst->isForwardLit() && !coreRI->lights[0] && rdata.getLightQuery() )
 274         rdata.getLightQuery()->getLights( coreRI->lights, 8 );
 275
 276      MeshRenderInst *ri = renderPass->allocInst<MeshRenderInst>();
 277      *ri = *coreRI;
 278
 279      ri->matInst = matInst;
 280      ri->defaultKey = matInst->getStateHint();
 281      ri->primBuffIndex = mPrimBufferOffset + i;
 282
 283      // Translucent materials need the translucent type.
 284      if ( matInst->getMaterial()->isTranslucent() && (!(matInst->getMaterial()->isAlphatest() && state->isShadowPass())))
 285      {
 286         ri->type = RenderPassManager::RIT_Translucent;
 287         ri->translucentSort = true;
 288      }
 289
 290      renderPass->addInst( ri );
 291   }
 292}
 293
 294const Point3F * TSMesh::getNormals( S32 firstVert )
 295{
 296   if ( getFlags( UseEncodedNormals ) )
 297   {
 298      gNormalStore.setSize( vertsPerFrame );
 299      for ( S32 i = 0; i < mEncodedNorms.size(); i++ )
 300         gNormalStore[i] = decodeNormal(mEncodedNorms[ i + firstVert ] );
 301
 302      return gNormalStore.address();
 303   }
 304
 305   return &mNorms[firstVert];
 306}
 307
 308//-----------------------------------------------------
 309// TSMesh collision methods
 310//-----------------------------------------------------
 311
 312bool TSMesh::buildPolyList( S32 frame, AbstractPolyList *polyList, U32 &surfaceKey, TSMaterialList *materials )
 313{
 314   S32 firstVert  = vertsPerFrame * frame, i, base = 0;
 315   bool hasTVert2 = getHasTVert2();
 316
 317   // add the verts...
 318   if ( vertsPerFrame )
 319   {
 320      if ( mVertexData.isReady() )
 321      {
 322         OptimizedPolyList* opList = dynamic_cast<OptimizedPolyList*>(polyList);
 323         if ( opList )
 324         {
 325            base = opList->mVertexList.size();
 326            for ( i = 0; i < vertsPerFrame; i++ )
 327            {
 328               // Don't use vertex() method as we want to retain the original indices
 329               OptimizedPolyList::VertIndex vert;
 330               vert.vertIdx   = opList->insertPoint( mVertexData.getBase( i + firstVert ).vert() );
 331               vert.normalIdx = opList->insertNormal( mVertexData.getBase( i + firstVert ).normal() );
 332               vert.uv0Idx    = opList->insertUV0( mVertexData.getBase( i + firstVert ).tvert() );
 333               if ( hasTVert2 )
 334                  vert.uv1Idx = opList->insertUV1( mVertexData.getColor( i + firstVert ).tvert2() );
 335
 336               opList->mVertexList.push_back( vert );
 337            }
 338         }
 339         else
 340         {
 341            base = polyList->addPointAndNormal( mVertexData.getBase( firstVert ).vert(), mVertexData.getBase( firstVert ).normal() );
 342            for ( i = 1; i < vertsPerFrame; i++ )
 343            {
 344               polyList->addPointAndNormal( mVertexData.getBase( i + firstVert ).vert(), mVertexData.getBase( i + firstVert ).normal() );
 345            }
 346         }
 347      }
 348      else
 349      {
 350         OptimizedPolyList* opList = dynamic_cast<OptimizedPolyList*>(polyList);
 351         if ( opList )
 352         {
 353            base = opList->mVertexList.size();
 354            for ( i = 0; i < vertsPerFrame; i++ )
 355            {
 356               // Don't use vertex() method as we want to retain the original indices
 357               OptimizedPolyList::VertIndex vert;
 358               vert.vertIdx   = opList->insertPoint( mVerts[ i + firstVert ] );
 359               vert.normalIdx = opList->insertNormal( mNorms[ i + firstVert ] );
 360               vert.uv0Idx    = opList->insertUV0( mTverts[ i + firstVert ] );
 361               if ( hasTVert2 )
 362                  vert.uv1Idx = opList->insertUV1(mTverts[ i + firstVert ] );
 363
 364               opList->mVertexList.push_back( vert );
 365            }
 366         }
 367         else
 368         {
 369            base = polyList->addPointAndNormal( mVerts[firstVert], mNorms[firstVert] );
 370            for ( i = 1; i < vertsPerFrame; i++ )
 371               polyList->addPointAndNormal(mVerts[ i + firstVert ], mNorms[ i + firstVert ] );
 372         }
 373      }
 374   }
 375
 376   // add the polys...
 377   for ( i = 0; i < mPrimitives.size(); i++ )
 378   {
 379      TSDrawPrimitive & draw = mPrimitives[i];
 380      U32 start = draw.start;
 381
 382      AssertFatal( draw.matIndex & TSDrawPrimitive::Indexed,"TSMesh::buildPolyList (1)" );
 383
 384      U32 matIndex = draw.matIndex & TSDrawPrimitive::MaterialMask;
 385      BaseMatInstance* material = ( materials ? materials->getMaterialInst( matIndex ) : 0 );
 386
 387      // gonna depend on what kind of primitive it is...
 388      if ( (draw.matIndex & TSDrawPrimitive::TypeMask) == TSDrawPrimitive::Triangles )
 389      {
 390         for ( S32 j = 0; j < draw.numElements; )
 391         {
 392            U32 idx0 = base + mIndices[start + j + 0];
 393            U32 idx1 = base + mIndices[start + j + 1];
 394            U32 idx2 = base + mIndices[start + j + 2];
 395            polyList->begin(material,surfaceKey++);
 396            polyList->vertex( idx0 );
 397            polyList->vertex( idx1 );
 398            polyList->vertex( idx2 );
 399            polyList->plane( idx0, idx1, idx2 );
 400            polyList->end();
 401            j += 3;
 402         }
 403      }
 404      else
 405      {
 406         AssertFatal( (draw.matIndex & TSDrawPrimitive::TypeMask) == TSDrawPrimitive::Strip,"TSMesh::buildPolyList (2)" );
 407
 408         U32 idx0 = base + mIndices[start + 0];
 409         U32 idx1;
 410         U32 idx2 = base + mIndices[start + 1];
 411         U32 * nextIdx = &idx1;
 412         for ( S32 j = 2; j < draw.numElements; j++ )
 413         {
 414            *nextIdx = idx2;
 415            // nextIdx = (j%2)==0 ? &idx0 : &idx1;
 416            nextIdx = (U32*) ( (dsize_t)nextIdx ^ (dsize_t)&idx0 ^ (dsize_t)&idx1);
 417            idx2 = base + mIndices[start + j];
 418            if ( idx0 == idx1 || idx0 == idx2 || idx1 == idx2 )
 419               continue;
 420
 421            polyList->begin( material, surfaceKey++ );
 422            polyList->vertex( idx0 );
 423            polyList->vertex( idx1 );
 424            polyList->vertex( idx2 );
 425            polyList->plane( idx0, idx1, idx2 );
 426            polyList->end();
 427         }
 428      }
 429   }
 430   return true;
 431}
 432
 433bool TSMesh::getFeatures( S32 frame, const MatrixF& mat, const VectorF&, ConvexFeature* cf, U32& )
 434{
 435   S32 firstVert = vertsPerFrame * frame;
 436   S32 i;
 437   S32 base = cf->mVertexList.size();
 438
 439   for ( i = 0; i < vertsPerFrame; i++ ) 
 440   {
 441      cf->mVertexList.increment();
 442      mat.mulP( mVertexData.getBase(firstVert + i).vert(), &cf->mVertexList.last() );
 443   }
 444
 445   // add the polys...
 446   for ( i = 0; i < mPrimitives.size(); i++ )
 447   {
 448      TSDrawPrimitive & draw = mPrimitives[i];
 449      U32 start = draw.start;
 450
 451      AssertFatal( draw.matIndex & TSDrawPrimitive::Indexed,"TSMesh::buildPolyList (1)" );
 452
 453      // gonna depend on what kind of primitive it is...
 454      if ( (draw.matIndex & TSDrawPrimitive::TypeMask) == TSDrawPrimitive::Triangles)
 455      {
 456         for ( S32 j = 0; j < draw.numElements; j += 3 )
 457         {
 458            PlaneF plane( cf->mVertexList[base + mIndices[start + j + 0]],
 459                          cf->mVertexList[base + mIndices[start + j + 1]],
 460                          cf->mVertexList[base + mIndices[start + j + 2]]);
 461
 462            cf->mFaceList.increment();
 463
 464            ConvexFeature::Face& lastFace = cf->mFaceList.last();
 465            lastFace.normal = plane;
 466
 467            lastFace.vertex[0] = base + mIndices[start + j + 0];
 468            lastFace.vertex[1] = base + mIndices[start + j + 1];
 469            lastFace.vertex[2] = base + mIndices[start + j + 2];
 470
 471            for ( U32 l = 0; l < 3; l++ ) 
 472            {
 473               U32 newEdge0, newEdge1;
 474               U32 zero = base + mIndices[start + j + l];
 475               U32 one  = base + mIndices[start + j + ((l+1)%3)];
 476               newEdge0 = getMin( zero, one );
 477               newEdge1 = getMax( zero, one );
 478               bool found = false;
 479               for ( S32 k = 0; k < cf->mEdgeList.size(); k++ ) 
 480               {
 481                  if ( cf->mEdgeList[k].vertex[0] == newEdge0 &&
 482                       cf->mEdgeList[k].vertex[1] == newEdge1) 
 483                  {
 484                     found = true;
 485                     break;
 486                  }
 487               }
 488
 489               if ( !found ) 
 490               {
 491                  cf->mEdgeList.increment();
 492                  cf->mEdgeList.last().vertex[0] = newEdge0;
 493                  cf->mEdgeList.last().vertex[1] = newEdge1;
 494               }
 495            }
 496         }
 497      }
 498      else
 499      {
 500         AssertFatal( (draw.matIndex & TSDrawPrimitive::TypeMask) == TSDrawPrimitive::Strip,"TSMesh::buildPolyList (2)" );
 501
 502         U32 idx0 = base + mIndices[start + 0];
 503         U32 idx1;
 504         U32 idx2 = base + mIndices[start + 1];
 505         U32 * nextIdx = &idx1;
 506         for ( S32 j = 2; j < draw.numElements; j++ )
 507         {
 508            *nextIdx = idx2;
 509            nextIdx = (U32*) ( (dsize_t)nextIdx ^ (dsize_t)&idx0 ^ (dsize_t)&idx1);
 510            idx2 = base + mIndices[start + j];
 511            if ( idx0 == idx1 || idx0 == idx2 || idx1 == idx2 )
 512               continue;
 513
 514            PlaneF plane( cf->mVertexList[idx0],
 515                          cf->mVertexList[idx1],
 516                          cf->mVertexList[idx2] );
 517
 518            cf->mFaceList.increment();
 519            cf->mFaceList.last().normal = plane;
 520
 521            cf->mFaceList.last().vertex[0] = idx0;
 522            cf->mFaceList.last().vertex[1] = idx1;
 523            cf->mFaceList.last().vertex[2] = idx2;
 524
 525            U32 newEdge0, newEdge1;
 526            newEdge0 = getMin( idx0, idx1 );
 527            newEdge1 = getMax( idx0, idx1 );
 528            bool found = false;
 529            S32 k;
 530            for ( k = 0; k < cf->mEdgeList.size(); k++ ) 
 531            {
 532               ConvexFeature::Edge currentEdge = cf->mEdgeList[k];
 533               if (currentEdge.vertex[0] == newEdge0 &&
 534                  currentEdge.vertex[1] == newEdge1)
 535               {
 536                  found = true;
 537                  break;
 538               }
 539            }
 540
 541            if ( !found ) 
 542            {
 543               cf->mEdgeList.increment();
 544               cf->mEdgeList.last().vertex[0] = newEdge0;
 545               cf->mEdgeList.last().vertex[1] = newEdge1;
 546            }
 547
 548            newEdge0 = getMin( idx1, idx2 );
 549            newEdge1 = getMax( idx1, idx2 );
 550            found = false;
 551            for ( k = 0; k < cf->mEdgeList.size(); k++ ) 
 552            {
 553               if ( cf->mEdgeList[k].vertex[0] == newEdge0 &&
 554                    cf->mEdgeList[k].vertex[1] == newEdge1 ) 
 555               {
 556                  found = true;
 557                  break;
 558               }
 559            }
 560
 561            if ( !found ) 
 562            {
 563               cf->mEdgeList.increment();
 564               cf->mEdgeList.last().vertex[0] = newEdge0;
 565               cf->mEdgeList.last().vertex[1] = newEdge1;
 566            }
 567
 568            newEdge0 = getMin(idx0, idx2);
 569            newEdge1 = getMax(idx0, idx2);
 570            found = false;
 571            for ( k = 0; k < cf->mEdgeList.size(); k++ ) 
 572            {
 573               if ( cf->mEdgeList[k].vertex[0] == newEdge0 &&
 574                    cf->mEdgeList[k].vertex[1] == newEdge1 ) 
 575               {
 576                  found = true;
 577                  break;
 578               }
 579            }
 580
 581            if ( !found ) 
 582            {
 583               cf->mEdgeList.increment();
 584               cf->mEdgeList.last().vertex[0] = newEdge0;
 585               cf->mEdgeList.last().vertex[1] = newEdge1;
 586            }
 587         }
 588      }
 589   }
 590
 591   return false;
 592}
 593
 594
 595void TSMesh::support( S32 frame, const Point3F &v, F32 *currMaxDP, Point3F *currSupport )
 596{
 597   if ( vertsPerFrame == 0 )
 598      return;
 599
 600   U32 waterMark = FrameAllocator::getWaterMark();
 601   F32* pDots = (F32*)FrameAllocator::alloc( sizeof(F32) * vertsPerFrame );
 602
 603   S32 firstVert = vertsPerFrame * frame;
 604   m_point3F_bulk_dot( &v.x,
 605                       &mVertexData.getBase(firstVert).vert().x,
 606                       vertsPerFrame,
 607                       mVertexData.vertSize(),
 608                       pDots );
 609
 610   F32 localdp = *currMaxDP;
 611   S32 index   = -1;
 612
 613   for ( S32 i = 0; i < vertsPerFrame; i++ )
 614   {
 615      if ( pDots[i] > localdp )
 616      {
 617         localdp = pDots[i];
 618         index   = i;
 619      }
 620   }
 621
 622   FrameAllocator::setWaterMark(waterMark);
 623
 624   if ( index != -1 )
 625   {
 626      *currMaxDP   = localdp;
 627      *currSupport = mVertexData.getBase(index + firstVert).vert();
 628   }
 629}
 630
 631bool TSMesh::castRay( S32 frame, const Point3F & start, const Point3F & end, RayInfo * rayInfo, TSMaterialList* materials )
 632{
 633   if ( mPlaneNormals.empty() )
 634      buildConvexHull(); // if haven't done it yet...
 635
 636   // Keep track of startTime and endTime.  They start out at just under 0 and just over 1, respectively.
 637   // As we check against each plane, prune start and end times back to represent current intersection of
 638   // line with all the planes (or rather with all the half-spaces defined by the planes).
 639   // But, instead of explicitly keeping track of startTime and endTime, keep track as numerator and denominator
 640   // so that we can avoid as many divisions as possible.
 641
 642   //   F32 startTime = -0.01f;
 643   F32 startNum = -0.01f;
 644   F32 startDen =  1.00f;
 645   //   F32 endTime   = 1.01f;
 646   F32 endNum = 1.01f;
 647   F32 endDen = 1.00f;
 648
 649   S32 curPlane = 0;
 650   U32 curMaterial = 0;
 651   bool found = false;
 652
 653   // the following block of code is an optimization...
 654   // it isn't necessary if the longer version of the main loop is used
 655   bool tmpFound;
 656   S32 tmpPlane;
 657   F32 sgn = -1.0f;
 658   F32 * pnum = &startNum;
 659   F32 * pden = &startDen;
 660   S32 * pplane = &curPlane;
 661   bool * pfound = &found;
 662
 663   S32 startPlane = frame * mPlanesPerFrame;
 664   for ( S32  i = startPlane; i < startPlane + mPlanesPerFrame; i++ )
 665   {
 666      // if start & end outside, no collision
 667      // if start & end inside, continue
 668      // if start outside, end inside, or visa versa, find intersection of line with plane
 669      //    then update intersection of line with hull (using startTime and endTime)
 670      F32 dot1 = mDot(mPlaneNormals[i], start ) - mPlaneConstants[i];
 671      F32 dot2 = mDot(mPlaneNormals[i], end) - mPlaneConstants[i];
 672      if ( dot1 * dot2 > 0.0f )
 673      {
 674         // same side of the plane...which side -- dot==0 considered inside
 675         if ( dot1 > 0.0f )
 676            return false; // start and end outside of this plane, no collision
 677         
 678         // start and end inside plane, continue
 679         continue;
 680      }
 681
 682      //AssertFatal( dot1 / ( dot1 - dot2 ) >= 0.0f && dot1 / ( dot1 - dot2 ) <= 1.0f,"TSMesh::castRay (1)" );
 683
 684      // find intersection (time) with this plane...
 685      // F32 time = dot1 / (dot1-dot2);
 686      F32 num = mFabs( dot1 );
 687      F32 den = mFabs( dot1 - dot2 );
 688
 689      // the following block of code is an optimized version...
 690      // this can be commented out and the following block of code used instead
 691      // if debugging a problem in this code, that should probably be done
 692      // if you want to see how this works, look at the following block of code,
 693      // not this one...
 694      // Note that this does not get optimized appropriately...it is included this way
 695      // as an idea for future optimization.
 696      if ( sgn * dot1 >= 0 )
 697      {
 698         sgn *= -1.0f;
 699         pnum = (F32*) ((dsize_t)pnum ^ (dsize_t)&endNum ^ (dsize_t)&startNum);
 700         pden = (F32*) ((dsize_t)pden ^ (dsize_t)&endDen ^ (dsize_t)&startDen);
 701         pplane = (S32*) ((dsize_t)pplane ^ (dsize_t)&tmpPlane ^ (dsize_t)&curPlane);
 702         pfound = (bool*) ((dsize_t)pfound ^ (dsize_t)&tmpFound ^ (dsize_t)&found);
 703      }
 704
 705      bool noCollision = num * endDen * sgn < endNum * den * sgn && num * startDen * sgn < startNum * den * sgn;
 706      if (num * *pden * sgn < *pnum * den * sgn && !noCollision)
 707      {
 708         *pnum = num;
 709         *pden = den;
 710         *pplane = i;
 711         *pfound = true;
 712      }
 713      else if ( noCollision )
 714         return false;
 715
 716//      if (dot1<=0.0f)
 717//      {
 718//         // start is inside plane, end is outside...chop off end
 719//         if (num*endDen<endNum*den) // if (time<endTime)
 720//         {
 721//            if (num*startDen<startNum*den) //if (time<startTime)
 722//               // no intersection of line and hull
 723//               return false;
 724//            // endTime = time;
 725//            endNum = num;
 726//            endDen = den;
 727//         }
 728//         // else, no need to do anything, just continue (we've been more inside than this)
 729//      }
 730//      else // dot2<=0.0f
 731//      {
 732//         // end is inside poly, start is outside...chop off start
 733//         AssertFatal(dot2<=0.0f,"TSMesh::castRay (2)");
 734//         if (num*startDen>startNum*den) // if (time>startTime)
 735//        {
 736//            if (num*endDen>endNum*den) //if (time>endTime)
 737//               // no intersection of line and hull
 738//               return false;
 739//            // startTime   = time;
 740//            startNum = num;
 741//            startDen = den;
 742//            curPlane    = i;
 743//            curMaterial = planeMaterials[i-startPlane];
 744//            found = true;
 745//         }
 746//         // else, no need to do anything, just continue (we've been more inside than this)
 747//      }
 748   }
 749
 750   // setup rayInfo
 751   if ( found && rayInfo )
 752   {
 753      curMaterial       = mPlaneMaterials[ curPlane - startPlane ];
 754
 755      rayInfo->t        = (F32)startNum/(F32)startDen; // finally divide...
 756      rayInfo->normal   = mPlaneNormals[curPlane];
 757
 758      if (materials && materials->size() > 0)
 759         rayInfo->material = materials->getMaterialInst( curMaterial );
 760      else
 761         rayInfo->material = NULL;
 762
 763      rayInfo->setContactPoint( start, end );
 764
 765      return true;
 766   }
 767   else if ( found )
 768      return true;
 769
 770   // only way to get here is if start is inside hull...
 771   // we could return null and just plug in garbage for the material and normal...
 772   return false;
 773}
 774
 775bool TSMesh::castRayRendered( S32 frame, const Point3F & start, const Point3F & end, RayInfo * rayInfo, TSMaterialList* materials )
 776{
 777   if( vertsPerFrame <= 0 ) 
 778      return false;
 779
 780   if( mNumVerts == 0 )
 781      return false;
 782
 783   S32 firstVert  = vertsPerFrame * frame;
 784
 785   bool found = false;
 786   F32 best_t = F32_MAX;
 787   U32 bestIdx0 = 0, bestIdx1 = 0, bestIdx2 = 0;
 788   BaseMatInstance* bestMaterial = NULL;
 789   Point3F dir = end - start;
 790
 791   for ( S32 i = 0; i < mPrimitives.size(); i++ )
 792   {
 793      TSDrawPrimitive & draw = mPrimitives[i];
 794      U32 drawStart = draw.start;
 795
 796      AssertFatal( draw.matIndex & TSDrawPrimitive::Indexed,"TSMesh::castRayRendered (1)" );
 797
 798      U32 matIndex = draw.matIndex & TSDrawPrimitive::MaterialMask;
 799      BaseMatInstance* material = ( materials ? materials->getMaterialInst( matIndex ) : 0 );
 800
 801      U32 idx0, idx1, idx2;
 802
 803      // gonna depend on what kind of primitive it is...
 804      if ( (draw.matIndex & TSDrawPrimitive::TypeMask) == TSDrawPrimitive::Triangles )
 805      {
 806         for ( S32 j = 0; j < draw.numElements-2; j += 3 )
 807         {
 808            idx0 = mIndices[drawStart + j + 0];
 809            idx1 = mIndices[drawStart + j + 1];
 810            idx2 = mIndices[drawStart + j + 2];
 811
 812            F32 cur_t = 0;
 813            Point2F b;
 814
 815            if(castRayTriangle(start, dir, mVertexData.getBase(firstVert + idx0).vert(),
 816               mVertexData.getBase(firstVert + idx1).vert(), mVertexData.getBase(firstVert + idx2).vert(), cur_t, b))
 817            {
 818               if(cur_t < best_t)
 819               {
 820                  best_t = cur_t;
 821                  bestIdx0 = idx0;
 822                  bestIdx1 = idx1;
 823                  bestIdx2 = idx2;
 824                  bestMaterial = material;
 825                  found = true;
 826               }
 827            }
 828         }
 829      }
 830      else
 831      {
 832         AssertFatal( (draw.matIndex & TSDrawPrimitive::TypeMask) == TSDrawPrimitive::Strip,"TSMesh::castRayRendered (2)" );
 833
 834         idx0 = mIndices[drawStart + 0];
 835         idx2 = mIndices[drawStart + 1];
 836         U32 * nextIdx = &idx1;
 837         for ( S32 j = 2; j < draw.numElements; j++ )
 838         {
 839            *nextIdx = idx2;
 840            // nextIdx = (j%2)==0 ? &idx0 : &idx1;
 841            nextIdx = (U32*) ( (dsize_t)nextIdx ^ (dsize_t)&idx0 ^ (dsize_t)&idx1);
 842            idx2 = mIndices[drawStart + j];
 843            if ( idx0 == idx1 || idx0 == idx2 || idx1 == idx2 )
 844               continue;
 845
 846            F32 cur_t = 0;
 847            Point2F b;
 848
 849            if(castRayTriangle(start, dir, mVertexData.getBase(firstVert + idx0).vert(), 
 850               mVertexData.getBase(firstVert + idx1).vert(), mVertexData.getBase(firstVert + idx2).vert(), cur_t, b))
 851            {
 852               if(cur_t < best_t)
 853               {
 854                  best_t = cur_t;
 855                  bestIdx0 = firstVert + idx0;
 856                  bestIdx1 = firstVert + idx1;
 857                  bestIdx2 = firstVert + idx2;
 858                  bestMaterial = material;
 859                  found = true;
 860               }
 861            }
 862         }
 863      }
 864   }
 865
 866   // setup rayInfo
 867   if ( found && rayInfo )
 868   {
 869      rayInfo->t = best_t;
 870
 871      Point3F normal;
 872      mCross(mVertexData.getBase(bestIdx2).vert()-mVertexData.getBase(bestIdx0).vert(),mVertexData.getBase(bestIdx1).vert()-mVertexData.getBase(bestIdx0).vert(),&normal);
 873      if ( mDot( normal, normal ) < 0.001f )
 874      {
 875         mCross( mVertexData.getBase(bestIdx0).vert() - mVertexData.getBase(bestIdx1).vert(), mVertexData.getBase(bestIdx2).vert() - mVertexData.getBase(bestIdx1).vert(), &normal );
 876         if ( mDot( normal, normal ) < 0.001f )
 877         {
 878            mCross( mVertexData.getBase(bestIdx1).vert() - mVertexData.getBase(bestIdx2).vert(), mVertexData.getBase(bestIdx0).vert() - mVertexData.getBase(bestIdx2).vert(), &normal );
 879         }
 880      }
 881      normal.normalize();
 882      rayInfo->normal = normal;
 883
 884      rayInfo->material = bestMaterial;
 885
 886      rayInfo->setContactPoint( start, end );
 887
 888      return true;
 889   }
 890   else if ( found )
 891      return true;
 892
 893   return false;
 894}
 895
 896bool TSMesh::addToHull( U32 idx0, U32 idx1, U32 idx2 )
 897{
 898   // calculate the normal of this triangle... remember, we lose precision
 899   // when we subtract two large numbers that are very close to each other,
 900   // so depending on how we calculate the normal, we could get a 
 901   // different result. so, we will calculate the normal three different
 902   // ways and take the one that gives us the largest vector before we
 903   // normalize.
 904   Point3F normal1, normal2, normal3;
 905
 906   const Point3F& vertex0Data = mVertexData.getBase(idx0).vert();
 907   const Point3F& vertex1Data = mVertexData.getBase(idx1).vert();
 908   const Point3F& vertex2Data = mVertexData.getBase(idx2).vert();
 909
 910   mCross(vertex2Data-vertex0Data,vertex1Data-vertex0Data,&normal1);
 911   mCross(vertex0Data-vertex1Data,vertex2Data-vertex1Data,&normal2);
 912   mCross(vertex1Data-vertex2Data,vertex0Data-vertex2Data,&normal3);
 913
 914   Point3F normal = normal1;
 915   F32 greatestMagSquared = mDot(normal1, normal1);
 916   F32 magSquared = mDot(normal2, normal2);
 917   if (magSquared > greatestMagSquared)
 918   {
 919      normal = normal2;
 920      greatestMagSquared = magSquared;
 921   }
 922   magSquared = mDot(normal3, normal3);
 923   if (magSquared > greatestMagSquared)
 924   {
 925      normal = normal3;
 926      greatestMagSquared = magSquared;
 927   }
 928   if (mDot(normal, normal) < 0.00000001f)
 929       return false;
 930
 931   normal.normalize();
 932   F32 k = mDot( normal, mVertexData.getBase(idx0).vert() );
 933   for ( S32 i = 0; i < mPlaneNormals.size(); i++ )
 934   {
 935      if ( mDot(mPlaneNormals[i], normal ) > 0.99f && mFabs( k- mPlaneConstants[i] ) < 0.01f )
 936         return false;          // this is a repeat...
 937   }
 938   // new plane, add it to the list...
 939   mPlaneNormals.push_back( normal );
 940   mPlaneConstants.push_back( k );
 941   return true;
 942}
 943
 944bool TSMesh::buildConvexHull()
 945{
 946   // already done, return without error
 947   if (mPlaneNormals.size() )
 948      return true;
 949
 950   bool error = false;
 951
 952   // should probably only have 1 frame, but just in case...
 953   mPlanesPerFrame = 0;
 954   S32 frame, i, j;
 955   for ( frame = 0; frame < numFrames; frame++ )
 956   {
 957      S32 firstVert  = vertsPerFrame * frame;
 958      S32 firstPlane = mPlaneNormals.size();
 959      for ( i = 0; i < mPrimitives.size(); i++ )
 960      {
 961         TSDrawPrimitive & draw = mPrimitives[i];
 962         U32 start = draw.start;
 963
 964         AssertFatal( draw.matIndex & TSDrawPrimitive::Indexed,"TSMesh::buildConvexHull (1)" );
 965
 966         // gonna depend on what kind of primitive it is...
 967         if ( (draw.matIndex & TSDrawPrimitive::TypeMask) == TSDrawPrimitive::Triangles )
 968         {
 969            for ( j = 0;  j < draw.numElements; j += 3 )
 970               if ( addToHull(   mIndices[start + j + 0] + firstVert, 
 971                                 mIndices[start + j + 1] + firstVert, 
 972                                 mIndices[start + j + 2] + firstVert ) && frame == 0 )
 973               mPlaneMaterials.push_back( draw.matIndex & TSDrawPrimitive::MaterialMask );
 974         }
 975         else
 976         {
 977            AssertFatal( (draw.matIndex&TSDrawPrimitive::Strip) == TSDrawPrimitive::Strip,"TSMesh::buildConvexHull (2)" );
 978
 979            U32 idx0 = mIndices[start + 0] + firstVert;
 980            U32 idx1;
 981            U32 idx2 = mIndices[start + 1] + firstVert;
 982            U32 * nextIdx = &idx1;
 983            for ( j = 2; j < draw.numElements; j++ )
 984            {
 985               *nextIdx = idx2;
 986//               nextIdx = (j%2)==0 ? &idx0 : &idx1;
 987               nextIdx = (U32*) ( (dsize_t)nextIdx ^ (dsize_t)&idx0 ^ (dsize_t)&idx1 );
 988               idx2 = mIndices[start + j] + firstVert;
 989               if ( addToHull( idx0, idx1, idx2 ) && frame == 0 )
 990               mPlaneMaterials.push_back( draw.matIndex & TSDrawPrimitive::MaterialMask );
 991            }
 992         }
 993      }
 994      // make sure all the verts on this frame are inside all the planes
 995      for ( i = 0; i < vertsPerFrame; i++ )
 996         for ( j = firstPlane; j < mPlaneNormals.size(); j++ )
 997            if ( mDot( mVertexData.getBase(firstVert + i).vert(), mPlaneNormals[j] ) - mPlaneConstants[j] < 0.01 ) // .01 == a little slack
 998               error = true;
 999
1000      if ( frame == 0 )
1001        mPlanesPerFrame = mPlaneNormals.size();
1002
1003      if ( (frame + 1) * mPlanesPerFrame != mPlaneNormals.size() )
1004      {
1005         // eek, not all frames have same number of planes...
1006         while ( (frame + 1) * mPlanesPerFrame > mPlaneNormals.size() )
1007         {
1008            // we're short, duplicate last plane till we match
1009            U32 sz = mPlaneNormals.size();
1010         mPlaneNormals.increment();
1011         mPlaneNormals.last() = mPlaneNormals[sz-1];
1012         mPlaneConstants.increment();
1013         mPlaneConstants.last() = mPlaneConstants[sz-1];
1014         }
1015         while ( (frame + 1) * mPlanesPerFrame < mPlaneNormals.size() )
1016         {
1017            // harsh -- last frame has more than other frames
1018            // duplicate last plane in each frame
1019            for ( S32 k = frame - 1; k >= 0; k-- )
1020            {
1021            mPlaneNormals.insert( k * mPlanesPerFrame + mPlanesPerFrame );
1022            mPlaneNormals[k * mPlanesPerFrame + mPlanesPerFrame] = mPlaneNormals[k * mPlanesPerFrame + mPlanesPerFrame - 1];
1023            mPlaneConstants.insert( k * mPlanesPerFrame + mPlanesPerFrame );
1024            mPlaneConstants[k * mPlanesPerFrame + mPlanesPerFrame] = mPlaneConstants[k * mPlanesPerFrame + mPlanesPerFrame - 1];
1025               if ( k == 0 )
1026               {
1027               mPlaneMaterials.increment();
1028               mPlaneMaterials.last() = mPlaneMaterials[mPlaneMaterials.size() - 2];
1029               }
1030            }
1031         mPlanesPerFrame++;
1032         }
1033      }
1034      AssertFatal( (frame + 1) * mPlanesPerFrame == mPlaneNormals.size(),"TSMesh::buildConvexHull (3)" );
1035   }
1036   return !error;
1037}
1038
1039//-----------------------------------------------------
1040// TSMesh bounds methods
1041//-----------------------------------------------------
1042
1043void TSMesh::computeBounds()
1044{
1045   MatrixF mat(true);
1046   computeBounds( mat, mBounds, -1, &mCenter, &mRadius );
1047}
1048
1049void TSMesh::computeBounds( const MatrixF &transform, Box3F &bounds, S32 frame, Point3F *center, F32 *radius )
1050{
1051   const Point3F *baseVert = NULL;
1052   S32 stride = 0;
1053   S32 numVerts = 0;
1054
1055   AssertFatal(!mVertexData.isReady()  || (mVertexData.isReady() && mNumVerts == mVertexData.size() && mNumVerts == vertsPerFrame), "vertex number mismatch");
1056
1057   if(mVerts.size() == 0 && mVertexData.isReady() && mVertexData.size() > 0)
1058   {
1059      baseVert = &mVertexData.getBase(0).vert();
1060      stride = mVertexData.vertSize();
1061
1062      if ( frame < 0 )
1063         numVerts = mNumVerts;
1064      else
1065      {
1066         baseVert = &mVertexData.getBase(frame * vertsPerFrame).vert();
1067         numVerts = vertsPerFrame;
1068      }
1069   }
1070   else
1071   {
1072      baseVert = mVerts.address();
1073      stride = sizeof(Point3F);
1074
1075      if ( frame < 0 )
1076         numVerts = mVerts.size();
1077      else
1078      {
1079         baseVert += frame * vertsPerFrame;
1080         numVerts = vertsPerFrame;
1081      }
1082   }
1083   computeBounds( baseVert, numVerts, stride, transform, bounds, center, radius );
1084}
1085
1086void TSMesh::computeBounds( const Point3F *v, S32 numVerts, S32 stride, const MatrixF &transform, Box3F &bounds, Point3F *center, F32 *radius )
1087{
1088   const U8 *_vb = reinterpret_cast<const U8 *>(v);
1089
1090   if ( !numVerts )
1091   {
1092      bounds.minExtents = Point3F::Zero;
1093      bounds.maxExtents = Point3F::Zero;
1094      if ( center )
1095         *center = Point3F::Zero;
1096      if ( radius )
1097         *radius = 0;
1098      return;
1099   }
1100
1101   S32 i;
1102   Point3F p;
1103   transform.mulP( *v, &bounds.minExtents );
1104   bounds.maxExtents = bounds.minExtents;
1105   for ( i = 0; i < numVerts; i++ )
1106   {
1107      const Point3F &curVert = *reinterpret_cast<const Point3F *>(_vb + i * stride);
1108      transform.mulP( curVert, &p );
1109      bounds.maxExtents.setMax( p );
1110      bounds.minExtents.setMin( p );
1111   }
1112   Point3F c;
1113   if ( !center )
1114      center = &c;
1115   center->x = 0.5f * (bounds.minExtents.x + bounds.maxExtents.x);
1116   center->y = 0.5f * (bounds.minExtents.y + bounds.maxExtents.y);
1117   center->z = 0.5f * (bounds.minExtents.z + bounds.maxExtents.z);
1118   if ( radius )
1119   {
1120      *radius = 0.0f;
1121      for ( i = 0; i < numVerts; i++ )
1122      {
1123         const Point3F &curVert = *reinterpret_cast<const Point3F *>(_vb + i * stride);
1124         transform.mulP( curVert, &p );
1125         p -= *center;
1126         *radius = getMax( *radius, mDot( p, p ) );
1127      }
1128      *radius = mSqrt( *radius );
1129   }
1130}
1131
1132//-----------------------------------------------------
1133
1134S32 TSMesh::getNumPolys() const
1135{
1136   S32 count = 0;
1137   for ( S32 i = 0; i < mPrimitives.size(); i++ )
1138   {
1139      switch (mPrimitives[i].matIndex & TSDrawPrimitive::TypeMask)
1140      {
1141         case TSDrawPrimitive::Triangles:
1142            count += mPrimitives[i].numElements / 3;
1143            break;
1144
1145         case TSDrawPrimitive::Fan:
1146            count += mPrimitives[i].numElements - 2;
1147            break;
1148
1149         case TSDrawPrimitive::Strip:
1150            // Don't count degenerate triangles
1151            for ( S32 j = mPrimitives[i].start;
1152                  j < mPrimitives[i].start+ mPrimitives[i].numElements-2;
1153                  j++ )
1154            {
1155               if ((mIndices[j] != mIndices[j+1]) &&
1156                   (mIndices[j] != mIndices[j+2]) &&
1157                   (mIndices[j+1] != mIndices[j+2]))
1158                  count++;
1159            }
1160            break;
1161      }
1162   }
1163   return count;
1164}
1165
1166//-----------------------------------------------------
1167
1168TSMesh::TSMesh() : mMeshType( StandardMeshType )
1169{
1170   VECTOR_SET_ASSOCIATION(mPlaneNormals );
1171   VECTOR_SET_ASSOCIATION(mPlaneConstants );
1172   VECTOR_SET_ASSOCIATION(mPlaneMaterials );
1173   mParentMesh = -1;
1174
1175   mOptTree = NULL;
1176   mOpMeshInterface = NULL;
1177   mOpTris = NULL;
1178   mOpPoints = NULL;
1179
1180   mVisibility = 1.0f;
1181
1182
1183   mNumVerts = 0;
1184   mVertSize = 0;
1185   mVertOffset = 0;
1186   mRadius = 0.0f;
1187   mVertexFormat = NULL;
1188   mPrimBufferOffset = 0;
1189   numFrames = 0;
1190   numMatFrames = 0;
1191   vertsPerFrame = 0;
1192   mPlanesPerFrame = 0;
1193   mMergeBufferStart = 0;
1194   mParentMeshObject = NULL;
1195}
1196
1197//-----------------------------------------------------
1198// TSMesh destructor
1199//-----------------------------------------------------
1200
1201TSMesh::~TSMesh()
1202{
1203   SAFE_DELETE( mOptTree );
1204   SAFE_DELETE( mOpMeshInterface );
1205   SAFE_DELETE_ARRAY( mOpTris );
1206   SAFE_DELETE_ARRAY( mOpPoints );
1207
1208   mNumVerts = 0;
1209}
1210
1211//-----------------------------------------------------
1212// TSSkinMesh methods
1213//-----------------------------------------------------
1214
1215void TSSkinMesh::updateSkinBuffer( const Vector<MatrixF> &transforms, U8* buffer )
1216{
1217   PROFILE_SCOPE(TSSkinMesh_UpdateSkinBuffer);
1218
1219   AssertFatal(batchData.initialized, "Batch data not initialized. Call createSkinBatchData() before any skin update is called.");
1220
1221   if (TSShape::smUseHardwareSkinning || mNumVerts == 0)
1222      return;
1223
1224   const MatrixF *matrices = NULL;
1225
1226   static Vector<MatrixF> sBoneTransforms;
1227   sBoneTransforms.setSize(batchData.nodeIndex.size());
1228
1229   // set up bone transforms
1230   PROFILE_START(TSSkinMesh_UpdateTransforms);
1231   for (S32 i = 0; i < batchData.nodeIndex.size(); i++)
1232   {
1233      S32 node = batchData.nodeIndex[i];
1234      sBoneTransforms[i].mul(transforms[node], batchData.initialTransforms[i]);
1235   }
1236
1237   matrices = &sBoneTransforms[0];
1238   PROFILE_END();
1239
1240   const Point3F *inVerts = batchData.initialVerts.address();
1241   const Point3F *inNorms = batchData.initialNorms.address();
1242
1243   AssertFatal(inVerts, "Something went wrong, verts should be valid");
1244
1245   U8 *dest = buffer + mVertOffset;
1246   if (!dest)
1247      return;
1248
1249   Point3F srcVtx, srcNrm;
1250
1251   AssertFatal(batchData.vertexBatchOperations.size() == batchData.initialVerts.size(), "Assumption failed!");
1252
1253   Point3F skinnedVert;
1254   Point3F skinnedNorm;
1255
1256   for (Vector<BatchData::BatchedVertex>::const_iterator itr = batchData.vertexBatchOperations.begin();
1257      itr != batchData.vertexBatchOperations.end(); itr++)
1258   {
1259      const BatchData::BatchedVertex &curVert = *itr;
1260
1261      skinnedVert.zero();
1262      skinnedNorm.zero();
1263
1264      for (S32 tOp = 0; tOp < curVert.transformCount; tOp++)
1265      {
1266         const BatchData::TransformOp &transformOp = curVert.transform[tOp];
1267
1268         const MatrixF& deltaTransform = matrices[transformOp.transformIndex];
1269
1270         deltaTransform.mulP(inVerts[curVert.vertexIndex], &srcVtx);
1271         skinnedVert += (srcVtx * transformOp.weight);
1272
1273         deltaTransform.mulV(inNorms[curVert.vertexIndex], &srcNrm);
1274         skinnedNorm += srcNrm * transformOp.weight;
1275      }
1276
1277      // Assign results 
1278      __TSMeshVertexBase *dvert = (__TSMeshVertexBase*)(dest + (mVertSize * curVert.vertexIndex));
1279      dvert->vert(skinnedVert);
1280      dvert->normal(skinnedNorm);
1281   }
1282}
1283
1284void TSSkinMesh::updateSkinBones( const Vector<MatrixF> &transforms, Vector<MatrixF>& destTransforms )
1285{
1286   // Update transforms for current mesh
1287   destTransforms.setSize(batchData.nodeIndex.size());
1288
1289   for (int i = 0; i<batchData.nodeIndex.size(); i++)
1290   {
1291      S32 node = batchData.nodeIndex[i];
1292
1293      if (node >= transforms.size())
1294         continue; // jamesu - ignore obviously invalid data
1295      destTransforms[i].mul(transforms[node], batchData.initialTransforms[i]);
1296   }
1297}
1298
1299void TSSkinMesh::createSkinBatchData()
1300{
1301   if(batchData.initialized)
1302      return;
1303
1304   batchData.initialized = true;
1305   S32 * curVtx = vertexIndex.begin();
1306   S32 * curBone = boneIndex.begin();
1307   F32 * curWeight = weight.begin();
1308   const S32 * endVtx = vertexIndex.end();
1309
1310   AssertFatal(batchData.nodeIndex.size() <= TSShape::smMaxSkinBones, "Too many bones are here!!!");
1311
1312   // Temp vector to build batch operations
1313   Vector<BatchData::BatchedVertex> batchOperations;
1314
1315   bool issuedWeightWarning = false;
1316
1317   if (mVertexData.isReady())
1318   {
1319      batchData.initialVerts.setSize(mNumVerts);
1320      batchData.initialNorms.setSize(mNumVerts);
1321
1322      // Fill arrays
1323      for (U32 i = 0; i < mNumVerts; i++)
1324      {
1325         const __TSMeshVertexBase &cv = mVertexData.getBase(i);
1326         batchData.initialVerts[i] = cv.vert();
1327         batchData.initialNorms[i] = cv.normal();
1328      }
1329
1330      addWeightsFromVertexBuffer();
1331
1332      curVtx = vertexIndex.begin();
1333      curBone = boneIndex.begin();
1334      curWeight = weight.begin();
1335      endVtx = vertexIndex.end();
1336   }
1337   else
1338   {
1339      batchData.initialNorms = mNorms;
1340      batchData.initialVerts = mVerts;
1341   }
1342
1343   // Build the batch operations
1344   while( curVtx != endVtx )
1345   {
1346      const S32 vidx = *curVtx;
1347      ++curVtx;
1348
1349      const S32 midx = *curBone;
1350      ++curBone;
1351
1352      const F32 w = *curWeight;
1353      ++curWeight;
1354
1355      // Ignore empty weights
1356      if ( vidx < 0 || midx < 0 || w == 0 )
1357         continue;
1358
1359      if( !batchOperations.empty() &&
1360         batchOperations.last().vertexIndex == vidx )
1361      {
1362         AssertFatal( batchOperations.last().transformCount > 0, "Not sure how this happened!" );
1363
1364         S32 opIdx = batchOperations.last().transformCount++;
1365
1366         // Limit the number of weights per bone (keep the N largest influences)
1367         if ( opIdx >= TSSkinMesh::BatchData::maxBonePerVert )
1368         {
1369            if ( !issuedWeightWarning )
1370            {
1371               issuedWeightWarning = true;
1372               Con::warnf( "At least one vertex has too many bone weights - limiting "
1373                  "to the largest %d influences (see maxBonePerVert in tsMesh.h).",
1374                  TSSkinMesh::BatchData::maxBonePerVert );
1375            }
1376
1377            // Too many weights => find and replace the smallest one
1378            S32 minIndex = 0;
1379            F32 minWeight = batchOperations.last().transform[0].weight;
1380            for ( S32 i = 1; i < batchOperations.last().transformCount; i++ )
1381            {
1382               if ( batchOperations.last().transform[i].weight < minWeight )
1383               {
1384                  minWeight = batchOperations.last().transform[i].weight;
1385                  minIndex = i;
1386               }
1387            }
1388
1389            opIdx = minIndex;
1390            batchOperations.last().transformCount = TSSkinMesh::BatchData::maxBonePerVert;
1391         }
1392
1393         batchOperations.last().transform[opIdx].transformIndex = midx;
1394         batchOperations.last().transform[opIdx].weight = w;
1395      }
1396      else
1397      {
1398         batchOperations.increment();
1399         batchOperations.last().vertexIndex = vidx;
1400         batchOperations.last().transformCount = 1;
1401
1402         batchOperations.last().transform[0].transformIndex = midx;
1403         batchOperations.last().transform[0].weight = w;
1404      }
1405      //Con::printf( "[%d] transform idx %d, weight %1.5f", vidx, midx, w );
1406   }
1407   //Con::printf("End skin update");
1408
1409   // Normalize vertex weights (force weights for each vert to sum to 1)
1410   if ( issuedWeightWarning )
1411   {
1412      for ( S32 i = 0; i < batchOperations.size(); i++ )
1413      {
1414         BatchData::BatchedVertex& batchOp = batchOperations[i];
1415
1416         // Sum weights for this vertex
1417         F32 invTotalWeight = 0;
1418         for ( S32 j = 0; j < batchOp.transformCount; j++ )
1419            invTotalWeight += batchOp.transform[j].weight;
1420
1421         // Then normalize the vertex weights
1422         invTotalWeight = 1.0f / invTotalWeight;
1423         for ( S32 j = 0; j < batchOp.transformCount; j++ )
1424            batchOp.transform[j].weight *= invTotalWeight;
1425      }
1426   }
1427
1428   batchData.vertexBatchOperations.set(batchOperations.address(), batchOperations.size());
1429
1430   U32 maxValue = 0;
1431   for (U32 i = 0; i<batchData.vertexBatchOperations.size(); i++)
1432   {
1433      maxValue = batchData.vertexBatchOperations[i].transformCount > maxValue ? batchData.vertexBatchOperations[i].transformCount : maxValue;
1434   }
1435   maxBones = maxValue;
1436}
1437
1438void TSSkinMesh::setupVertexTransforms()
1439{
1440   AssertFatal(mVertexData.vertSize() == mVertSize, "vert size mismatch");
1441
1442   // Generate the bone transforms for the verts
1443   for( Vector<BatchData::BatchedVertex>::const_iterator itr = batchData.vertexBatchOperations.begin();
1444      itr != batchData.vertexBatchOperations.end(); itr++ )
1445   {
1446      const BatchData::BatchedVertex &curTransform = *itr;
1447      S32 i=0;
1448      S32 j=0;
1449      S32 transformsLeft = curTransform.transformCount;
1450
1451      // Set weights and indices in batches of 4
1452      for( i = 0, j = 0; i < curTransform.transformCount; i += 4, j += 1 )
1453      {
1454         __TSMeshVertex_BoneData &v = mVertexData.getBone(curTransform.vertexIndex, j);
1455         S32 vertsSet = transformsLeft > 4 ? 4 : transformsLeft;
1456
1457         __TSMeshIndex_List indices;
1458         Point4F weights;
1459         dMemset(&indices, '\0', sizeof(indices));
1460         dMemset(&weights, '\0', sizeof(weights));
1461
1462         switch (vertsSet)
1463         {
1464         case 1:
1465            indices.x = curTransform.transform[i+0].transformIndex;
1466            weights.x = curTransform.transform[i+0].weight;
1467            break;
1468         case 2:
1469            indices.x = curTransform.transform[i+0].transformIndex;
1470            weights.x = curTransform.transform[i+0].weight;
1471            indices.y = curTransform.transform[i+1].transformIndex;
1472            weights.y = curTransform.transform[i+1].weight;
1473            break;
1474         case 3:
1475            indices.x = curTransform.transform[i+0].transformIndex;
1476            weights.x = curTransform.transform[i+0].weight;
1477            indices.y = curTransform.transform[i+1].transformIndex;
1478            weights.y = curTransform.transform[i+1].weight;
1479            indices.z = curTransform.transform[i+2].transformIndex;
1480            weights.z = curTransform.transform[i+2].weight;
1481            break;
1482         case 4:
1483            indices.x = curTransform.transform[i+0].transformIndex;
1484            weights.x = curTransform.transform[i+0].weight;
1485            indices.y = curTransform.transform[i+1].transformIndex;
1486            weights.y = curTransform.transform[i+1].weight;
1487            indices.z = curTransform.transform[i+2].transformIndex;
1488            weights.z = curTransform.transform[i+2].weight;
1489            indices.w = curTransform.transform[i+3].transformIndex;
1490            weights.w = curTransform.transform[i+3].weight;
1491            break;
1492         case 0:
1493         default:
1494            break;
1495         }
1496
1497         v.index(indices);
1498         v.weight(weights);
1499         transformsLeft -= 4;
1500      }
1501   }
1502}
1503
1504U32 TSSkinMesh::getMaxBonesPerVert()
1505{
1506   return maxBones >= 0 ? maxBones : 0;
1507}
1508
1509void TSSkinMesh::render( TSVertexBufferHandle &instanceVB )
1510{
1511   innerRender(instanceVB, mPB);
1512}
1513
1514void TSSkinMesh::render(   TSMaterialList *materials, 
1515                           const TSRenderState &rdata,
1516                           bool isSkinDirty,
1517                           const Vector<MatrixF> &transforms, 
1518                           TSVertexBufferHandle &vertexBuffer,
1519                           const char *meshName )
1520{
1521   PROFILE_SCOPE(TSSkinMesh_render);
1522
1523   if (mNumVerts == 0)
1524      return;
1525
1526   // verify stuff first
1527   AssertFatal(mVertexData.size() == mNumVerts, "Vert # mismatch");
1528   AssertFatal((TSShape::smUseHardwareSkinning && vertexBuffer == mVB) || (!TSShape::smUseHardwareSkinning), "Vertex buffer mismatch");
1529
1530   // render...
1531   innerRender(materials, rdata, vertexBuffer, mPB, meshName);
1532}
1533
1534bool TSSkinMesh::buildPolyList( S32 frame, AbstractPolyList *polyList, U32 &surfaceKey, TSMaterialList *materials )
1535{
1536   return false;
1537}
1538
1539bool TSSkinMesh::castRay( S32 frame, const Point3F &start, const Point3F &end, RayInfo *rayInfo, TSMaterialList *materials )
1540{
1541   TORQUE_UNUSED(frame);
1542   TORQUE_UNUSED(start);
1543   TORQUE_UNUSED(end);
1544   TORQUE_UNUSED(rayInfo);
1545   TORQUE_UNUSED(materials);
1546
1547   return false;
1548}
1549
1550bool TSSkinMesh::buildConvexHull()
1551{
1552   return false; // no error, but we don't do anything either...
1553}
1554
1555void TSSkinMesh::computeBounds( const MatrixF &transform, Box3F &bounds, S32 frame, Point3F *center, F32 *radius )
1556{
1557   TORQUE_UNUSED(frame);
1558
1559   if (mVerts.size() != 0)
1560   {
1561      // Use unskinned verts
1562      TSMesh::computeBounds(mVerts.address(), mVerts.size(), sizeof(Point3F), transform, bounds, center, radius );
1563   }
1564   else if (frame <= 0 && batchData.initialVerts.size() > 0)
1565   {
1566      // Use unskinned verts
1567      TSMesh::computeBounds(batchData.initialVerts.address(), batchData.initialVerts.size(), sizeof(Point3F), transform, bounds, center, radius);
1568   }
1569   else
1570   {
1571      Point3F *vertStart = reinterpret_cast<Point3F *>(mVertexData.address());
1572      TSMesh::computeBounds( vertStart, mVertexData.size(), mVertexData.vertSize(), transform, bounds, center, radius );
1573   }
1574}
1575
1576//-----------------------------------------------------
1577// encoded normals
1578//-----------------------------------------------------
1579
1580const Point3F TSMesh::smU8ToNormalTable[] =
1581{
1582      Point3F( 0.565061f, -0.270644f, -0.779396f ),
1583      Point3F( -0.309804f, -0.731114f, 0.607860f ),
1584      Point3F( -0.867412f, 0.472957f, 0.154619f ),
1585      Point3F( -0.757488f, 0.498188f, -0.421925f ),
1586      Point3F( 0.306834f, -0.915340f, 0.260778f ),
1587      Point3F( 0.098754f, 0.639153f, -0.762713f ),
1588      Point3F( 0.713706f, -0.558862f, -0.422252f ),
1589      Point3F( -0.890431f, -0.407603f, -0.202466f ),
1590      Point3F( 0.848050f, -0.487612f, -0.207475f ),
1591      Point3F( -0.232226f, 0.776855f, 0.585293f ),
1592      Point3F( -0.940195f, 0.304490f, -0.152706f ),
1593      Point3F( 0.602019f, -0.491878f, -0.628991f ),
1594      Point3F( -0.096835f, -0.494354f, -0.863850f ),
1595      Point3F( 0.026630f, -0.323659f, -0.945799f ),
1596      Point3F( 0.019208f, 0.909386f, 0.415510f ),
1597      Point3F( 0.854440f, 0.491730f, 0.167731f ),
1598      Point3F( -0.418835f, 0.866521f, -0.271512f ),
1599      Point3F( 0.465024f, 0.409667f, 0.784809f ),
1600      Point3F( -0.674391f, -0.691087f, -0.259992f ),
1601      Point3F( 0.303858f, -0.869270f, -0.389922f ),
1602      Point3F( 0.991333f, 0.090061f, -0.095640f ),
1603      Point3F( -0.275924f, -0.369550f, 0.887298f ),
1604      Point3F( 0.426545f, -0.465962f, 0.775202f ),
1605      Point3F( -0.482741f, -0.873278f, -0.065920f ),
1606      Point3F( 0.063616f, 0.932012f, -0.356800f ),
1607      Point3F( 0.624786f, -0.061315f, 0.778385f ),
1608      Point3F( -0.530300f, 0.416850f, 0.738253f ),
1609      Point3F( 0.312144f, -0.757028f, -0.573999f ),
1610      Point3F( 0.399288f, -0.587091f, -0.704197f ),
1611      Point3F( -0.132698f, 0.482877f, 0.865576f ),
1612      Point3F( 0.950966f, 0.306530f, 0.041268f ),
1613      Point3F( -0.015923f, -0.144300f, 0.989406f ),
1614      Point3F( -0.407522f, -0.854193f, 0.322925f ),
1615      Point3F( -0.932398f, 0.220464f, 0.286408f ),
1616      Point3F( 0.477509f, 0.876580f, 0.059936f ),
1617      Point3F( 0.337133f, 0.932606f, -0.128796f ),
1618      Point3F( -0.638117f, 0.199338f, 0.743687f ),
1619      Point3F( -0.677454f, 0.445349f, 0.585423f ),
1620      Point3F( -0.446715f, 0.889059f, -0.100099f ),
1621      Point3F( -0.410024f, 0.909168f, 0.072759f ),
1622      Point3F( 0.708462f, 0.702103f, -0.071641f ),
1623      Point3F( -0.048801f, -0.903683f, -0.425411f ),
1624      Point3F( -0.513681f, -0.646901f, 0.563606f ),
1625      Point3F( -0.080022f, 0.000676f, -0.996793f ),
1626      Point3F( 0.066966f, -0.991150f, -0.114615f ),
1627      Point3F( -0.245220f, 0.639318f, -0.728793f ),
1628      Point3F( 0.250978f, 0.855979f, 0.452006f ),
1629      Point3F( -0.123547f, 0.982443f, -0.139791f ),
1630      Point3F( -0.794825f, 0.030254f, -0.606084f ),
1631      Point3F( -0.772905f, 0.547941f, 0.319967f ),
1632      Point3F( 0.916347f, 0.369614f, -0.153928f ),
1633      Point3F( -0.388203f, 0.105395f, 0.915527f ),
1634      Point3F( -0.700468f, -0.709334f, 0.078677f ),
1635      Point3F( -0.816193f, 0.390455f, 0.425880f ),
1636      Point3F( -0.043007f, 0.769222f, -0.637533f ),
1637      Point3F( 0.911444f, 0.113150f, 0.395560f ),
1638      Point3F( 0.845801f, 0.156091f, -0.510153f ),
1639      Point3F( 0.829801f, -0.029340f, 0.557287f ),
1640      Point3F( 0.259529f, 0.416263f, 0.871418f ),
1641      Point3F( 0.231128f, -0.845982f, 0.480515f ),
1642      Point3F( -0.626203f, -0.646168f, 0.436277f ),
1643      Point3F( -0.197047f, -0.065791f, 0.978184f ),
1644      Point3F( -0.255692f, -0.637488f, -0.726794f ),
1645      Point3F( 0.530662f, -0.844385f, -0.073567f ),
1646      Point3F( -0.779887f, 0.617067f, -0.104899f ),
1647      Point3F( 0.739908f, 0.113984f, 0.662982f ),
1648      Point3F( -0.218801f, 0.930194f, -0.294729f ),
1649      Point3F( -0.374231f, 0.818666f, 0.435589f ),
1650      Point3F( -0.720250f, -0.028285f, 0.693137f ),
1651      Point3F( 0.075389f, 0.415049f, 0.906670f ),
1652      Point3F( -0.539724f, -0.106620f, 0.835063f ),
1653      Point3F( -0.452612f, -0.754669f, -0.474991f ),
1654      Point3F( 0.682822f, 0.581234f, -0.442629f ),
1655      Point3F( 0.002435f, -0.618462f, -0.785811f ),
1656      Point3F( -0.397631f, 0.110766f, -0.910835f ),
1657      Point3F( 0.133935f, -0.985438f, 0.104754f ),
1658      Point3F( 0.759098f, -0.608004f, 0.232595f ),
1659      Point3F( -0.825239f, -0.256087f, 0.503388f ),
1660      Point3F( 0.101693f, -0.565568f, 0.818408f ),
1661      Point3F( 0.386377f, 0.793546f, -0.470104f ),
1662      Point3F( -0.520516f, -0.840690f, 0.149346f ),
1663      Point3F( -0.784549f, -0.479672f, 0.392935f ),
1664      Point3F( -0.325322f, -0.927581f, -0.183735f ),
1665      Point3F( -0.069294f, -0.428541f, 0.900861f ),
1666      Point3F( 0.993354f, -0.115023f, -0.004288f ),
1667      Point3F( -0.123896f, -0.700568f, 0.702747f ),
1668      Point3F( -0.438031f, -0.120880f, -0.890795f ),
1669      Point3F( 0.063314f, 0.813233f, 0.578484f ),
1670      Point3F( 0.322045f, 0.889086f, -0.325289f ),
1671      Point3F( -0.133521f, 0.875063f, -0.465228f ),
1672      Point3F( 0.637155f, 0.564814f, 0.524422f ),
1673      Point3F( 0.260092f, -0.669353f, 0.695930f ),
1674      Point3F( 0.953195f, 0.040485f, -0.299634f ),
1675      Point3F( -0.840665f, -0.076509f, 0.536124f ),
1676      Point3F( -0.971350f, 0.202093f, 0.125047f ),
1677      Point3F( -0.804307f, -0.396312f, -0.442749f ),
1678      Point3F( -0.936746f, 0.069572f, 0.343027f ),
1679      Point3F( 0.426545f, -0.465962f, 0.775202f ),
1680      Point3F( 0.794542f, -0.227450f, 0.563000f ),
1681      Point3F( -0.892172f, 0.091169f, -0.442399f ),
1682      Point3F( -0.312654f, 0.541264f, 0.780564f ),
1683      Point3F( 0.590603f, -0.735618f, -0.331743f ),
1684      Point3F( -0.098040f, -0.986713f, 0.129558f ),
1685      Point3F( 0.569646f, 0.283078f, -0.771603f ),
1686      Point3F( 0.431051f, -0.407385f, -0.805129f ),
1687      Point3F( -0.162087f, -0.938749f, -0.304104f ),
1688      Point3F( 0.241533f, -0.359509f, 0.901341f ),
1689      Point3F( -0.576191f, 0.614939f, 0.538380f ),
1690      Point3F( -0.025110f, 0.085740f, 0.996001f ),
1691      Point3F( -0.352693f, -0.198168f, 0.914515f ),
1692      Point3F( -0.604577f, 0.700711f, 0.378802f ),
1693      Point3F( 0.465024f, 0.409667f, 0.784809f ),
1694      Point3F( -0.254684f, -0.030474f, -0.966544f ),
1695      Point3F( -0.604789f, 0.791809f, 0.085259f ),
1696      Point3F( -0.705147f, -0.399298f, 0.585943f ),
1697      Point3F( 0.185691f, 0.017236f, -0.982457f ),
1698      Point3F( 0.044588f, 0.973094f, 0.226052f ),
1699      Point3F( -0.405463f, 0.642367f, 0.650357f ),
1700      Point3F( -0.563959f, 0.599136f, -0.568319f ),
1701      Point3F( 0.367162f, -0.072253f, -0.927347f ),
1702      Point3F( 0.960429f, -0.213570f, -0.178783f ),
1703      Point3F( -0.192629f, 0.906005f, 0.376893f ),
1704      Point3F( -0.199718f, -0.359865f, -0.911378f ),
1705      Point3F( 0.485072f, 0.121233f, -0.866030f ),
1706      Point3F( 0.467163f, -0.874294f, 0.131792f ),
1707      Point3F( -0.638953f, -0.716603f, 0.279677f ),
1708      Point3F( -0.622710f, 0.047813f, -0.780990f ),
1709      Point3F( 0.828724f, -0.054433f, -0.557004f ),
1710      Point3F( 0.130241f, 0.991080f, 0.028245f ),
1711      Point3F( 0.310995f, -0.950076f, -0.025242f ),
1712      Point3F( 0.818118f, 0.275336f, 0.504850f ),
1713      Point3F( 0.676328f, 0.387023f, 0.626733f ),
1714      Point3F( -0.100433f, 0.495114f, -0.863004f ),
1715      Point3F( -0.949609f, -0.240681f, -0.200786f ),
1716      Point3F( -0.102610f, 0.261831f, -0.959644f ),
1717      Point3F( -0.845732f, -0.493136f, 0.203850f ),
1718      Point3F( 0.672617f, -0.738838f, 0.041290f ),
1719      Point3F( 0.380465f, 0.875938f, 0.296613f ),
1720      Point3F( -0.811223f, 0.262027f, -0.522742f ),
1721      Point3F( -0.074423f, -0.775670f, -0.626736f ),
1722      Point3F( -0.286499f, 0.755850f, -0.588735f ),
1723      Point3F( 0.291182f, -0.276189f, -0.915933f ),
1724      Point3F( -0.638117f, 0.199338f, 0.743687f ),
1725      Point3F( 0.439922f, -0.864433f, -0.243359f ),
1726      Point3F( 0.177649f, 0.206919f, 0.962094f ),
1727      Point3F( 0.277107f, 0.948521f, 0.153361f ),
1728      Point3F( 0.507629f, 0.661918f, -0.551523f ),
1729      Point3F( -0.503110f, -0.579308f, -0.641313f ),
1730      Point3F( 0.600522f, 0.736495f, -0.311364f ),
1731      Point3F( -0.691096f, -0.715301f, -0.103592f ),
1732      Point3F( -0.041083f, -0.858497f, 0.511171f ),
1733      Point3F( 0.207773f, -0.480062f, -0.852274f ),
1734      Point3F( 0.795719f, 0.464614f, 0.388543f ),
1735      Point3F( -0.100433f, 0.495114f, -0.863004f ),
1736      Point3F( 0.703249f, 0.065157f, -0.707951f ),
1737      Point3F( -0.324171f, -0.941112f, 0.096024f ),
1738      Point3F( -0.134933f, -0.940212f, 0.312722f ),
1739      Point3F( -0.438240f, 0.752088f, -0.492249f ),
1740      Point3F( 0.964762f, -0.198855f, 0.172311f ),
1741      Point3F( -0.831799f, 0.196807f, 0.519015f ),
1742      Point3F( -0.508008f, 0.819902f, 0.263986f ),
1743      Point3F( 0.471075f, -0.001146f, 0.882092f ),
1744      Point3F( 0.919512f, 0.246162f, -0.306435f ),
1745      Point3F( -0.960050f, 0.279828f, -0.001187f ),
1746      Point3F( 0.110232f, -0.847535f, -0.519165f ),
1747      Point3F( 0.208229f, 0.697360f, 0.685806f ),
1748      Point3F( -0.199680f, -0.560621f, 0.803637f ),
1749      Point3F( 0.170135f, -0.679985f, -0.713214f ),
1750      Point3F( 0.758371f, -0.494907f, 0.424195f ),
1751      Point3F( 0.077734f, -0.755978f, 0.649965f ),
1752      Point3F( 0.612831f, -0.672475f, 0.414987f ),
1753      Point3F( 0.142776f, 0.836698f, -0.528726f ),
1754      Point3F( -0.765185f, 0.635778f, 0.101382f ),
1755      Point3F( 0.669873f, -0.419737f, 0.612447f ),
1756      Point3F( 0.593549f, 0.194879f, 0.780847f ),
1757      Point3F( 0.646930f, 0.752173f, 0.125368f ),
1758      Point3F( 0.837721f, 0.545266f, -0.030127f ),
1759      Point3F( 0.541505f, 0.768070f, 0.341820f ),
1760      Point3F( 0.760679f, -0.365715f, -0.536301f ),
1761      Point3F( 0.381516f, 0.640377f, 0.666605f ),
1762      Point3F( 0.565794f, -0.072415f, -0.821361f ),
1763      Point3F( -0.466072f, -0.401588f, 0.788356f ),
1764      Point3F( 0.987146f, 0.096290f, 0.127560f ),
1765      Point3F( 0.509709f, -0.688886f, -0.515396f ),
1766      Point3F( -0.135132f, -0.988046f, -0.074192f ),
1767      Point3F( 0.600499f, 0.476471f, -0.642166f ),
1768      Point3F( -0.732326f, -0.275320f, -0.622815f ),
1769      Point3F( -0.881141f, -0.470404f, 0.048078f ),
1770      Point3F( 0.051548f, 0.601042f, 0.797553f ),
1771      Point3F( 0.402027f, -0.763183f, 0.505891f ),
1772      Point3F( 0.404233f, -0.208288f, 0.890624f ),
1773      Point3F( -0.311793f, 0.343843f, 0.885752f ),
1774      Point3F( 0.098132f, -0.937014f, 0.335223f ),
1775      Point3F( 0.537158f, 0.830585f, -0.146936f ),
1776      Point3F( 0.725277f, 0.298172f, -0.620538f ),
1777      Point3F( -0.882025f, 0.342976f, -0.323110f ),
1778      Point3F( -0.668829f, 0.424296f, -0.610443f ),
1779      Point3F( -0.408835f, -0.476442f, -0.778368f ),
1780      Point3F( 0.809472f, 0.397249f, -0.432375f ),
1781      Point3F( -0.909184f, -0.205938f, -0.361903f ),
1782      Point3F( 0.866930f, -0.347934f, -0.356895f ),
1783      Point3F( 0.911660f, -0.141281f, -0.385897f ),
1784      Point3F( -0.431404f, -0.844074f, -0.318480f ),
1785      Point3F( -0.950593f, -0.073496f, 0.301614f ),
1786      Point3F( -0.719716f, 0.626915f, -0.298305f ),
1787      Point3F( -0.779887f, 0.617067f, -0.104899f ),
1788      Point3F( -0.475899f, -0.542630f, 0.692151f ),
1789      Point3F( 0.081952f, -0.157248f, -0.984153f ),
1790      Point3F( 0.923990f, -0.381662f, -0.024025f ),
1791      Point3F( -0.957998f, 0.120979f, -0.260008f ),
1792      Point3F( 0.306601f, 0.227975f, -0.924134f ),
1793      Point3F( -0.141244f, 0.989182f, 0.039601f ),
1794      Point3F( 0.077097f, 0.186288f, -0.979466f ),
1795      Point3F( -0.630407f, -0.259801f, 0.731499f ),
1796      Point3F( 0.718150f, 0.637408f, 0.279233f ),
1797      Point3F( 0.340946f, 0.110494f, 0.933567f ),
1798      Point3F( -0.396671f, 0.503020f, -0.767869f ),
1799      Point3F( 0.636943f, -0.245005f, 0.730942f ),
1800      Point3F( -0.849605f, -0.518660f, -0.095724f ),
1801      Point3F( -0.388203f, 0.105395f, 0.915527f ),
1802      Point3F( -0.280671f, -0.776541f, -0.564099f ),
1803      Point3F( -0.601680f, 0.215451f, -0.769131f ),
1804      Point3F( -0.660112f, -0.632371f, -0.405412f ),
1805      Point3F( 0.921096f, 0.284072f, 0.266242f ),
1806      Point3F( 0.074850f, -0.300846f, 0.950731f ),
1807      Point3F( 0.943952f, -0.067062f, 0.323198f ),
1808      Point3F( -0.917838f, -0.254589f, 0.304561f ),
1809      Point3F( 0.889843f, -0.409008f, 0.202219f ),
1810      Point3F( -0.565849f, 0.753721f, -0.334246f ),
1811      Point3F( 0.791460f, 0.555918f, -0.254060f ),
1812      Point3F( 0.261936f, 0.703590f, -0.660568f ),
1813      Point3F( -0.234406f, 0.952084f, 0.196444f ),
1814      Point3F( 0.111205f, 0.979492f, -0.168014f ),
1815      Point3F( -0.869844f, -0.109095f, -0.481113f ),
1816      Point3F( -0.337728f, -0.269701f, -0.901777f ),
1817      Point3F( 0.366793f, 0.408875f, -0.835634f ),
1818      Point3F( -0.098749f, 0.261316f, 0.960189f ),
1819      Point3F( -0.272379f, -0.847100f, 0.456324f ),
1820      Point3F( -0.319506f, 0.287444f, -0.902935f ),
1821      Point3F( 0.873383f, -0.294109f, 0.388203f ),
1822      Point3F( -0.088950f, 0.710450f, 0.698104f ),
1823      Point3F( 0.551238f, -0.786552f, 0.278340f ),
1824      Point3F( 0.724436f, -0.663575f, -0.186712f ),
1825      Point3F( 0.529741f, -0.606539f, 0.592861f ),
1826      Point3F( -0.949743f, -0.282514f, 0.134809f ),
1827      Point3F( 0.155047f, 0.419442f, -0.894443f ),
1828      Point3F( -0.562653f, -0.329139f, -0.758346f ),
1829      Point3F( 0.816407f, -0.576953f, 0.024576f ),
1830      Point3F( 0.178550f, -0.950242f, -0.255266f ),
1831      Point3F( 0.479571f, 0.706691f, 0.520192f ),
1832      Point3F( 0.391687f, 0.559884f, -0.730145f ),
1833      Point3F( 0.724872f, -0.205570f, -0.657496f ),
1834      Point3F( -0.663196f, -0.517587f, -0.540624f ),
1835      Point3F( -0.660054f, -0.122486f, -0.741165f ),
1836      Point3F( -0.531989f, 0.374711f, -0.759328f ),
1837      Point3F( 0.194979f, -0.059120f, 0.979024f )
1838};
1839
1840U8 TSMesh::encodeNormal( const Point3F &normal )
1841{
1842   U8 bestIndex = 0;
1843   F32 bestDot = -10E30f;
1844   for ( U32 i = 0; i < 256; i++ )
1845   {
1846      F32 dot = mDot( normal, smU8ToNormalTable[i] );
1847      if ( dot > bestDot )
1848      {
1849         bestIndex = i;
1850         bestDot = dot;
1851      }
1852   }
1853   return bestIndex;
1854}
1855
1856//-----------------------------------------------------
1857// TSMesh assemble from/ dissemble to memory buffer
1858//-----------------------------------------------------
1859
1860#define tsalloc TSShape::smTSAlloc
1861
1862TSMesh* TSMesh::assembleMesh( U32 meshType, bool skip )
1863{
1864   static TSMesh tempStandardMesh;
1865   static TSSkinMesh tempSkinMesh;
1866   static TSDecalMesh tempDecalMesh;
1867   static TSSortedMesh tempSortedMesh;
1868
1869   bool justSize = skip || !tsalloc.allocShape32(0); // if this returns NULL, we're just sizing memory block
1870
1871   // a little funny business because we pretend decals are derived from meshes
1872   S32 * ret = NULL;
1873   TSMesh * mesh = NULL;
1874   TSDecalMesh * decal = NULL;
1875
1876   if ( justSize )
1877   {
1878      switch ( meshType )
1879      {
1880         case StandardMeshType :
1881         {
1882            ret = (S32*)&tempStandardMesh;
1883            mesh = &tempStandardMesh;
1884            tsalloc.allocShape32( sizeof(TSMesh) >> 2 );
1885            break;
1886         }
1887         case SkinMeshType     :
1888         {
1889            ret = (S32*)&tempSkinMesh;
1890            mesh = &tempSkinMesh;
1891            tsalloc.allocShape32( sizeof(TSSkinMesh) >> 2 );
1892            break;
1893         }
1894         case DecalMeshType    :
1895         {
1896            ret = (S32*)&tempDecalMesh;
1897            decal = &tempDecalMesh;
1898            tsalloc.allocShape32( sizeof(TSDecalMesh) >> 2 );
1899            break;
1900         }
1901         case SortedMeshType   :
1902         {
1903            ret = (S32*)&tempSortedMesh;
1904            mesh = &tempSortedMesh;
1905            tsalloc.allocShape32( sizeof(TSSortedMesh) >> 2 );
1906            break;
1907         }
1908      }
1909   }
1910   else
1911   {
1912      switch ( meshType )
1913      {
1914         case StandardMeshType :
1915         {
1916            ret = tsalloc.allocShape32( sizeof(TSMesh) >> 2 );
1917            constructInPlace( (TSMesh*)ret );
1918            mesh = (TSMesh*)ret;
1919            break;
1920         }
1921         case SkinMeshType     :
1922         {
1923            ret = tsalloc.allocShape32( sizeof(TSSkinMesh) >> 2 );
1924            constructInPlace( (TSSkinMesh*)ret );
1925            mesh = (TSSkinMesh*)ret;
1926            break;
1927         }
1928         case DecalMeshType    :
1929         {
1930            ret = tsalloc.allocShape32( sizeof(TSDecalMesh) >> 2 );
1931            constructInPlace((TSDecalMesh*)ret);
1932            decal = (TSDecalMesh*)ret;
1933            break;
1934         }
1935         case SortedMeshType   :
1936         {
1937            ret = tsalloc.allocShape32( sizeof(TSSortedMesh) >> 2 );
1938            constructInPlace( (TSSortedMesh*)ret );
1939            mesh = (TSSortedMesh*)ret;
1940            break;
1941         }
1942      }
1943   }
1944
1945   tsalloc.setSkipMode( skip );
1946
1947   if ( mesh )
1948      mesh->assemble( skip );
1949
1950   if ( decal )
1951      decal->assemble( skip );
1952
1953   tsalloc.setSkipMode( false );
1954
1955   return (TSMesh*)ret;
1956}
1957
1958void TSMesh::convertToTris(   const TSDrawPrimitive *primitivesIn,
1959                              const S32 *indicesIn,
1960                              S32 numPrimIn,
1961                              S32 &numPrimOut, 
1962                              S32 &numIndicesOut,
1963                              TSDrawPrimitive *primitivesOut, 
1964                              S32 *indicesOut ) const
1965{
1966   S32 prevMaterial = -99999;
1967   TSDrawPrimitive * newDraw = NULL;
1968   numPrimOut = 0;
1969   numIndicesOut = 0;
1970   for ( S32 i = 0; i < numPrimIn; i++ )
1971   {
1972      S32 newMat = primitivesIn[i].matIndex;
1973      newMat &= ~<a href="/coding/class/structtsdrawprimitive/">TSDrawPrimitive</a>::TypeMask;
1974
1975      U32 start = primitivesIn[i].start;
1976      U32 prevStart = (i > 0) ? primitivesIn[i-1].start : start;
1977      U32 numElements = primitivesIn[i].numElements;
1978
1979      // Add a new primitive if changing materials, or if this primitive
1980      // indexes vertices in a different 16-bit range
1981      if ( ( newMat != prevMaterial ) ||
1982           ((indicesIn[prevStart] ^ indicesIn[start]) & 0xFFFF0000) )
1983      {
1984         if ( primitivesOut )
1985         {
1986            newDraw = &primitivesOut[numPrimOut];
1987            newDraw->start = numIndicesOut;
1988            newDraw->numElements = 0;
1989            newDraw->matIndex = newMat | TSDrawPrimitive::Triangles;
1990         }
1991         numPrimOut++;
1992         prevMaterial = newMat;
1993      }
1994
1995      // gonna depend on what kind of primitive it is...
1996      if ( (primitivesIn[i].matIndex & TSDrawPrimitive::TypeMask) == TSDrawPrimitive::Triangles)
1997      {
1998         for ( S32 j = 0; j < numElements; j += 3 )
1999         {
2000            if ( indicesOut )
2001            {
2002               indicesOut[numIndicesOut + 0] = indicesIn[start + j + 0];
2003               indicesOut[numIndicesOut + 1] = indicesIn[start + j + 1];
2004               indicesOut[numIndicesOut + 2] = indicesIn[start + j + 2];
2005            }
2006            if ( newDraw )
2007               newDraw->numElements += 3;
2008
2009            numIndicesOut += 3;
2010         }
2011      }
2012      else
2013      {
2014         U32 idx0 = indicesIn[start + 0];
2015         U32 idx1;
2016         U32 idx2 = indicesIn[start + 1];
2017         U32 * nextIdx = &idx1;
2018         for ( S32 j = 2; j < numElements; j++ )
2019         {
2020            *nextIdx = idx2;
2021            nextIdx = (U32*) ( (dsize_t)nextIdx ^ (dsize_t)&idx0 ^ (dsize_t)&idx1);
2022            idx2 = indicesIn[start + j];
2023            if ( idx0 == idx1 || idx1 == idx2 || idx2 == idx0 )
2024               continue;
2025
2026            if ( indicesOut )
2027            {
2028               indicesOut[numIndicesOut+0] = idx0;
2029               indicesOut[numIndicesOut+1] = idx1;
2030               indicesOut[numIndicesOut+2] = idx2;
2031            }
2032
2033            if ( newDraw )
2034               newDraw->numElements += 3;
2035            numIndicesOut += 3;
2036         }
2037      }
2038   }
2039}
2040
2041void unwindStrip( const S32 * indices, S32 numElements, Vector<S32> &triIndices )
2042{
2043   U32 idx0 = indices[0];
2044   U32 idx1;
2045   U32 idx2 = indices[1];
2046   U32 * nextIdx = &idx1;
2047   for ( S32 j = 2; j < numElements; j++ )
2048   {
2049      *nextIdx = idx2;
2050      nextIdx = (U32*) ( (dsize_t)nextIdx ^ (dsize_t)&idx0 ^ (dsize_t)&idx1);
2051      idx2 = indices[j];
2052      if ( idx0 == idx1 || idx1 == idx2 || idx2 == idx0 )
2053         continue;
2054
2055      triIndices.push_back( idx0 );
2056      triIndices.push_back( idx1 );
2057      triIndices.push_back( idx2 );
2058   }
2059}
2060
2061void TSMesh::convertToSingleStrip(  const TSDrawPrimitive *primitivesIn, 
2062                                    const S32 *indicesIn,
2063                                    S32 numPrimIn, 
2064                                    S32 &numPrimOut, 
2065                                    S32 &numIndicesOut,
2066                                    TSDrawPrimitive *primitivesOut,
2067                                    S32 *indicesOut ) const
2068{
2069   S32 prevMaterial = -99999;
2070   TSDrawPrimitive * newDraw = NULL;
2071   TSDrawPrimitive * newTris = NULL;
2072   Vector<S32> triIndices;
2073   S32 curDrawOut = 0;
2074   numPrimOut = 0;
2075   numIndicesOut = 0;
2076   for ( S32 i = 0; i < numPrimIn; i++ )
2077   {
2078      S32 newMat = primitivesIn[i].matIndex;
2079
2080      U32 start = primitivesIn[i].start;
2081      U32 prevStart = (i > 0) ? primitivesIn[i-1].start : start;
2082      U32 numElements = primitivesIn[i].numElements;
2083
2084      // Add a new primitive if changing materials, or if this primitive
2085      // indexes vertices in a different 16-bit range
2086      if ( ( newMat != prevMaterial ) ||
2087           ((indicesIn[prevStart] ^ indicesIn[start]) & 0xFFFF0000) )
2088      {
2089         // before adding the new primitive, transfer triangle indices
2090         if ( triIndices.size() )
2091         {
2092            if ( newTris && indicesOut )
2093            {
2094               newTris->start = numIndicesOut;
2095               newTris->numElements = triIndices.size();
2096               dMemcpy(&indicesOut[numIndicesOut],triIndices.address(),triIndices.size()*sizeof(U32));
2097            }
2098            numIndicesOut += triIndices.size();
2099            triIndices.clear();
2100            newTris = NULL;
2101         }
2102
2103         if ( primitivesOut )
2104         {
2105            newDraw = &primitivesOut[numPrimOut];
2106            newDraw->start = numIndicesOut;
2107            newDraw->numElements = 0;
2108            newDraw->matIndex = newMat;
2109         }
2110
2111         numPrimOut++;
2112         curDrawOut = 0;
2113         prevMaterial = newMat;
2114      }
2115
2116      // gonna depend on what kind of primitive it is...
2117      // from above we know it's the same kind as the one we're building...
2118      if ( (primitivesIn[i].matIndex & TSDrawPrimitive::TypeMask) == TSDrawPrimitive::Triangles)
2119      {
2120         // triangles primitive...add to it
2121         for ( S32 j = 0; j < numElements; j += 3 )
2122         {
2123            if ( indicesOut )
2124            {
2125               indicesOut[numIndicesOut + 0] = indicesIn[start + j + 0];
2126               indicesOut[numIndicesOut + 1] = indicesIn[start + j + 1];
2127               indicesOut[numIndicesOut + 2] = indicesIn[start + j + 2];
2128            }
2129
2130            if ( newDraw )
2131               newDraw->numElements += 3;
2132            
2133            numIndicesOut += 3;
2134         }
2135      }
2136      else
2137      {
2138         // strip primitive...
2139         // if numElements less than smSmallestStripSize, add to triangles...
2140         if ( numElements < smMinStripSize + 2 )
2141         {
2142            // put triangle indices aside until material changes...
2143            if ( triIndices.empty() )
2144            {
2145               // set up for new triangle primitive and add it if we are copying data right now
2146               if ( primitivesOut )
2147               {
2148                  newTris = &primitivesOut[numPrimOut];
2149                  newTris->matIndex  = newMat;
2150                  newTris->matIndex &= ~(TSDrawPrimitive::Triangles</a>|<a href="/coding/class/structtsdrawprimitive/#structtsdrawprimitive_1ac9813717d47f33a65a1a751a1713507dab4690780bfa05758bdbe8e9e39bb64bf">TSDrawPrimitive::Strip);
2151                  newTris->matIndex |= TSDrawPrimitive::Triangles;
2152               }
2153
2154               numPrimOut++;
2155            }
2156            unwindStrip( indicesIn + start, numElements, triIndices );
2157         }
2158         else
2159         {
2160            // strip primitive...add to it
2161            if ( indicesOut )
2162            {
2163               if ( curDrawOut & 1 )
2164               {
2165                  indicesOut[numIndicesOut + 0] = indicesOut[numIndicesOut - 1];
2166                  indicesOut[numIndicesOut + 1] = indicesOut[numIndicesOut - 1];
2167                  indicesOut[numIndicesOut + 2] = indicesIn[start];
2168                  dMemcpy(indicesOut+numIndicesOut+3,indicesIn+start,numElements*sizeof(U32));
2169               }
2170               else if ( curDrawOut )
2171               {
2172                  indicesOut[numIndicesOut + 0] = indicesOut[numIndicesOut - 1];
2173                  indicesOut[numIndicesOut + 1] = indicesIn[start];
2174                  dMemcpy(indicesOut+numIndicesOut+2,indicesIn+start,numElements*sizeof(U32));
2175               }
2176               else
2177                  dMemcpy(indicesOut+numIndicesOut,indicesIn+start,numElements*sizeof(U32));
2178            }
2179            S32 added = numElements;
2180            added += curDrawOut ? (curDrawOut&1 ? 3 : 2) : 0;
2181            if ( newDraw )
2182               newDraw->numElements += added;
2183            
2184            numIndicesOut += added;
2185            curDrawOut += added;
2186         }
2187      }
2188   }
2189   // spit out tris before leaving
2190   // before adding the new primitive, transfer triangle indices
2191   if ( triIndices.size() )
2192   {
2193      if ( newTris && indicesOut )
2194      {
2195         newTris->start = numIndicesOut;
2196         newTris->numElements = triIndices.size();
2197         dMemcpy(&indicesOut[numIndicesOut],triIndices.address(),triIndices.size()*sizeof(U32));
2198      }
2199
2200      numIndicesOut += triIndices.size();
2201      triIndices.clear();
2202      newTris = NULL;
2203   }
2204}
2205
2206// this method does none of the converting that the above methods do, except that small strips are converted
2207// to triangle lists...
2208void TSMesh::leaveAsMultipleStrips( const TSDrawPrimitive *primitivesIn, 
2209                                    const S32 *indicesIn,
2210                                    S32 numPrimIn, 
2211                                    S32 &numPrimOut, 
2212                                    S32 &numIndicesOut,
2213                                    TSDrawPrimitive *primitivesOut, 
2214                                    S32 *indicesOut ) const
2215{
2216   S32 prevMaterial = -99999;
2217   TSDrawPrimitive * newDraw = NULL;
2218   Vector<S32> triIndices;
2219   numPrimOut = 0;
2220   numIndicesOut = 0;
2221   for ( S32 i = 0; i < numPrimIn; i++ )
2222   {
2223      S32 newMat = primitivesIn[i].matIndex;
2224
2225      U32 start = primitivesIn[i].start;
2226      U32 prevStart = (i > 0) ? primitivesIn[i-1].start : start;
2227      U32 numElements = primitivesIn[i].numElements;
2228
2229      // Add a new primitive if changing materials, or if this primitive
2230      // indexes vertices in a different 16-bit range
2231      if ( triIndices.size() &&
2232           (( newMat != prevMaterial ) ||
2233           ((indicesIn[prevStart] ^ indicesIn[start]) & 0xFFFF0000) ))
2234      {
2235         // material just changed and we have triangles lying around
2236         // add primitive and indices for triangles and clear triIndices
2237         if ( indicesOut )
2238         {
2239            TSDrawPrimitive * newTris = &primitivesOut[numPrimOut];
2240            newTris->matIndex = prevMaterial;
2241            newTris->matIndex &= ~(TSDrawPrimitive::Triangles</a>|<a href="/coding/class/structtsdrawprimitive/#structtsdrawprimitive_1ac9813717d47f33a65a1a751a1713507dab4690780bfa05758bdbe8e9e39bb64bf">TSDrawPrimitive::Strip);
2242            newTris->matIndex |= TSDrawPrimitive::Triangles;
2243            newTris->start = numIndicesOut;
2244            newTris->numElements = triIndices.size();
2245            dMemcpy(&indicesOut[numIndicesOut],triIndices.address(),triIndices.size()*sizeof(U32));
2246         }
2247         numPrimOut++;
2248         numIndicesOut += triIndices.size();
2249         triIndices.clear();
2250      }
2251
2252      // this is a little convoluted because this code was adapted from convertToSingleStrip
2253      // but we will need a new primitive only if it is a triangle primitive coming in
2254      // or we have more elements than the min strip size...
2255      if ( (primitivesIn[i].matIndex & TSDrawPrimitive::TypeMask) == TSDrawPrimitive::Triangles || numElements>=<a href="/coding/class/classtsmesh/#classtsmesh_1a30775157e7f49fdd31b0458872e3a413">smMinStripSize</a>+2)
2256      {
2257         if ( primitivesOut )
2258         {
2259            newDraw = &primitivesOut[numPrimOut];
2260            newDraw->start = numIndicesOut;
2261            newDraw->numElements = 0;
2262            newDraw->matIndex = newMat;
2263         }
2264         numPrimOut++;
2265      }
2266      prevMaterial = newMat;
2267
2268      // gonna depend on what kind of primitive it is...
2269      // from above we know it's the same kind as the one we're building...
2270      if ( (primitivesIn[i].matIndex & TSDrawPrimitive::TypeMask) == TSDrawPrimitive::Triangles)
2271      {
2272         // triangles primitive...add to it
2273         for ( S32 j = 0; j < numElements; j += 3 )
2274         {
2275            if ( indicesOut )
2276            {
2277               indicesOut[numIndicesOut + 0] = indicesIn[start + j + 0];
2278               indicesOut[numIndicesOut + 1] = indicesIn[start + j + 1];
2279               indicesOut[numIndicesOut + 2] = indicesIn[start + j + 2];
2280            }
2281            if ( newDraw )
2282               newDraw->numElements += 3;
2283            
2284            numIndicesOut += 3;
2285         }
2286      }
2287      else
2288      {
2289         // strip primitive...
2290         // if numElements less than smSmallestStripSize, add to triangles...
2291         if ( numElements < smMinStripSize + 2 )
2292            // put triangle indices aside until material changes...
2293            unwindStrip( indicesIn + start, numElements, triIndices );
2294         else
2295         {
2296            // strip primitive...add to it
2297            if ( indicesOut )
2298               dMemcpy(indicesOut+numIndicesOut,indicesIn+start,numElements*sizeof(U32));
2299            if ( newDraw )
2300               newDraw->numElements = numElements;
2301            
2302            numIndicesOut += numElements;
2303         }
2304      }
2305   }
2306   // spit out tris before leaving
2307   if ( triIndices.size() )
2308   {
2309      // material just changed and we have triangles lying around
2310      // add primitive and indices for triangles and clear triIndices
2311      if ( indicesOut )
2312      {
2313         TSDrawPrimitive *newTris = &primitivesOut[numPrimOut];
2314         newTris->matIndex = prevMaterial;
2315         newTris->matIndex &= ~(TSDrawPrimitive::Triangles</a>|<a href="/coding/class/structtsdrawprimitive/#structtsdrawprimitive_1ac9813717d47f33a65a1a751a1713507dab4690780bfa05758bdbe8e9e39bb64bf">TSDrawPrimitive::Strip);
2316         newTris->matIndex |= TSDrawPrimitive::Triangles;
2317         newTris->start = numIndicesOut;
2318         newTris->numElements = triIndices.size();
2319         dMemcpy(&indicesOut[numIndicesOut],triIndices.address(),triIndices.size()*sizeof(U32));
2320      }
2321      numPrimOut++;
2322      numIndicesOut += triIndices.size();
2323      triIndices.clear();
2324   }
2325}
2326
2327// This method retrieves data that is shared (or possibly shared) between different meshes.
2328// This adds an extra step to the copying of data from the memory buffer to the shape data buffer.
2329// If we have no parentMesh, then we either return a pointer to the data in the memory buffer
2330// (in the case that we skip this mesh) or copy the data into the shape data buffer and return
2331// that pointer (in the case that we don't skip this mesh).
2332// If we do have a parent mesh, then we return a pointer to the data in the shape buffer,
2333// copying the data in there ourselves if our parent didn't already do it (i.e., if it was skipped).
2334S32 * TSMesh::getSharedData32( S32 parentMesh, S32 size, S32 **source, bool skip )
2335{
2336   S32 * ptr;
2337   if( parentMesh < 0 )
2338      ptr = skip ? tsalloc.getPointer32( size ) : tsalloc.copyToShape32( size );
2339   else
2340   {
2341      ptr = source[parentMesh];
2342      // if we skipped the previous mesh (and we're not skipping this one) then
2343      // we still need to copy points into the shape...
2344      if ( !smDataCopied[parentMesh] && !skip )
2345      {
2346         S32 * tmp = ptr;
2347         ptr = tsalloc.allocShape32( size );
2348         if ( ptr && tmp )
2349            dMemcpy(ptr, tmp, size * sizeof(S32) );
2350      } 
2351   }
2352   return ptr;
2353}
2354
2355S8 * TSMesh::getSharedData8( S32 parentMesh, S32 size, S8 **source, bool skip )
2356{
2357   S8 * ptr;
2358   if( parentMesh < 0 )
2359      ptr = skip ? tsalloc.getPointer8( size ) : tsalloc.copyToShape8( size );
2360   else
2361   {
2362      ptr = source[parentMesh];
2363      // if we skipped the previous mesh (and we're not skipping this one) then
2364      // we still need to copy points into the shape...
2365      if ( !smDataCopied[parentMesh] && !skip )
2366      {
2367         S8 * tmp = ptr;
2368         ptr = tsalloc.allocShape8( size );
2369         if ( ptr && tmp )
2370            dMemcpy( ptr, tmp, size * sizeof(S32) );
2371      }
2372   }
2373   return ptr;
2374}
2375
2376void TSMesh::dumpPrimitives(U32 startVertex, U32 startIndex, GFXPrimitive *piArray, U16* ibIndices)
2377{
2378   // go through and create PrimitiveInfo array
2379   GFXPrimitive pInfo;
2380
2381   U32 primitivesSize = mPrimitives.size();
2382   for (U32 i = 0; i < primitivesSize; i++)
2383   {
2384      const TSDrawPrimitive & draw = mPrimitives[i];
2385
2386      GFXPrimitiveType drawType = getDrawType(draw.matIndex >> 30);
2387
2388      switch (drawType)
2389      {
2390      case GFXTriangleList:
2391         pInfo.type = drawType;
2392         pInfo.numPrimitives = draw.numElements / 3;
2393         pInfo.startIndex = startIndex + draw.start;
2394         // Use the first index to determine which 16-bit address space we are operating in
2395         pInfo.startVertex = (mIndices[draw.start] & 0xFFFF0000); // TODO: figure out a good solution for this
2396         pInfo.minIndex = 0; // minIndex are zero based index relative to startVertex. See @GFXDevice
2397         pInfo.numVertices = getMin((U32)0x10000, mNumVerts - pInfo.startVertex);
2398         pInfo.startVertex += startVertex;
2399         break;
2400
2401      case GFXTriangleStrip:
2402         pInfo.type = drawType;
2403         pInfo.numPrimitives = draw.numElements - 2;
2404         pInfo.startIndex = startIndex + draw.start;
2405         // Use the first index to determine which 16-bit address space we are operating in
2406         pInfo.startVertex = (mIndices[draw.start] & 0xFFFF0000); // TODO: figure out a good solution for this
2407         pInfo.minIndex = 0; // minIndex are zero based index relative to startVertex. See @GFXDevice
2408         pInfo.numVertices = getMin((U32)0x10000, mNumVerts - pInfo.startVertex);
2409         pInfo.startVertex += startVertex;
2410         break;
2411
2412      default:
2413         AssertFatal(false, "WTF?!");
2414      }
2415
2416      *piArray++ = pInfo;
2417   }
2418
2419   dCopyArray(ibIndices, mIndices.address(), mIndices.size());
2420}
2421
2422void TSMesh::assemble( bool skip )
2423{
2424   tsalloc.checkGuard();
2425
2426   numFrames = tsalloc.get32();
2427   numMatFrames = tsalloc.get32();
2428   mParentMesh = tsalloc.get32();
2429   tsalloc.get32( (S32*)&mBounds, 6 );
2430   tsalloc.get32( (S32*)&mCenter, 3 );
2431   mRadius = (F32)tsalloc.get32();
2432
2433   if (TSShape::smReadVersion >= 27)
2434   {
2435      // Offsetted
2436      mVertOffset = tsalloc.get32();
2437      mNumVerts = tsalloc.get32();
2438      mVertSize = tsalloc.get32();
2439   }
2440   else
2441   {
2442      mVertOffset = 0;
2443      mNumVerts = 0;
2444      mVertSize = 0;
2445   }
2446
2447   S32 numVerts = tsalloc.get32();
2448   S32 *ptr32 = getSharedData32(mParentMesh, 3 * numVerts, (S32**)smVertsList.address(), skip );
2449   mVerts.set( (Point3F*)ptr32, numVerts );
2450
2451   S32 numTVerts = tsalloc.get32();
2452   ptr32 = getSharedData32(mParentMesh, 2 * numTVerts, (S32**)smTVertsList.address(), skip );
2453   mTverts.set( (Point2F*)ptr32, numTVerts );
2454
2455   if ( TSShape::smReadVersion > 25 )
2456   {
2457      numTVerts = tsalloc.get32();
2458      ptr32 = getSharedData32(mParentMesh, 2 * numTVerts, (S32**)smTVerts2List.address(), skip );
2459      mTverts2.set( (Point2F*)ptr32, numTVerts );
2460
2461      S32 numVColors = tsalloc.get32();
2462      ptr32 = getSharedData32(mParentMesh, numVColors, (S32**)smColorsList.address(), skip );
2463      mColors.set( (ColorI*)ptr32, numVColors );
2464   }
2465
2466   S8 *ptr8;
2467   if ( TSShape::smReadVersion > 21 && TSMesh::smUseEncodedNormals)
2468   {
2469      // we have encoded normals and we want to use them...
2470      if (mParentMesh < 0 )
2471         tsalloc.getPointer32( numVerts * 3 ); // adva  nce past norms, don't use
2472      mNorms.set( NULL, 0 );
2473
2474      ptr8 = getSharedData8(mParentMesh, numVerts, (S8**)smEncodedNormsList.address(), skip );
2475      mEncodedNorms.set( ptr8, numVerts );
2476   }
2477   else if ( TSShape::smReadVersion > 21 )
2478   {
2479      // we have encoded normals but we don't want to use them...
2480      ptr32 = getSharedData32(mParentMesh, 3 * numVerts, (S32**)smNormsList.address(), skip );
2481      mNorms.set( (Point3F*)ptr32, numVerts );
2482
2483      if (mParentMesh < 0 )
2484         tsalloc.getPointer8( numVerts ); // advance past encoded normls, don't use
2485      mEncodedNorms.set( NULL, 0 );
2486   }
2487   else
2488   {
2489      // no encoded normals...
2490      ptr32 = getSharedData32(mParentMesh, 3 * numVerts, (S32**)smNormsList.address(), skip );
2491      mNorms.set( (Point3F*)ptr32, numVerts );
2492      mEncodedNorms.set( NULL, 0 );
2493   }
2494
2495   // copy the primitives and indices...how we do this depends on what
2496   // form we want them in when copied...just get pointers to data for now
2497   S32 szPrimIn, szIndIn;
2498   TSDrawPrimitive *primIn;
2499   S32 *indIn;
2500   bool deleteInputArrays = false;
2501
2502   if (TSShape::smReadVersion > 25)
2503   {
2504      // mesh primitives (start, numElements) and indices are stored as 32 bit values
2505      szPrimIn = tsalloc.get32();
2506      primIn = (TSDrawPrimitive*)tsalloc.getPointer32(szPrimIn*3);
2507      szIndIn = tsalloc.get32();
2508      indIn = tsalloc.getPointer32(szIndIn);
2509   }
2510   else
2511   {
2512      // mesh primitives (start, numElements) indices are stored as 16 bit values
2513      szPrimIn = tsalloc.get32();
2514      S16 *prim16 = tsalloc.getPointer16(szPrimIn*2);   // primitive: start, numElements
2515      S32 *prim32 = tsalloc.getPointer32(szPrimIn);     // primitive: matIndex
2516      szIndIn = tsalloc.get32();
2517
2518      // warn about non-addressable indices
2519      if ( !skip && szIndIn >= 0x10000 )
2520      {
2521         Con::warnf("Mesh contains non-addressable indices, and may not render "
2522            "correctly. Either split this mesh into pieces of no more than 65k "
2523            "unique verts prior to export, or use COLLADA.");
2524      }
2525
2526      S16 *ind16 = tsalloc.getPointer16(szIndIn);
2527
2528      // need to copy to temporary arrays
2529      deleteInputArrays = true;
2530      primIn = new TSDrawPrimitive[szPrimIn];
2531      for (S32 i = 0; i < szPrimIn; i++)
2532      {
2533         primIn[i].start = prim16[i*2];
2534         primIn[i].numElements = prim16[i*2+1];
2535         primIn[i].matIndex = prim32[i];
2536      }
2537
2538      indIn = new S32[szIndIn];
2539      dCopyArray(indIn, ind16, szIndIn);
2540   }
2541
2542   // count the number of output primitives and indices
2543   S32 szPrimOut = szPrimIn, szIndOut = szIndIn;
2544   if (smUseTriangles)
2545      convertToTris(primIn, indIn, szPrimIn, szPrimOut, szIndOut, NULL, NULL);
2546   else if (smUseOneStrip)
2547      convertToSingleStrip(primIn, indIn, szPrimIn, szPrimOut, szIndOut, NULL, NULL);
2548   else
2549      leaveAsMultipleStrips(primIn, indIn, szPrimIn, szPrimOut, szIndOut, NULL, NULL);
2550
2551   // allocate enough space for the new primitives and indices (all 32 bits)
2552   TSDrawPrimitive *primOut = (TSDrawPrimitive*)tsalloc.allocShape32(3*szPrimOut);
2553   S32 *indOut = tsalloc.allocShape32(szIndOut);
2554
2555   // copy output primitives and indices
2556   S32 chkPrim = szPrimOut, chkInd = szIndOut;
2557   if (smUseTriangles)
2558      convertToTris(primIn, indIn, szPrimIn, chkPrim, chkInd, primOut, indOut);
2559   else if (smUseOneStrip)
2560      convertToSingleStrip(primIn, indIn, szPrimIn, chkPrim, chkInd, primOut, indOut);
2561   else
2562      leaveAsMultipleStrips(primIn, indIn, szPrimIn, chkPrim, chkInd, primOut, indOut);
2563   AssertFatal(chkPrim==szPrimOut && chkInd==szIndOut,"TSMesh::primitive conversion");
2564
2565   // store output
2566   mPrimitives.set(primOut, szPrimOut);
2567   mIndices.set(indOut, szIndOut);
2568
2569   // delete temporary arrays if necessary
2570   if (deleteInputArrays)
2571   {
2572      delete [] primIn;
2573      delete [] indIn;
2574   }
2575
2576   S32 sz = tsalloc.get32();
2577   tsalloc.getPointer16( sz ); // skip deprecated merge indices
2578   tsalloc.align32();
2579
2580   vertsPerFrame = tsalloc.get32();
2581   U32 flags = (U32)tsalloc.get32();
2582   if ( mEncodedNorms.size() )
2583      flags |= UseEncodedNormals;
2584   
2585   setFlags( flags );
2586
2587   // Set color & tvert2 flags if we have an old version
2588   if (TSShape::smReadVersion < 27)
2589   {
2590      if (mColors.size() > 0) setFlags(HasColor);
2591      if (mTverts2.size() > 0) setFlags(HasTVert2);
2592      mNumVerts = mVerts.size();
2593   }
2594
2595   tsalloc.checkGuard();
2596
2597   if ( tsalloc.allocShape32( 0 ) && TSShape::smReadVersion < 19 )
2598      computeBounds(); // only do this if we copied the data...
2599
2600   createTangents(mVerts, mNorms);
2601}
2602
2603void TSMesh::disassemble()
2604{
2605   tsalloc.setGuard();
2606
2607   tsalloc.set32( numFrames );
2608   tsalloc.set32( numMatFrames );
2609   tsalloc.set32(mParentMesh);
2610   tsalloc.copyToBuffer32( (S32*)&mBounds, 6 );
2611   tsalloc.copyToBuffer32( (S32*)&mCenter, 3 );
2612   tsalloc.set32( (S32)mRadius );
2613
2614   bool shouldMakeEditable = TSShape::smVersion < 27 || mVertSize == 0;
2615
2616   // Re-create the vectors
2617   if (shouldMakeEditable)
2618   {
2619      makeEditable();
2620
2621      // No Offset
2622      if (TSShape::smVersion >= 27)
2623      {
2624         tsalloc.set32(0);
2625         tsalloc.set32(0);
2626         tsalloc.set32(0);
2627      }
2628   }
2629   else
2630   {
2631      // Offsetted
2632      tsalloc.set32(mVertOffset);
2633      tsalloc.set32(mNumVerts);
2634      tsalloc.set32(mVertSize);
2635      AssertFatal(mNumVerts >= vertsPerFrame, "invalid mNumVerts");
2636   }
2637
2638   if (TSShape::smVersion >= 27 && mVertexData.isReady())
2639   {
2640      // If not editable  all arrays are effectively 0.
2641      tsalloc.set32(0); // verts
2642      tsalloc.set32(0); // tverts
2643      tsalloc.set32(0); // tverts2
2644      tsalloc.set32(0); // colors
2645   }
2646   else
2647   {
2648      // verts...
2649      tsalloc.set32(mVerts.size());
2650      if (mParentMesh < 0)
2651         tsalloc.copyToBuffer32((S32*)mVerts.address(), 3 * mVerts.size()); // if no parent mesh, then save off our verts
2652
2653      // tverts...
2654      tsalloc.set32(mTverts.size());
2655      if (mParentMesh < 0)
2656         tsalloc.copyToBuffer32((S32*)mTverts.address(), 2 * mTverts.size()); // if no parent mesh, then save off our tverts
2657
2658      if (TSShape::smVersion > 25)
2659      {
2660         // tverts2...
2661         tsalloc.set32(mTverts2.size());
2662         if (mParentMesh < 0)
2663            tsalloc.copyToBuffer32((S32*)mTverts2.address(), 2 * mTverts2.size()); // if no parent mesh, then save off our tverts
2664
2665                                                                                 // colors
2666         tsalloc.set32(mColors.size());
2667         if (mParentMesh < 0)
2668            tsalloc.copyToBuffer32((S32*)mColors.address(), mColors.size()); // if no parent mesh, then save off our tverts
2669      }
2670
2671      // norms...
2672      if (mParentMesh < 0) // if no parent mesh, then save off our norms
2673         tsalloc.copyToBuffer32((S32*)mNorms.address(), 3 * mNorms.size()); // norms.size()==verts.size() or error...
2674
2675                                                                          // encoded norms...
2676      if (mParentMesh < 0)
2677      {
2678         // if no parent mesh, compute encoded normals and copy over
2679         for (S32 i = 0; i < mNorms.size(); i++)
2680         {
2681            U8 normIdx = mEncodedNorms.size() ? mEncodedNorms[i] : encodeNormal(mNorms[i]);
2682            tsalloc.copyToBuffer8((S8*)&normIdx, 1);
2683         }
2684      }
2685   }
2686
2687   // optimize triangle draw order during disassemble
2688   {
2689      FrameTemp<TriListOpt::IndexType> tmpIdxs(mIndices.size());
2690      for ( S32 i = 0; i < mPrimitives.size(); i++ )
2691      {
2692         const TSDrawPrimitive& prim = mPrimitives[i];
2693
2694         // only optimize triangle lists (strips and fans are assumed to be already optimized)
2695         if ( (prim.matIndex & TSDrawPrimitive::TypeMask) == TSDrawPrimitive::Triangles )
2696         {
2697            TriListOpt::OptimizeTriangleOrdering(mVerts.size(), prim.numElements,
2698            mIndices.address() + prim.start, tmpIdxs.address());
2699            dCopyArray(mIndices.address() + prim.start, tmpIdxs.address(),
2700               prim.numElements);
2701         }
2702      }
2703   }
2704
2705   if (TSShape::smVersion > 25)
2706   {
2707      // primitives...
2708      tsalloc.set32(mPrimitives.size() );
2709      tsalloc.copyToBuffer32((S32*)mPrimitives.address(),3* mPrimitives.size());
2710
2711      // indices...
2712      tsalloc.set32(mIndices.size());
2713      tsalloc.copyToBuffer32((S32*)mIndices.address(),mIndices.size());
2714   }
2715   else
2716   {
2717      // primitives
2718      tsalloc.set32(mPrimitives.size() );
2719      for (S32 i=0; i<mPrimitives.size(); i++)
2720      {
2721         S16 start = (S16)mPrimitives[i].start;
2722         S16 numElements = (S16)mPrimitives[i].numElements;
2723
2724         tsalloc.copyToBuffer16(&start, 1);
2725         tsalloc.copyToBuffer16(&numElements, 1);
2726         tsalloc.copyToBuffer32(&(mPrimitives[i].matIndex), 1);
2727      }
2728
2729      // indices
2730      tsalloc.set32(mIndices.size());
2731      Vector<S16> s16_indices(mIndices.size());
2732      for (S32 i=0; i<mIndices.size(); i++)
2733         s16_indices.push_back((S16)mIndices[i]);
2734      tsalloc.copyToBuffer16(s16_indices.address(), s16_indices.size());
2735   }
2736
2737   // merge indices...DEPRECATED
2738   tsalloc.set32( 0 );
2739
2740   // small stuff...
2741   tsalloc.set32( vertsPerFrame );
2742   tsalloc.set32( getFlags() );
2743
2744   tsalloc.setGuard();
2745}
2746
2747//-----------------------------------------------------------------------------
2748// TSSkinMesh assemble from/ dissemble to memory buffer
2749//-----------------------------------------------------------------------------
2750void TSSkinMesh::assemble( bool skip )
2751{
2752   // avoid a crash on computeBounds...
2753   batchData.initialVerts.set( NULL, 0 );
2754
2755   TSMesh::assemble( skip );
2756
2757   if (TSShape::smReadVersion >= 27)
2758   {
2759      maxBones = tsalloc.get32();
2760   }
2761   else
2762   {
2763      maxBones = -1;
2764   }
2765
2766   S32 sz;
2767   S32 * ptr32;
2768
2769   if (TSShape::smReadVersion < 27)
2770   {
2771      sz = tsalloc.get32();
2772      S32 numVerts = sz;
2773      ptr32 = getSharedData32(mParentMesh, 3 * numVerts, (S32**)smVertsList.address(), skip);
2774      batchData.initialVerts.set((Point3F*)ptr32, sz);
2775
2776      S8 * ptr8;
2777      if (TSShape::smReadVersion > 21 && TSMesh::smUseEncodedNormals)
2778      {
2779         // we have encoded normals and we want to use them...
2780         if (mParentMesh < 0)
2781            tsalloc.getPointer32(numVerts * 3); // advance past norms, don't use
2782         batchData.initialNorms.set(NULL, 0);
2783
2784         ptr8 = getSharedData8(mParentMesh, numVerts, (S8**)smEncodedNormsList.address(), skip);
2785         mEncodedNorms.set(ptr8, numVerts);
2786         // Note: we don't set the encoded normals flag because we handle them in updateSkin and
2787         //       hide the fact that we are using them from base class (TSMesh)
2788      }
2789      else if (TSShape::smReadVersion > 21)
2790      {
2791         // we have encoded normals but we don't want to use them...
2792         ptr32 = getSharedData32(mParentMesh, 3 * numVerts, (S32**)smNormsList.address(), skip);
2793         batchData.initialNorms.set((Point3F*)ptr32, numVerts);
2794
2795         if (mParentMesh < 0)
2796            tsalloc.getPointer8(numVerts); // advance past encoded normls, don't use
2797
2798       mEncodedNorms.set(NULL, 0);
2799      }
2800      else
2801      {
2802         // no encoded normals...
2803         ptr32 = getSharedData32(mParentMesh, 3 * numVerts, (S32**)smNormsList.address(), skip);
2804         batchData.initialNorms.set((Point3F*)ptr32, numVerts);
2805       mEncodedNorms.set(NULL, 0);
2806      }
2807
2808      // Sometimes we'll have a mesh with 0 verts but initialVerts is set,
2809      // so set these accordingly
2810      if (mVerts.size() == 0)
2811      {
2812        mVerts = batchData.initialVerts;
2813      }
2814
2815      if (mNorms.size() == 0)
2816      {
2817        mNorms = batchData.initialNorms;
2818      }
2819   }
2820   else
2821   {
2822      // Set from the mesh data
2823      batchData.initialVerts = mVerts;
2824      batchData.initialNorms = mNorms;
2825   }
2826
2827   sz = tsalloc.get32();
2828   ptr32 = getSharedData32(mParentMesh, 16 * sz, (S32**)smInitTransformList.address(), skip );
2829   batchData.initialTransforms.set( ptr32, sz );
2830
2831   sz = tsalloc.get32();
2832   ptr32 = getSharedData32(mParentMesh, sz, (S32**)smVertexIndexList.address(), skip );
2833   vertexIndex.set( ptr32, sz );
2834
2835   ptr32 = getSharedData32(mParentMesh, sz, (S32**)smBoneIndexList.address(), skip );
2836   boneIndex.set( ptr32, sz );
2837
2838   ptr32 = getSharedData32(mParentMesh, sz, (S32**)smWeightList.address(), skip );
2839   weight.set( (F32*)ptr32, sz );
2840
2841   sz = tsalloc.get32();
2842   ptr32 = getSharedData32(mParentMesh, sz, (S32**)smNodeIndexList.address(), skip );
2843   batchData.nodeIndex.set( ptr32, sz );
2844
2845   tsalloc.checkGuard();
2846
2847   if (smDebugSkinVerts && ptr32 != NULL)
2848   {
2849      Con::printf("Loaded skin verts...");
2850      for (U32 i = 0; i < vertexIndex.size(); i++)
2851      {
2852         Con::printf("vi[%i] == %i", i, vertexIndex[i]);
2853      }
2854      for (U32 i = 0; i < boneIndex.size(); i++)
2855      {
2856         Con::printf("bi[%i] == %i", i, boneIndex[i]);
2857      }
2858      for (U32 i = 0; i < batchData.nodeIndex.size(); i++)
2859      {
2860         Con::printf("ni[%i] == %i", i, batchData.nodeIndex[i]);
2861      }
2862      for (U32 i = 0; i < boneIndex.size(); i++)
2863      {
2864         Con::printf("we[%i] == %f", i, weight[i]);
2865      }
2866
2867      if (mNumVerts != 0)
2868      {
2869         AssertFatal(batchData.initialVerts.size() == mNumVerts, "err WTF");
2870      }
2871
2872      Con::printf("---");
2873   }
2874
2875   if ( tsalloc.allocShape32( 0 ) && TSShape::smReadVersion < 19 )
2876      TSMesh::computeBounds(); // only do this if we copied the data...c
2877}
2878
2879//-----------------------------------------------------------------------------
2880// disassemble
2881//-----------------------------------------------------------------------------
2882void TSSkinMesh::disassemble()
2883{
2884   TSMesh::disassemble();
2885
2886   if (TSShape::smVersion >= 27)
2887   {
2888      AssertFatal(maxBones != 0, "Skin mesh with no bones? No way!");
2889      tsalloc.set32(maxBones);
2890   }
2891
2892   if (TSShape::smVersion < 27)
2893   {
2894      tsalloc.set32(batchData.initialVerts.size());
2895      // if we have no parent mesh, then save off our verts & norms
2896      if (mParentMesh < 0)
2897      {
2898         tsalloc.copyToBuffer32((S32*)mVerts.address(), 3 * mVerts.size());
2899
2900         // no longer do this here...let tsmesh handle this
2901         tsalloc.copyToBuffer32((S32*)mNorms.address(), 3 * mNorms.size());
2902
2903         // if no parent mesh, compute encoded normals and copy over
2904         for (S32 i = 0; i < mNorms.size(); i++)
2905         {
2906            U8 normIdx = mEncodedNorms.size() ? mEncodedNorms[i] : encodeNormal(mNorms[i]);
2907            tsalloc.copyToBuffer8((S8*)&normIdx, 1);
2908         }
2909      }
2910   }
2911
2912   tsalloc.set32( batchData.initialTransforms.size() );
2913   if (mParentMesh < 0 )
2914      tsalloc.copyToBuffer32( (S32*)batchData.initialTransforms.address(), batchData.initialTransforms.size() * 16 );
2915
2916   if (!mVertexData.isReady())
2917   {
2918      tsalloc.set32(vertexIndex.size());
2919
2920      tsalloc.copyToBuffer32((S32*)vertexIndex.address(), vertexIndex.size());
2921
2922      tsalloc.copyToBuffer32((S32*)boneIndex.address(), boneIndex.size());
2923
2924      tsalloc.copyToBuffer32((S32*)weight.address(), weight.size());
2925   }
2926   else
2927   {
2928      tsalloc.set32(0);
2929   }
2930
2931   if (TSShape::smVersion < 27)
2932   {
2933      if (mParentMesh < 0)
2934      {
2935         tsalloc.copyToBuffer32((S32*)vertexIndex.address(), vertexIndex.size());
2936
2937         tsalloc.copyToBuffer32((S32*)boneIndex.address(), boneIndex.size());
2938
2939         tsalloc.copyToBuffer32((S32*)weight.address(), weight.size());
2940      }
2941   }
2942
2943   tsalloc.set32( batchData.nodeIndex.size() );
2944   if (mParentMesh < 0 )
2945      tsalloc.copyToBuffer32( (S32*)batchData.nodeIndex.address(), batchData.nodeIndex.size() );
2946
2947   tsalloc.setGuard();
2948}
2949
2950TSSkinMesh::TSSkinMesh()
2951{
2952   mMeshType = SkinMeshType;
2953   batchData.initialized = false;
2954   maxBones = -1;
2955}
2956
2957//-----------------------------------------------------------------------------
2958// find tangent vector
2959//-----------------------------------------------------------------------------
2960inline void TSMesh::findTangent( U32 index1, 
2961                                 U32 index2, 
2962                                 U32 index3, 
2963                                 Point3F *tan0, 
2964                                 Point3F *tan1,
2965                                 const Vector<Point3F> &_verts)
2966{
2967   const Point3F &v1 = _verts[index1];
2968   const Point3F &v2 = _verts[index2];
2969   const Point3F &v3 = _verts[index3];
2970
2971   const Point2F &w1 = mTverts[index1];
2972   const Point2F &w2 = mTverts[index2];
2973   const Point2F &w3 = mTverts[index3];
2974
2975   F32 x1 = v2.x - v1.x;
2976   F32 x2 = v3.x - v1.x;
2977   F32 y1 = v2.y - v1.y;
2978   F32 y2 = v3.y - v1.y;
2979   F32 z1 = v2.z - v1.z;
2980   F32 z2 = v3.z - v1.z;
2981
2982   F32 s1 = w2.x - w1.x;
2983   F32 s2 = w3.x - w1.x;
2984   F32 t1 = w2.y - w1.y;
2985   F32 t2 = w3.y - w1.y;
2986
2987   F32 denom = (s1 * t2 - s2 * t1);
2988
2989   if( mFabs( denom ) < 0.0001f )
2990   {
2991      // handle degenerate triangles from strips
2992      if (denom<0) denom = -0.0001f;
2993      else denom = 0.0001f;
2994   }
2995   F32 r = 1.0f / denom;
2996
2997   Point3F sdir(  (t2 * x1 - t1 * x2) * r, 
2998                  (t2 * y1 - t1 * y2) * r, 
2999                  (t2 * z1 - t1 * z2) * r );
3000
3001   Point3F tdir(  (s1 * x2 - s2 * x1) * r, 
3002                  (s1 * y2 - s2 * y1) * r, 
3003                  (s1 * z2 - s2 * z1) * r );
3004
3005
3006   tan0[index1]  += sdir;
3007   tan1[index1]  += tdir;
3008
3009   tan0[index2]  += sdir;
3010   tan1[index2]  += tdir;
3011
3012   tan0[index3]  += sdir;
3013   tan1[index3]  += tdir;
3014}
3015
3016//-----------------------------------------------------------------------------
3017// create array of tangent vectors
3018//-----------------------------------------------------------------------------
3019void TSMesh::createTangents(const Vector<Point3F> &_verts, const Vector<Point3F> &_norms)
3020{
3021   if (_verts.size() == 0) // can only be done in editable mode
3022      return;
3023
3024   U32 numVerts = _verts.size();
3025   U32 numNorms = _norms.size();
3026   if ( numVerts <= 0 || numNorms <= 0 )
3027      return;
3028
3029   if( numVerts != numNorms)
3030      return;
3031
3032   Vector<Point3F> tan0;
3033   tan0.setSize( numVerts * 2 );
3034
3035   Point3F *tan1 = tan0.address() + numVerts;
3036   dMemset( tan0.address(), 0, sizeof(Point3F) * 2 * numVerts );
3037   
3038   U32   numPrimatives = mPrimitives.size();
3039
3040   for (S32 i = 0; i < numPrimatives; i++ )
3041   {
3042      const TSDrawPrimitive & draw = mPrimitives[i];
3043      GFXPrimitiveType drawType = getDrawType( draw.matIndex >> 30 );
3044
3045      U32 p1Index = 0;
3046      U32 p2Index = 0;
3047
3048      U32 *baseIdx = &mIndices[draw.start];
3049
3050      const U32 numElements = (U32)draw.numElements;
3051
3052      switch( drawType )
3053      {
3054      case GFXTriangleList:
3055         {
3056            for( U32 j = 0; j < numElements; j += 3 )
3057               findTangent( baseIdx[j], baseIdx[j + 1], baseIdx[j + 2], tan0.address(), tan1, _verts );
3058            break;
3059         }
3060
3061      case GFXTriangleStrip:
3062         {
3063            p1Index = baseIdx[0];
3064            p2Index = baseIdx[1];
3065            for( U32 j = 2; j < numElements; j++ )
3066            {
3067               findTangent( p1Index, p2Index, baseIdx[j], tan0.address(), tan1, _verts );
3068               p1Index = p2Index;
3069               p2Index = baseIdx[j];
3070            }
3071            break;
3072         }
3073
3074      default:
3075         AssertFatal( false, "TSMesh::createTangents: unknown primitive type!" );
3076      }
3077   }
3078
3079   mTangents.setSize( numVerts );
3080
3081   // fill out final info from accumulated basis data
3082   for( U32 i = 0; i < numVerts; i++ )
3083   {
3084      const Point3F &n = _norms[i];
3085      const Point3F &t = tan0[i];
3086      const Point3F &b = tan1[i];
3087
3088      Point3F tempPt = t - n * mDot( n, t );
3089      tempPt.normalize();
3090     mTangents[i] = tempPt;
3091
3092      Point3F cp;
3093      mCross( n, t, &cp );
3094      
3095     mTangents[i].w = (mDot( cp, b ) < 0.0f) ? -1.0f : 1.0f;
3096   }
3097}
3098
3099void TSMesh::convertToVertexData()
3100{
3101   if (!mVertexData.isReady())
3102   {
3103      _convertToVertexData(mVertexData, mVerts, mNorms);
3104   }
3105}
3106
3107void TSSkinMesh::convertToVertexData()
3108{
3109   if (!mVertexData.isReady())
3110   {
3111      // Batch data required here
3112      createSkinBatchData();
3113
3114      // Dump verts to buffer
3115      _convertToVertexData(mVertexData, batchData.initialVerts, batchData.initialNorms);
3116
3117      // Setup bones too
3118      setupVertexTransforms();
3119   }
3120}
3121
3122void TSMesh::copySourceVertexDataFrom(const TSMesh* srcMesh)
3123{
3124   mVerts = srcMesh->mVerts;
3125   mTverts = srcMesh->mTverts;
3126   mNorms = srcMesh->mNorms;
3127   mColors = srcMesh->mColors;
3128   mTverts2 = srcMesh->mTverts2;
3129
3130   if (mVerts.size() == 0)
3131   {
3132      bool hasTVert2 = srcMesh->getHasTVert2();
3133      bool hasColor = srcMesh->getHasColor();
3134
3135     mVerts.setSize(srcMesh->mNumVerts);
3136      mTverts.setSize(srcMesh->mNumVerts);
3137      mNorms.setSize(srcMesh->mNumVerts);
3138
3139      if (hasColor)
3140         mColors.setSize(mNumVerts);
3141      if (hasTVert2)
3142         mTverts2.setSize(mNumVerts);
3143
3144      // Fill arrays
3145      for (U32 i = 0; i < mNumVerts; i++)
3146      {
3147         const __TSMeshVertexBase &cv = srcMesh->mVertexData.getBase(i);
3148         const __TSMeshVertex_3xUVColor &cvc = srcMesh->mVertexData.getColor(i);
3149         mVerts[i] = cv.vert();
3150         mTverts[i] = cv.tvert();
3151         mNorms[i] = cv.normal();
3152
3153         if (hasColor)
3154            cvc.color().getColor(&mColors[i]);
3155         if (hasTVert2)
3156            mTverts2[i] = cvc.tvert2();
3157      }
3158   }
3159}
3160
3161void TSSkinMesh::copySourceVertexDataFrom(const TSMesh* srcMesh)
3162{
3163   TSMesh::copySourceVertexDataFrom(srcMesh);
3164
3165   if (srcMesh->getMeshType() == TSMesh::SkinMeshType)
3166   {
3167      const TSSkinMesh* srcSkinMesh = static_cast<const TSSkinMesh*>(srcMesh);
3168
3169      weight = srcSkinMesh->weight;
3170      boneIndex = srcSkinMesh->boneIndex;
3171      vertexIndex = srcSkinMesh->vertexIndex;
3172      maxBones = srcSkinMesh->maxBones;
3173
3174      // Extract from vertex data
3175      if (srcSkinMesh->vertexIndex.size() == 0)
3176      {
3177         mVertexData = srcMesh->mVertexData;
3178         addWeightsFromVertexBuffer();
3179         mVertexData.setReady(false);
3180      }
3181   }
3182}
3183
3184U32 TSMesh::getNumVerts()
3185{
3186   return mVertexData.isReady() ? mNumVerts : mVerts.size();
3187}
3188
3189void TSMesh::_convertToVertexData(TSMeshVertexArray &outArray, const Vector<Point3F> &_verts, const Vector<Point3F> &_norms)
3190{
3191   // Update tangents list
3192   createTangents(mVerts, mNorms);
3193
3194   AssertFatal(_verts.size() == mNumVerts, "vert count mismatch");
3195   AssertFatal(!getHasColor() || mColors.size() == _verts.size(), "Vector of color elements should be the same size as other vectors");
3196   AssertFatal(!getHasTVert2() || mTverts2.size() == _verts.size(), "Vector of tvert2 elements should be the same size as other vectors");
3197
3198   AssertFatal(!outArray.isReady(), "Mesh already converted to aligned data! Re-check code!");
3199   AssertFatal(_verts.size() == _norms.size() &&
3200      _verts.size() == mTangents.size(),
3201      "Vectors: verts, norms, tangents must all be the same size");
3202   AssertFatal(mVertSize == outArray.vertSize(), "Size inconsistency");
3203
3204   if (mNumVerts == 0)
3205      return;
3206
3207   bool needWeightSet = outArray.getBoneOffset() != 0;
3208
3209   bool hasColor = getHasColor();
3210   bool hasTVert2 = getHasTVert2();
3211
3212   dMemset(&outArray.getBase(0), '\0', mVertSize * mNumVerts);
3213
3214   for (U32 i = 0; i < mNumVerts; i++)
3215   {
3216      __TSMeshVertexBase &v = outArray.getBase(i);
3217      v.vert(_verts[i]);
3218      v.normal(_norms[i]);
3219      v.tangent(mTangents[i]);
3220
3221      if (i < mTverts.size())
3222         v.tvert(mTverts[i]);
3223
3224      if (hasTVert2 || hasColor)
3225      {
3226         __TSMeshVertex_3xUVColor &vc = outArray.getColor(i);
3227         if (hasTVert2 && i < mTverts2.size())
3228            vc.tvert2(mTverts2[i]);
3229         if (hasColor && i < mColors.size())
3230            vc.color(mColors[i]);
3231      }
3232
3233      // 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
3234      if (needWeightSet)
3235      {
3236         const Point4F wt(1.0f, 0.0f, 0.0f, 0.0f);
3237         outArray.getBone(i, 0).weight(wt);
3238      }
3239   }
3240}
3241
3242void TSMesh::makeEditable()
3243{
3244   bool hasVerts = mVerts.size() != 0;
3245
3246   if(mVertexData.isReady() && !hasVerts)
3247   {
3248      copySourceVertexDataFrom(this);
3249   }
3250
3251   mVertexData.setReady(false);
3252
3253   mVertSize = 0;
3254   mNumVerts = 0;
3255   mVertOffset = 0;
3256
3257   updateMeshFlags();
3258}
3259
3260void TSSkinMesh::addWeightsFromVertexBuffer()
3261{
3262   weight.setSize(0);
3263   boneIndex.setSize(0);
3264   vertexIndex.setSize(0);
3265
3266   U32 numBoneBlocks = maxBones >= 0 ? (maxBones + 3) / 4 : 0;
3267   for (U32 i = 0; i < mNumVerts; i++)
3268   {
3269      for (U32 j = 0; j < numBoneBlocks; j++)
3270      {
3271         const __TSMeshVertex_BoneData &cv = mVertexData.getBone(i, j);
3272
3273         if (cv._weights.x != 0.0f)
3274         {
3275            addWeightForVert(i, cv._indexes.x, cv._weights.x);
3276         }
3277         if (cv._weights.y != 0.0f)
3278         {
3279            addWeightForVert(i, cv._indexes.y, cv._weights.y);
3280         }
3281         if (cv._weights.z != 0.0f)
3282         {
3283            addWeightForVert(i, cv._indexes.z, cv._weights.z);
3284         }
3285         if (cv._weights.w != 0.0f)
3286         {
3287            addWeightForVert(i, cv._indexes.w, cv._weights.w);
3288         }
3289      }
3290   }
3291}
3292
3293void TSSkinMesh::makeEditable()
3294{
3295   bool hasVerts = mVerts.size() != 0;
3296
3297   // Reconstruct bone mapping
3298   if (mVertexData.isReady() && !hasVerts)
3299   {
3300      copySourceVertexDataFrom(this);
3301
3302      weight.setSize(0);
3303      boneIndex.setSize(0);
3304      vertexIndex.setSize(0);
3305
3306      addWeightsFromVertexBuffer();
3307   }
3308
3309   mVertexData.setReady(false);
3310
3311   mVertSize = 0;
3312   mNumVerts = 0;
3313
3314   updateMeshFlags();
3315   batchData.initialized = false;
3316}
3317
3318void TSMesh::clearEditable()
3319{
3320   if (mVerts.size() == 0)
3321      return;
3322
3323   if (mColors.empty())
3324      clearFlags(HasColor);
3325   else
3326      setFlags(HasColor);
3327
3328   if (mTverts2.empty())
3329      clearFlags(HasTVert2);
3330   else
3331      setFlags(HasTVert2);
3332
3333   mVerts.free_memory();
3334   mNorms.free_memory();
3335   mTangents.free_memory();
3336   mTverts.free_memory();
3337   mTverts2.free_memory();
3338   mColors.free_memory();
3339}
3340
3341void TSMesh::updateMeshFlags()
3342{
3343   // Make sure flags are correct
3344   if (mColors.empty())
3345      clearFlags(HasColor);
3346   else
3347      setFlags(HasColor);
3348
3349   if (mTverts2.empty())
3350      clearFlags(HasTVert2);
3351   else
3352      setFlags(HasTVert2);
3353}
3354
3355void TSSkinMesh::clearEditable()
3356{
3357   TSMesh::clearEditable();
3358
3359   weight.free_memory();
3360   boneIndex.free_memory();
3361   vertexIndex.free_memory();
3362}
3363
3364TSBasicVertexFormat::TSBasicVertexFormat() :
3365   texCoordOffset(-1),
3366   boneOffset(-1),
3367   colorOffset(-1),
3368   numBones(0),
3369   vertexSize(-1)
3370{
3371}
3372
3373TSBasicVertexFormat::TSBasicVertexFormat(TSMesh *mesh)
3374{
3375   texCoordOffset = -1;
3376   boneOffset = -1;
3377   colorOffset = -1;
3378   numBones = 0;
3379   vertexSize = -1;
3380
3381   addMeshRequirements(mesh);
3382}
3383
3384void TSBasicVertexFormat::getFormat(GFXVertexFormat &fmt)
3385{
3386   // NOTE: previously the vertex data was padded to allow for verts to be skinned via SSE. 
3387   //       since we now prefer to skin on the GPU and use a basic non-SSE fallback for software 
3388   //       skinning, adding in padding via GFXSemantic::PADDING or dummy fields is no longer required.
3389   fmt.addElement(GFXSemantic::POSITION, GFXDeclType_Float3);
3390   fmt.addElement(GFXSemantic::TANGENTW, GFXDeclType_Float, 3);
3391   fmt.addElement(GFXSemantic::NORMAL, GFXDeclType_Float3);
3392   fmt.addElement(GFXSemantic::TANGENT, GFXDeclType_Float3);
3393
3394   fmt.addElement(GFXSemantic::TEXCOORD, GFXDeclType_Float2, 0);
3395
3396   if (texCoordOffset >= 0 || colorOffset >= 0)
3397   {
3398      fmt.addElement(GFXSemantic::TEXCOORD, GFXDeclType_Float2, 1);
3399      fmt.addElement(GFXSemantic::COLOR, GFXDeclType_Color);
3400   }
3401
3402   for (U32 i=0; i<numBones; i++)
3403   {
3404      fmt.addElement(GFXSemantic::BLENDINDICES, GFXDeclType_UByte4, i);
3405      fmt.addElement(GFXSemantic::BLENDWEIGHT, GFXDeclType_Float4, i);
3406   }
3407}
3408
3409void TSBasicVertexFormat::calculateSize()
3410{
3411   GFXVertexFormat fmt;
3412   vertexSize = 0;
3413   getFormat(fmt);
3414   vertexSize = fmt.getSizeInBytes();
3415}
3416
3417void TSBasicVertexFormat::writeAlloc(TSShapeAlloc* alloc)
3418{
3419   alloc->set16(texCoordOffset);
3420   alloc->set16(boneOffset);
3421   alloc->set16(colorOffset);
3422   alloc->set16(numBones);
3423   alloc->set16(vertexSize);
3424}
3425
3426void TSBasicVertexFormat::readAlloc(TSShapeAlloc* alloc)
3427{
3428   texCoordOffset = alloc->get16();
3429   boneOffset = alloc->get16();
3430   colorOffset = alloc->get16();
3431   numBones = alloc->get16();
3432   vertexSize = alloc->get16();
3433}
3434
3435void TSBasicVertexFormat::addMeshRequirements(TSMesh *mesh)
3436{
3437   bool hasColors = false;
3438   bool hasTexcoord2 = false;
3439   bool hasSkin = false;
3440
3441   hasColors = mesh->getHasColor() || (colorOffset != -1);
3442   hasTexcoord2 = mesh->getHasTVert2() || (texCoordOffset != -1);
3443   hasSkin = (mesh->getMeshType() == TSMesh::SkinMeshType) || (boneOffset != -1);
3444
3445   S32 offset = sizeof(TSMesh::__TSMeshVertexBase);
3446
3447   if ((hasTexcoord2 || hasColors))
3448   {
3449      if (texCoordOffset == -1 || colorOffset == -1)
3450      {
3451         texCoordOffset = offset;
3452         colorOffset = offset + (sizeof(float) * 2);
3453      }
3454      
3455      offset += sizeof(TSMesh::__TSMeshVertex_3xUVColor);
3456   }
3457
3458   if (hasSkin)
3459   {
3460      boneOffset = offset;
3461
3462      U32 numMeshBones = mesh->getMaxBonesPerVert();
3463      U32 boneBlocks = numMeshBones / 4;
3464      U32 extraBlocks = numMeshBones % 4 != 0 ? 1 : 0;
3465      U32 neededBones = boneBlocks + extraBlocks;
3466      numBones = MAX(neededBones, numBones);
3467   }
3468}
3469
3470void TSSkinMesh::printVerts()
3471{
3472   for (U32 i = 0; i < mNumVerts; i++)
3473   {
3474      TSMesh::__TSMeshVertexBase &vb = mVertexData.getBase(i);
3475      TSMesh::__TSMeshVertex_BoneData &bw = mVertexData.getBone(i, 0);
3476
3477      Point3F vert = batchData.initialVerts[i];
3478      Con::printf("v[%i] == %f,%f,%f; iv == %f,%f,%f. bo=%i,%i,%i,%i bw=%f,%f,%f,%f",
3479         i, vb._vert.x, vb._vert.y, vb._vert.z,
3480         vert.x, vert.y, vert.z,
3481         bw._indexes.x, bw._indexes.y, bw._indexes.z, bw._indexes.w,
3482         bw._weights.x, bw._weights.y, bw._weights.z, bw._weights.w);
3483   }
3484}
3485