tsPartInstance.cpp
Engine/source/ts/tsPartInstance.cpp
Detailed Description
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 "ts/tsPartInstance.h" 25#include "math/mMath.h" 26 27//------------------------------------------------------------------------------------- 28// Constructors 29//------------------------------------------------------------------------------------- 30 31MRandomR250 TSPartInstance::smRandom; 32 33TSPartInstance::TSPartInstance(TSShapeInstance * sourceShape) 34{ 35 VECTOR_SET_ASSOCIATION(mMeshObjects); 36 37 init(sourceShape); 38} 39 40TSPartInstance::TSPartInstance(TSShapeInstance * sourceShape, S32 objectIndex) 41{ 42 init(sourceShape); 43 addObject(objectIndex); 44} 45 46void TSPartInstance::init(TSShapeInstance * sourceShape) 47{ 48 mSourceShape = sourceShape; 49 mSizeCutoffs = NULL; 50 mPolyCount = NULL; 51 mNumDetails = 0; 52 mCurrentObjectDetail = 0; 53 mCurrentIntraDL = 1.0f; 54 mData = 0; 55 mRadius = 0.125; 56} 57 58TSPartInstance::~TSPartInstance() 59{ 60 delete [] mPolyCount; 61} 62 63//------------------------------------------------------------------------------------- 64// Methods for updating PartInstances 65//------------------------------------------------------------------------------------- 66 67void TSPartInstance::addObject(S32 objectIndex) 68{ 69 if (mSourceShape->mMeshObjects[objectIndex].forceHidden || 70 mSourceShape->mMeshObjects[objectIndex].visible < 0.01f) 71 // not visible, don't bother 72 return; 73 74 mMeshObjects.push_back(&mSourceShape->mMeshObjects[objectIndex]); 75} 76 77void TSPartInstance::updateBounds() 78{ 79 // run through meshes and brute force it? 80 Box3F bounds; 81 mBounds.minExtents.set( 10E30f, 10E30f, 10E30f); 82 mBounds.maxExtents.set(-10E30f,-10E30f,-10E30f); 83 for (S32 i=0; i<mMeshObjects.size(); i++) 84 { 85 if (mMeshObjects[i]->getMesh(0)) 86 mMeshObjects[i]->getMesh(0)->computeBounds(mMeshObjects[i]->getTransform(),bounds,mMeshObjects[i]->frame); 87 mBounds.minExtents.setMin(bounds.minExtents); 88 mBounds.maxExtents.setMax(bounds.maxExtents); 89 } 90 mCenter = mBounds.minExtents + mBounds.maxExtents; 91 mCenter *= 0.5f; 92 Point3F r = mBounds.maxExtents-mCenter; 93 mRadius = mSqrt(mDot(r,r)); 94} 95 96//------------------------------------------------------------------------------------- 97// Methods for breaking shapes into pieces 98//------------------------------------------------------------------------------------- 99 100void TSPartInstance::breakShape(TSShapeInstance * shape, S32 subShape, Vector<TSPartInstance*> & partList, F32 * probShatter, F32 * probBreak, S32 probDepth) 101{ 102 AssertFatal(subShape>=0 && subShape<shape->mShape->subShapeFirstNode.size(),"TSPartInstance::breakShape: subShape out of range."); 103 104 S32 start = shape->mShape->subShapeFirstNode[subShape]; 105 106 TSPartInstance::breakShape(shape, NULL, start, partList, probShatter, probBreak, probDepth); 107 108 // update bounds (and get rid of empty parts) 109 for (S32 i=0; i<partList.size(); i++) 110 { 111 if (partList[i]->mMeshObjects.size()) 112 { 113 partList[i]->updateBounds(); 114 // Remove any parts parts with invalid box 115 Box3F box = partList[i]->getBounds(); 116 if (!box.isValidBox()) 117 { 118 Con::warnf("TSPartInstance::breakShape - part created with invalid object box. Removing from list."); 119 partList.erase(i); 120 i--; 121 } 122 } 123 else 124 { 125 partList.erase(i); 126 i--; 127 } 128 } 129} 130 131void TSPartInstance::breakShape(TSShapeInstance * shape, TSPartInstance * currentPart, S32 currentNode, Vector<TSPartInstance*> & partList, F32 * probShatter, F32 * probBreak, S32 probDepth) 132{ 133 AssertFatal( !probDepth || (probShatter && probBreak),"TSPartInstance::breakShape: probabilities improperly specified."); 134 135 const TSShape::Node * node = &shape->mShape->nodes[currentNode]; 136 S32 object = node->firstObject; 137 S32 child = node->firstChild; 138 139 // copy off probabilities and update probability lists for next level 140 F32 ps = probShatter ? *probShatter : 1.0f; 141 F32 pb = probBreak ? *probBreak : 1.0f; 142 if (probDepth>1 && probShatter && probBreak) 143 { 144 probShatter++; 145 probBreak++; 146 probDepth--; 147 } 148 149 // what to do...depending on how the die roll, we can: 150 // a) shatter the shape at this level -- meaning we make a part out of each object on this node and 151 // we make parts out of all the children (perhaps breaking them up further still) 152 // b) break the shape off at this level -- meaning we make a part out of the intact piece from here 153 // on down (again, we might break the result further as we iterate through the nodes...what breaking 154 // the shape really does is separate this piece from the parent piece). 155 // c) add this piece to the parent -- meaning all objects on this node are added to the parent, and children 156 // are also added (but children will be recursively sent through this routine, so if a parent gets option 157 // (c) and the child option (a) or (b), then the child will be ripped from the parents grasp. Cruel 158 // people us coders are. 159 // Note: (a) is the only way that two objects on the same node can be separated...that is why both 160 // option a and option b are needed. 161 if (!probShatter || smRandom.randF() < ps) 162 { 163 // option a -- shatter the shape at this level 164 165 // iterate through the objects, make part out of each one 166 while (object>=0) 167 { 168 partList.increment(); 169 partList.last() = new TSPartInstance(shape,object); 170 object = shape->mShape->objects[object].nextSibling; 171 } 172 173 // iterate through the child nodes, call ourselves on each one with currentPart = NULL 174 while (child>=0) 175 { 176 TSPartInstance::breakShape(shape,NULL,child,partList,probShatter,probBreak,probDepth); 177 child = shape->mShape->nodes[child].nextSibling; 178 } 179 180 return; 181 } 182 183 if (!probBreak || smRandom.randF() < pb) 184 // option b -- break the shape off at this level 185 currentPart = NULL; // fall through to option C 186 187 // option c -- add this piece to the parent 188 189 if (!currentPart) 190 { 191 currentPart = new TSPartInstance(shape); 192 partList.push_back(currentPart); 193 } 194 195 // iterate through objects, add to currentPart 196 while (object>=0) 197 { 198 currentPart->addObject(object); 199 object = shape->mShape->objects[object].nextSibling; 200 } 201 202 // iterate through child nodes, call ourselves on each one with currentPart as is 203 while (child>=0) 204 { 205 TSPartInstance::breakShape(shape,currentPart,child,partList,probShatter,probBreak,probDepth); 206 child = shape->mShape->nodes[child].nextSibling; 207 } 208} 209 210//------------------------------------------------------------------------------------- 211// render methods -- we use TSShapeInstance code as much as possible 212// issues: setupTexturing expects a detail level, we give it an object detail level 213//------------------------------------------------------------------------------------- 214 215void TSPartInstance::render(S32 od, const TSRenderState &rdata) 216{ 217 S32 i; 218 219 // render mesh objects 220 for (i=0; i<mMeshObjects.size(); i++) 221 { 222 TSRenderState objState = rdata; 223 const char *meshName = mSourceShape->mShape->names[mMeshObjects[i]->object->nameIndex]; 224 mMeshObjects[i]->render(od,mSourceShape->mShape->mShapeVertexBuffer,mSourceShape->getMaterialList(),objState,1.0, meshName); 225 } 226} 227 228//------------------------------------------------------------------------------------- 229// Detail selection 230// 2 methods: 231// method 1: use source shapes detail levels... 232// method 2: pass in our own table... 233// In either case, you can compute the pixel size on your own or let open gl do it. 234// If you want to use method 2, you have to call setDetailData sometime before selecting detail 235//------------------------------------------------------------------------------------- 236 237void TSPartInstance::setDetailData(F32 * sizeCutoffs, S32 numDetails) 238{ 239 if (mSizeCutoffs == sizeCutoffs && mNumDetails==numDetails) 240 return; 241 242 mSizeCutoffs = sizeCutoffs; 243 mNumDetails = numDetails; 244 delete [] mPolyCount; 245 mPolyCount = NULL; 246} 247 248/* 249void TSPartInstance::selectCurrentDetail(bool ignoreScale) 250{ 251 if (mSizeCutoffs) 252 { 253 selectCurrentDetail(mSizeCutoffs,mNumDetails,ignoreScale); 254 return; 255 } 256 257 mSourceShape->selectCurrentDetail(ignoreScale); 258 mCurrentObjectDetail = mSourceShape->getCurrentDetail(); 259 mCurrentIntraDL = mSourceShape->getCurrentIntraDetail(); 260} 261 262void TSPartInstance::selectCurrentDetail(F32 pixelSize) 263{ 264 if (mSizeCutoffs) 265 { 266 selectCurrentDetail(pixelSize,mSizeCutoffs,mNumDetails); 267 return; 268 } 269 270 mSourceShape->selectCurrentDetail(pixelSize); 271 mCurrentObjectDetail = mSourceShape->getCurrentDetail(); 272 mCurrentIntraDL = mSourceShape->getCurrentIntraDetail(); 273} 274 275void TSPartInstance::selectCurrentDetail(F32 dist, F32 invScale) 276{ 277 if (mSizeCutoffs) 278 { 279 const RectI &viewport = GFX->getViewport(); 280 F32 pixelScale = viewport.extent.x * 1.6f / 640.0f; 281 F32 pixelSize = GFX->projectRadius(dist*invScale,mSourceShape->getShape()->radius) * pixelScale * TSShapeInstance::smDetailAdjust; 282 selectCurrentDetail(pixelSize,mSizeCutoffs,mNumDetails); 283 return; 284 } 285 286 mSourceShape->selectCurrentDetail(dist, invScale); 287 mCurrentObjectDetail = mSourceShape->getCurrentDetail(); 288 mCurrentIntraDL = mSourceShape->getCurrentIntraDetail(); 289} 290 291void TSPartInstance::selectCurrentDetail(F32 * sizeCutoffs, S32 numDetails, bool ignoreScale) 292{ 293 // compute pixel size 294 Point3F p; 295 MatrixF toCam = GFX->getWorldMatrix(); 296 toCam.mulP(mCenter,&p); 297 F32 dist = mDot(p,p); 298 F32 scale = 1.0f; 299 if (!ignoreScale) 300 { 301 // any scale? 302 Point3F x,y,z; 303 toCam.getRow(0,&x); 304 toCam.getRow(1,&y); 305 toCam.getRow(2,&z); 306 F32 scalex = mDot(x,x); 307 F32 scaley = mDot(y,y); 308 F32 scalez = mDot(z,z); 309 scale = scalex; 310 if (scaley > scale) 311 scale = scaley; 312 if (scalez > scale) 313 scale = scalez; 314 } 315 dist /= scale; 316 dist = mSqrt(dist); 317 318 const RectI &viewport = GFX->getViewport(); 319 // JMQMERGE: is this using a hardcoded res/aspect ? (and the code above) 320 F32 pixelScale = viewport.extent.x * 1.6f / 640.0f; 321 F32 pixelRadius = GFX->projectRadius(dist,mRadius) * pixelScale * TSShapeInstance::smDetailAdjust; 322 323 selectCurrentDetail(pixelRadius,sizeCutoffs,numDetails); 324} 325 326void TSPartInstance::selectCurrentDetail(F32 pixelSize, F32 * sizeCutoffs, S32 numDetails) 327{ 328 mCurrentObjectDetail = 0; 329 while (numDetails) 330 { 331 if (pixelSize > *sizeCutoffs) 332 return; 333 mCurrentObjectDetail++; 334 numDetails--; 335 sizeCutoffs++; 336 } 337 mCurrentObjectDetail = -1; 338} 339*/ 340 341//------------------------------------------------------------------------------------- 342// Detail query methods...complicated because there are two ways that detail information 343// can be determined...1) using source shape, or 2) using mSizeCutoffs 344//------------------------------------------------------------------------------------- 345 346F32 TSPartInstance::getDetailSize(S32 dl) const 347{ 348 if (dl<0) 349 return 0; 350 else if (mSizeCutoffs && dl<mNumDetails) 351 return mSizeCutoffs[dl]; 352 else if (!mSizeCutoffs && dl<=mSourceShape->getShape()->mSmallestVisibleDL) 353 return mSourceShape->getShape()->details[dl].size; 354 else return 0; 355} 356 357S32 TSPartInstance::getPolyCount(S32 dl) 358{ 359 if (!mPolyCount) 360 computePolyCount(); 361 362 if (dl<0 || dl>=mNumDetails) 363 return 0; 364 else 365 return mPolyCount[dl]; 366} 367 368void TSPartInstance::computePolyCount() 369{ 370 if (!mSizeCutoffs) 371 mNumDetails = mSourceShape->getShape()->mSmallestVisibleDL+1; 372 373 delete [] mPolyCount; 374 mPolyCount = new S32[mNumDetails]; 375 376 for (S32 i=0; i<mNumDetails; i++) 377 { 378 mPolyCount[i] = 0; 379 for (S32 j=0; j<mMeshObjects.size(); j++) 380 { 381 if (mMeshObjects[j]->getMesh(i)) 382 mPolyCount[i] += mMeshObjects[j]->getMesh(i)->getNumPolys(); 383 } 384 } 385} 386 387 388