| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386 | //-----------------------------------------------------------------------------// Copyright (c) 2012 GarageGames, LLC//// Permission is hereby granted, free of charge, to any person obtaining a copy// of this software and associated documentation files (the "Software"), to// deal in the Software without restriction, including without limitation the// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or// sell copies of the Software, and to permit persons to whom the Software is// furnished to do so, subject to the following conditions://// The above copyright notice and this permission notice shall be included in// all copies or substantial portions of the Software.//// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS// IN THE SOFTWARE.//-----------------------------------------------------------------------------#include "ts/tsPartInstance.h"#include "math/mMath.h"//-------------------------------------------------------------------------------------// Constructors//-------------------------------------------------------------------------------------MRandomR250 TSPartInstance::smRandom;TSPartInstance::TSPartInstance(TSShapeInstance * sourceShape){   VECTOR_SET_ASSOCIATION(mMeshObjects);   init(sourceShape);}TSPartInstance::TSPartInstance(TSShapeInstance * sourceShape, S32 objectIndex){   init(sourceShape);   addObject(objectIndex);}void TSPartInstance::init(TSShapeInstance * sourceShape){   mSourceShape = sourceShape;   mSizeCutoffs = NULL;   mPolyCount = NULL;   mNumDetails = 0;   mCurrentObjectDetail = 0;   mCurrentIntraDL = 1.0f;   mData = 0;   mRadius = 0.125;}TSPartInstance::~TSPartInstance(){   delete [] mPolyCount;}//-------------------------------------------------------------------------------------// Methods for updating PartInstances//-------------------------------------------------------------------------------------void TSPartInstance::addObject(S32 objectIndex){   if (mSourceShape->mMeshObjects[objectIndex].forceHidden ||      mSourceShape->mMeshObjects[objectIndex].visible < 0.01f)      // not visible, don't bother      return;   mMeshObjects.push_back(&mSourceShape->mMeshObjects[objectIndex]);}void TSPartInstance::updateBounds(){   // run through meshes and brute force it?   Box3F bounds;   mBounds.minExtents.set( 10E30f, 10E30f, 10E30f);   mBounds.maxExtents.set(-10E30f,-10E30f,-10E30f);   for (S32 i=0; i<mMeshObjects.size(); i++)   {      if (mMeshObjects[i]->getMesh(0))         mMeshObjects[i]->getMesh(0)->computeBounds(mMeshObjects[i]->getTransform(),bounds,mMeshObjects[i]->frame);      mBounds.minExtents.setMin(bounds.minExtents);      mBounds.maxExtents.setMax(bounds.maxExtents);   }   mCenter = mBounds.minExtents + mBounds.maxExtents;   mCenter *= 0.5f;   Point3F r = mBounds.maxExtents-mCenter;   mRadius = mSqrt(mDot(r,r));}//-------------------------------------------------------------------------------------// Methods for breaking shapes into pieces//-------------------------------------------------------------------------------------void TSPartInstance::breakShape(TSShapeInstance * shape, S32 subShape, Vector<TSPartInstance*> & partList, F32 * probShatter, F32 * probBreak, S32 probDepth){   AssertFatal(subShape>=0 && subShape<shape->mShape->subShapeFirstNode.size(),"TSPartInstance::breakShape: subShape out of range.");   S32 start = shape->mShape->subShapeFirstNode[subShape];   TSPartInstance::breakShape(shape, NULL, start, partList, probShatter, probBreak, probDepth);   // update bounds (and get rid of empty parts)   for (S32 i=0; i<partList.size(); i++)   {      if (partList[i]->mMeshObjects.size())      {         partList[i]->updateBounds();         // Remove any parts parts with invalid box         Box3F box = partList[i]->getBounds();         if (!box.isValidBox())         {            Con::warnf("TSPartInstance::breakShape - part created with invalid object box. Removing from list.");            partList.erase(i);            i--;         }     }     else     {        partList.erase(i);        i--;     }   }}void TSPartInstance::breakShape(TSShapeInstance * shape, TSPartInstance * currentPart, S32 currentNode, Vector<TSPartInstance*> & partList, F32 * probShatter, F32 * probBreak, S32 probDepth){   AssertFatal( !probDepth || (probShatter && probBreak),"TSPartInstance::breakShape: probabilities improperly specified.");   const TSShape::Node * node = &shape->mShape->nodes[currentNode];   S32 object = node->firstObject;   S32 child  = node->firstChild;   // copy off probabilities and update probability lists for next level   F32 ps = probShatter ? *probShatter : 1.0f;   F32 pb = probBreak   ? *probBreak   : 1.0f;   if (probDepth>1 && probShatter && probBreak)   {      probShatter++;      probBreak++;      probDepth--;   }   // what to do...depending on how the die roll, we can:   // a) shatter the shape at this level -- meaning we make a part out of each object on this node and   //    we make parts out of all the children (perhaps breaking them up further still)   // b) break the shape off at this level -- meaning we make a part out of the intact piece from here   //    on down (again, we might break the result further as we iterate through the nodes...what breaking   //    the shape really does is separate this piece from the parent piece).   // c) add this piece to the parent -- meaning all objects on this node are added to the parent, and children   //    are also added (but children will be recursively sent through this routine, so if a parent gets option   //    (c) and the child option (a) or (b), then the child will be ripped from the parents grasp.  Cruel   //    people us coders are.   // Note: (a) is the only way that two objects on the same node can be separated...that is why both   //       option a and option b are needed.   if (!probShatter || smRandom.randF() < ps)   {      // option a -- shatter the shape at this level      // iterate through the objects, make part out of each one      while (object>=0)      {         partList.increment();         partList.last() = new TSPartInstance(shape,object);         object = shape->mShape->objects[object].nextSibling;      }      // iterate through the child nodes, call ourselves on each one with currentPart = NULL      while (child>=0)      {         TSPartInstance::breakShape(shape,NULL,child,partList,probShatter,probBreak,probDepth);         child = shape->mShape->nodes[child].nextSibling;      }      return;   }   if (!probBreak || smRandom.randF() < pb)      // option b -- break the shape off at this level      currentPart = NULL; // fall through to option C   // option c -- add this piece to the parent   if (!currentPart)   {      currentPart = new TSPartInstance(shape);      partList.push_back(currentPart);   }   // iterate through objects, add to currentPart   while (object>=0)   {      currentPart->addObject(object);      object = shape->mShape->objects[object].nextSibling;   }   // iterate through child nodes, call ourselves on each one with currentPart as is   while (child>=0)   {      TSPartInstance::breakShape(shape,currentPart,child,partList,probShatter,probBreak,probDepth);      child = shape->mShape->nodes[child].nextSibling;   }}//-------------------------------------------------------------------------------------// render methods -- we use TSShapeInstance code as much as possible// issues: setupTexturing expects a detail level, we give it an object detail level//-------------------------------------------------------------------------------------void TSPartInstance::render(S32 od, const TSRenderState &rdata){   S32 i;   // render mesh objects   for (i=0; i<mMeshObjects.size(); i++)   {      TSRenderState objState = rdata;      const char *meshName = mSourceShape->mShape->names[mMeshObjects[i]->object->nameIndex];      mMeshObjects[i]->render(od,mSourceShape->mShape->mShapeVertexBuffer,mSourceShape->getMaterialList(),objState,1.0, meshName);   }}//-------------------------------------------------------------------------------------// Detail selection// 2 methods:// method 1:  use source shapes detail levels...// method 2:  pass in our own table...// In either case, you can compute the pixel size on your own or let open gl do it.// If you want to use method 2, you have to call setDetailData sometime before selecting detail//-------------------------------------------------------------------------------------void TSPartInstance::setDetailData(F32 * sizeCutoffs, S32 numDetails){   if (mSizeCutoffs == sizeCutoffs && mNumDetails==numDetails)      return;   mSizeCutoffs = sizeCutoffs;   mNumDetails = numDetails;   delete [] mPolyCount;   mPolyCount = NULL;}/*void TSPartInstance::selectCurrentDetail(bool ignoreScale){   if (mSizeCutoffs)   {      selectCurrentDetail(mSizeCutoffs,mNumDetails,ignoreScale);      return;   }   mSourceShape->selectCurrentDetail(ignoreScale);   mCurrentObjectDetail = mSourceShape->getCurrentDetail();   mCurrentIntraDL = mSourceShape->getCurrentIntraDetail();}void TSPartInstance::selectCurrentDetail(F32 pixelSize){   if (mSizeCutoffs)   {      selectCurrentDetail(pixelSize,mSizeCutoffs,mNumDetails);      return;   }   mSourceShape->selectCurrentDetail(pixelSize);   mCurrentObjectDetail = mSourceShape->getCurrentDetail();   mCurrentIntraDL = mSourceShape->getCurrentIntraDetail();}void TSPartInstance::selectCurrentDetail(F32 dist, F32 invScale){   if (mSizeCutoffs)   {      const RectI &viewport = GFX->getViewport();      F32 pixelScale = viewport.extent.x * 1.6f / 640.0f;      F32 pixelSize = GFX->projectRadius(dist*invScale,mSourceShape->getShape()->radius) * pixelScale * TSShapeInstance::smDetailAdjust;      selectCurrentDetail(pixelSize,mSizeCutoffs,mNumDetails);      return;   }   mSourceShape->selectCurrentDetail(dist, invScale);   mCurrentObjectDetail = mSourceShape->getCurrentDetail();   mCurrentIntraDL = mSourceShape->getCurrentIntraDetail();}void TSPartInstance::selectCurrentDetail(F32 * sizeCutoffs, S32 numDetails, bool ignoreScale){   // compute pixel size   Point3F p;   MatrixF toCam = GFX->getWorldMatrix();   toCam.mulP(mCenter,&p);   F32 dist = mDot(p,p);   F32 scale = 1.0f;   if (!ignoreScale)   {      // any scale?      Point3F x,y,z;      toCam.getRow(0,&x);      toCam.getRow(1,&y);      toCam.getRow(2,&z);      F32 scalex = mDot(x,x);      F32 scaley = mDot(y,y);      F32 scalez = mDot(z,z);      scale = scalex;      if (scaley > scale)         scale = scaley;      if (scalez > scale)         scale = scalez;   }   dist /= scale;   dist = mSqrt(dist);   const RectI &viewport = GFX->getViewport();   // JMQMERGE: is this using a hardcoded res/aspect ?  (and the code above)   F32 pixelScale = viewport.extent.x * 1.6f / 640.0f;   F32 pixelRadius = GFX->projectRadius(dist,mRadius) * pixelScale * TSShapeInstance::smDetailAdjust;   selectCurrentDetail(pixelRadius,sizeCutoffs,numDetails);}void TSPartInstance::selectCurrentDetail(F32 pixelSize, F32 * sizeCutoffs, S32 numDetails){   mCurrentObjectDetail = 0;   while (numDetails)   {      if (pixelSize > *sizeCutoffs)         return;      mCurrentObjectDetail++;      numDetails--;      sizeCutoffs++;   }   mCurrentObjectDetail = -1;}*///-------------------------------------------------------------------------------------// Detail query methods...complicated because there are two ways that detail information// can be determined...1) using source shape, or 2) using mSizeCutoffs//-------------------------------------------------------------------------------------F32 TSPartInstance::getDetailSize(S32 dl) const{   if (dl<0)      return 0;   else if (mSizeCutoffs && dl<mNumDetails)      return mSizeCutoffs[dl];   else if (!mSizeCutoffs && dl<=mSourceShape->getShape()->mSmallestVisibleDL)      return mSourceShape->getShape()->details[dl].size;   else return 0;}S32 TSPartInstance::getPolyCount(S32 dl){   if (!mPolyCount)      computePolyCount();   if (dl<0 || dl>=mNumDetails)      return 0;   else      return mPolyCount[dl];}void TSPartInstance::computePolyCount(){   if (!mSizeCutoffs)      mNumDetails = mSourceShape->getShape()->mSmallestVisibleDL+1;   delete [] mPolyCount;   mPolyCount = new S32[mNumDetails];   for (S32 i=0; i<mNumDetails; i++)   {      mPolyCount[i] = 0;      for (S32 j=0; j<mMeshObjects.size(); j++)      {         if (mMeshObjects[j]->getMesh(i))            mPolyCount[i] += mMeshObjects[j]->getMesh(i)->getNumPolys();      }   }}
 |