worldEditorSelection.cpp
Engine/source/gui/worldEditor/worldEditorSelection.cpp
Public Functions
ConsoleDocClass(WorldEditorSelection , "@brief Specialized simset that stores the objects selected by the <a href="/coding/file/gizmo_8h/#gizmo_8h_1a10fcd3bee2ea25191e31795e36bdeba1a81f4537631c9ab219ec74de554483adc">World</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Editor\n\n</a>" "Editor use <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">only.\n\n</a>" "@internal" )
DefineEngineMethod(WorldEditorSelection , containsGlobalBounds , bool , () , "True <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> an object with global bounds is contained in the selection." )
DefineEngineMethod(WorldEditorSelection , getBoxCentroid , Point3F , () , "Return the center of the bounding box around the selection." )
DefineEngineMethod(WorldEditorSelection , getCentroid , Point3F , () , "Return the median of all object positions in the selection." )
DefineEngineMethod(WorldEditorSelection , offset , void , (Point3F delta, F32 gridSnap) , (0.0f) , "Move all objects in the selection by the given delta." )
DefineEngineMethod(WorldEditorSelection , subtract , void , (SimSet *selection) , "Remove all objects in the given set from this selection." )
DefineEngineMethod(WorldEditorSelection , union , <a href="/coding/file/tsshapeconstruct_8cpp/#tsshapeconstruct_8cpp_1ac9c84fa68bbad002983e35ce3663c686">void</a> , (SimSet *selection) , "Add all objects in the given set <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> this selection." )
Detailed Description
Public Functions
ConsoleDocClass(WorldEditorSelection , "@brief Specialized simset that stores the objects selected by the <a href="/coding/file/gizmo_8h/#gizmo_8h_1a10fcd3bee2ea25191e31795e36bdeba1a81f4537631c9ab219ec74de554483adc">World</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Editor\n\n</a>" "Editor use <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">only.\n\n</a>" "@internal" )
DefineEngineMethod(WorldEditorSelection , containsGlobalBounds , bool , () , "True <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> an object with global bounds is contained in the selection." )
DefineEngineMethod(WorldEditorSelection , getBoxCentroid , Point3F , () , "Return the center of the bounding box around the selection." )
DefineEngineMethod(WorldEditorSelection , getCentroid , Point3F , () , "Return the median of all object positions in the selection." )
DefineEngineMethod(WorldEditorSelection , offset , void , (Point3F delta, F32 gridSnap) , (0.0f) , "Move all objects in the selection by the given delta." )
DefineEngineMethod(WorldEditorSelection , subtract , void , (SimSet *selection) , "Remove all objects in the given set from this selection." )
DefineEngineMethod(WorldEditorSelection , union , <a href="/coding/file/tsshapeconstruct_8cpp/#tsshapeconstruct_8cpp_1ac9c84fa68bbad002983e35ce3663c686">void</a> , (SimSet *selection) , "Add all objects in the given set <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> this selection." )
IMPLEMENT_CONOBJECT(WorldEditorSelection )
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 "gui/worldEditor/worldEditorSelection.h" 25#include "gui/worldEditor/worldEditor.h" 26#include "scene/sceneObject.h" 27 28IMPLEMENT_CONOBJECT( WorldEditorSelection ); 29 30ConsoleDocClass( WorldEditorSelection, 31 "@brief Specialized simset that stores the objects selected by the World Editor\n\n" 32 "Editor use only.\n\n" 33 "@internal" 34); 35 36//----------------------------------------------------------------------------- 37 38WorldEditorSelection::WorldEditorSelection() 39 : mCentroidValid(false), 40 mAutoSelect(false), 41 mContainsGlobalBounds(false), 42 mPrevCentroid(0.0f, 0.0f, 0.0f) 43{ 44 // Selections are transient by default. 45 setCanSave( false ); 46 setEditorOnly( true ); 47} 48 49//----------------------------------------------------------------------------- 50 51WorldEditorSelection::~WorldEditorSelection() 52{ 53} 54 55//----------------------------------------------------------------------------- 56 57void WorldEditorSelection::initPersistFields() 58{ 59 Parent::initPersistFields(); 60} 61 62//----------------------------------------------------------------------------- 63 64void WorldEditorSelection::setCanSave( bool value ) 65{ 66 if( getCanSave() == value ) 67 return; 68 69 Parent::setCanSave( value ); 70 71 // If we went from being transient to being persistent, 72 // make sure all objects in the selection have persistent IDs. 73 74 if( getCanSave() ) 75 for( iterator iter = begin(); iter != end(); ++ iter ) 76 ( *iter )->getOrCreatePersistentId(); 77} 78 79//----------------------------------------------------------------------------- 80 81bool WorldEditorSelection::objInSet( SimObject* obj ) 82{ 83 if( !mIsResolvingPIDs ) 84 resolvePIDs(); 85 86 lock(); 87 88 bool result = false; 89 for( iterator iter = begin(); iter != end(); ++ iter ) 90 { 91 if( obj == *iter ) 92 { 93 result = true; 94 break; 95 } 96 97 WorldEditorSelection* set = dynamic_cast< WorldEditorSelection* >( *iter ); 98 if( set && set->objInSet( obj ) ) 99 { 100 result = true; 101 break; 102 } 103 } 104 105 unlock(); 106 107 return result; 108} 109 110//----------------------------------------------------------------------------- 111 112void WorldEditorSelection::addObject( SimObject* obj ) 113{ 114 // Return if object is already in selection. 115 116 if( objInSet( obj ) ) 117 return; 118 119 // Refuse to add object if this selection is locked. 120 121 if( isLocked() ) 122 return; 123 124 // Prevent adding us to ourselves. 125 126 if( obj == this ) 127 return; 128 129 // If the object is itself a selection set, make sure we 130 // don't create a cycle. 131 132 WorldEditorSelection* selection = dynamic_cast< WorldEditorSelection* >( obj ); 133 if( selection && !selection->objInSet( this ) ) 134 return; 135 136 // Refuse to add any of our parents. 137 138 for( SimGroup* group = getGroup(); group != NULL; group = group->getGroup() ) 139 if( obj == group ) 140 return; 141 142 invalidateCentroid(); 143 144 Parent::addObject( obj ); 145 146 if( mAutoSelect ) 147 WorldEditor::markAsSelected( obj, true ); 148 149 return; 150} 151 152//----------------------------------------------------------------------------- 153 154void WorldEditorSelection::removeObject( SimObject* obj ) 155{ 156 if( !objInSet( obj ) ) 157 return; 158 159 // Refuse to remove object if this selection is locked. 160 161 if( isLocked() ) 162 return; 163 164 invalidateCentroid(); 165 166 Parent::removeObject( obj ); 167 168 if( mAutoSelect ) 169 WorldEditor::markAsSelected( obj, false ); 170 171 return; 172} 173 174//----------------------------------------------------------------------------- 175 176bool WorldEditorSelection::containsGlobalBounds() 177{ 178 updateCentroid(); 179 return mContainsGlobalBounds; 180} 181 182//----------------------------------------------------------------------------- 183 184void WorldEditorSelection::updateCentroid() 185{ 186 if( mCentroidValid ) 187 return; 188 189 resolvePIDs(); 190 191 mCentroidValid = true; 192 193 mCentroid.set(0,0,0); 194 mBoxCentroid = mCentroid; 195 mBoxBounds.minExtents.set(1e10, 1e10, 1e10); 196 mBoxBounds.maxExtents.set(-1e10, -1e10, -1e10); 197 198 mContainsGlobalBounds = false; 199 200 if( empty() ) 201 return; 202 203 // 204 for( SimSet::iterator iter = begin(); iter != end(); ++ iter ) 205 { 206 SceneObject* obj = dynamic_cast<SceneObject*>( *iter ); 207 if( !obj ) 208 continue; 209 210 const MatrixF & mat = obj->getTransform(); 211 Point3F wPos; 212 mat.getColumn(3, &wPos); 213 214 // 215 mCentroid += wPos; 216 217 // 218 const Box3F& bounds = obj->getWorldBox(); 219 mBoxBounds.minExtents.setMin(bounds.minExtents); 220 mBoxBounds.maxExtents.setMax(bounds.maxExtents); 221 222 if(obj->isGlobalBounds()) 223 mContainsGlobalBounds = true; 224 } 225 226 mCentroid /= (F32) size(); 227 mBoxCentroid = mBoxBounds.getCenter(); 228} 229 230//----------------------------------------------------------------------------- 231 232const Point3F & WorldEditorSelection::getCentroid() 233{ 234 updateCentroid(); 235 return(mCentroid); 236} 237 238//----------------------------------------------------------------------------- 239 240const Point3F & WorldEditorSelection::getBoxCentroid() 241{ 242 updateCentroid(); 243 return(mBoxCentroid); 244} 245 246//----------------------------------------------------------------------------- 247 248const Box3F & WorldEditorSelection::getBoxBounds() 249{ 250 updateCentroid(); 251 return(mBoxBounds); 252} 253 254//----------------------------------------------------------------------------- 255 256Point3F WorldEditorSelection::getBoxBottomCenter() 257{ 258 updateCentroid(); 259 260 Point3F bottomCenter = mBoxCentroid; 261 bottomCenter.z -= mBoxBounds.len_z() * 0.5f; 262 263 return bottomCenter; 264} 265 266//----------------------------------------------------------------------------- 267 268void WorldEditorSelection::enableCollision() 269{ 270 for( iterator iter = begin(); iter != end(); ++ iter ) 271 { 272 SceneObject* object = dynamic_cast<SceneObject*>( *iter ); 273 if( object ) 274 object->enableCollision(); 275 } 276} 277 278//----------------------------------------------------------------------------- 279 280void WorldEditorSelection::disableCollision() 281{ 282 for( iterator iter = begin(); iter != end(); ++ iter ) 283 { 284 SceneObject* object = dynamic_cast< SceneObject* >( *iter ); 285 if( object ) 286 object->disableCollision(); 287 } 288} 289 290//----------------------------------------------------------------------------- 291 292void WorldEditorSelection::offset( const Point3F& offset, F32 gridSnap ) 293{ 294 for( iterator iter = begin(); iter != end(); ++ iter ) 295 { 296 SceneObject* obj = dynamic_cast<SceneObject*>( *iter ); 297 if( !obj ) 298 continue; 299 300 MatrixF mat = obj->getTransform(); 301 Point3F wPos; 302 mat.getColumn(3, &wPos); 303 304 // adjust 305 wPos += offset; 306 307 if( gridSnap != 0.f ) 308 { 309 wPos.x = _snapFloat(wPos.x, gridSnap); 310 wPos.y = _snapFloat(wPos.y, gridSnap); 311 wPos.z = _snapFloat(wPos.z, gridSnap); 312 } 313 314 mat.setColumn(3, wPos); 315 obj->setTransform(mat); 316 } 317 318 mCentroidValid = false; 319} 320 321F32 WorldEditorSelection::_snapFloat(const F32 &val, const F32 &snap) const 322{ 323 if (snap == 0.0f) 324 return val; 325 326 F32 a = mFmod(val, snap); 327 328 F32 temp = val; 329 330 if (mFabs(a) > (snap / 2)) 331 val < 0.0f ? temp -= snap : temp += snap; 332 333 return(temp - a); 334} 335 336 337//----------------------------------------------------------------------------- 338 339void WorldEditorSelection::setPosition(const Point3F & pos) 340{ 341 for( iterator iter = begin(); iter != end(); ++ iter ) 342 { 343 SceneObject* object = dynamic_cast<SceneObject*>( *iter ); 344 if( object ) 345 object->setPosition(pos); 346 } 347 348 mCentroidValid = false; 349} 350 351//----------------------------------------------------------------------------- 352 353void WorldEditorSelection::setCentroidPosition(bool useBoxCenter, const Point3F & pos) 354{ 355 Point3F centroid; 356 if( containsGlobalBounds() ) 357 { 358 centroid = getCentroid(); 359 } 360 else 361 { 362 centroid = useBoxCenter ? getBoxCentroid() : getCentroid(); 363 } 364 365 offset(pos - centroid); 366} 367 368//----------------------------------------------------------------------------- 369 370void WorldEditorSelection::orient(const MatrixF & rot, const Point3F & center) 371{ 372 // Orient all the selected objects to the given rotation 373 for( iterator iter = begin(); iter != end(); ++ iter ) 374 { 375 SceneObject* object = dynamic_cast< SceneObject* >( *iter ); 376 if( !object ) 377 continue; 378 379 MatrixF mat = rot; 380 mat.setPosition( object->getPosition() ); 381 object->setTransform(mat); 382 } 383 384 mCentroidValid = false; 385} 386 387//----------------------------------------------------------------------------- 388 389void WorldEditorSelection::rotate(const EulerF &rot) 390{ 391 for( iterator iter = begin(); iter != end(); ++ iter ) 392 { 393 SceneObject* object = dynamic_cast< SceneObject* >( *iter ); 394 if( !object ) 395 continue; 396 397 MatrixF mat = object->getTransform(); 398 399 MatrixF transform(rot); 400 mat.mul(transform); 401 402 object->setTransform(mat); 403 } 404} 405 406//----------------------------------------------------------------------------- 407 408void WorldEditorSelection::rotate(const EulerF & rot, const Point3F & center) 409{ 410 // single selections will rotate around own axis, multiple about world 411 if(size() == 1) 412 { 413 SceneObject* object = dynamic_cast<SceneObject*>(at(0)); 414 if (object) 415 { 416 MatrixF mat = object->getTransform(); 417 418 Point3F pos; 419 mat.getColumn(3, &pos); 420 421 // get offset in obj space 422 Point3F offset = pos - center; 423 MatrixF wMat = object->getWorldTransform(); 424 wMat.mulV(offset); 425 426 // 427 MatrixF transform(EulerF(0, 0, 0), -offset); 428 transform.mul(MatrixF(rot)); 429 transform.mul(MatrixF(EulerF(0, 0, 0), offset)); 430 mat.mul(transform); 431 432 object->setTransform(mat); 433 } 434 } 435 else 436 { 437 for( iterator iter = begin(); iter != end(); ++ iter ) 438 { 439 SceneObject* object = dynamic_cast< SceneObject* >( *iter ); 440 if( !object ) 441 continue; 442 443 MatrixF mat = object->getTransform(); 444 445 Point3F pos; 446 mat.getColumn(3, &pos); 447 448 // get offset in obj space 449 Point3F offset = pos - center; 450 451 MatrixF transform(rot); 452 Point3F wOffset; 453 transform.mulV(offset, &wOffset); 454 455 MatrixF wMat = object->getWorldTransform(); 456 wMat.mulV(offset); 457 458 // 459 transform.set(EulerF(0,0,0), -offset); 460 461 mat.setColumn(3, Point3F(0,0,0)); 462 wMat.setColumn(3, Point3F(0,0,0)); 463 464 transform.mul(wMat); 465 transform.mul(MatrixF(rot)); 466 transform.mul(mat); 467 mat.mul(transform); 468 469 mat.normalize(); 470 mat.setColumn(3, wOffset + center); 471 472 object->setTransform(mat); 473 } 474 } 475 476 mCentroidValid = false; 477} 478 479//----------------------------------------------------------------------------- 480 481void WorldEditorSelection::setRotate(const EulerF & rot) 482{ 483 for( iterator iter = begin(); iter != end(); ++ iter ) 484 { 485 SceneObject* object = dynamic_cast< SceneObject* >( *iter ); 486 if( !object ) 487 continue; 488 489 MatrixF mat = object->getTransform(); 490 Point3F pos; 491 mat.getColumn(3, &pos); 492 493 MatrixF rmat(rot); 494 rmat.setPosition(pos); 495 496 object->setTransform(rmat); 497 } 498} 499 500//----------------------------------------------------------------------------- 501 502void WorldEditorSelection::scale(const VectorF & scale) 503{ 504 for( iterator iter = begin(); iter != end(); ++ iter ) 505 { 506 SceneObject* object = dynamic_cast< SceneObject* >( *iter ); 507 if( !object ) 508 continue; 509 510 VectorF current = object->getScale(); 511 current.convolve(scale); 512 513 // clamp scale to sensible limits 514 current.setMax( Point3F( 0.01f ) ); 515 current.setMin( Point3F( 1000.0f ) ); 516 517 object->setScale(current); 518 } 519 520 mCentroidValid = false; 521} 522 523//----------------------------------------------------------------------------- 524 525void WorldEditorSelection::scale(const VectorF & scale, const Point3F & center) 526{ 527 for( iterator iter = begin(); iter != end(); ++ iter ) 528 { 529 SceneObject* object = dynamic_cast< SceneObject* >( *iter ); 530 if( !object ) 531 continue; 532 533 VectorF current = object->getScale(); 534 current.convolve(scale); 535 536 // clamp scale to sensible limits 537 current.setMax( Point3F( 0.01f ) ); 538 current.setMin( Point3F( 1000.0f ) ); 539 540 // Apply the scale first. If the object's scale doesn't change with 541 // this operation then this object doesn't scale. In this case 542 // we don't want to continue with the offset operation. 543 VectorF prevScale = object->getScale(); 544 object->setScale(current); 545 if( !object->getScale().equal(current) ) 546 continue; 547 548 // determine the actual scale factor to apply to the object offset 549 // need to account for the scale limiting above to prevent offsets 550 // being reduced to 0 which then cannot be restored by unscaling 551 VectorF adjustedScale = current / prevScale; 552 553 MatrixF mat = object->getTransform(); 554 555 Point3F pos; 556 mat.getColumn(3, &pos); 557 558 Point3F offset = pos - center; 559 offset *= adjustedScale; 560 561 object->setPosition(offset + center); 562 } 563} 564 565//----------------------------------------------------------------------------- 566 567void WorldEditorSelection::setScale(const VectorF & scale) 568{ 569 for( iterator iter = begin(); iter != end(); ++ iter ) 570 { 571 SceneObject* object = dynamic_cast< SceneObject* >( *iter ); 572 if( object ) 573 object->setScale( scale ); 574 } 575 576 mCentroidValid = false; 577} 578 579//----------------------------------------------------------------------------- 580 581void WorldEditorSelection::setScale(const VectorF & scale, const Point3F & center) 582{ 583 for( iterator iter = begin(); iter != end(); ++ iter ) 584 { 585 SceneObject* object = dynamic_cast< SceneObject* >( *iter ); 586 if( !object ) 587 continue; 588 589 MatrixF mat = object->getTransform(); 590 591 Point3F pos; 592 mat.getColumn(3, &pos); 593 594 Point3F offset = pos - center; 595 offset *= scale; 596 597 object->setPosition(offset + center); 598 object->setScale(scale); 599 } 600} 601 602//----------------------------------------------------------------------------- 603 604void WorldEditorSelection::addSize(const VectorF & newsize) 605{ 606 for( iterator iter = begin(); iter != end(); ++ iter ) 607 { 608 SceneObject* object = dynamic_cast< SceneObject* >( *iter ); 609 if( !object ) 610 continue; 611 612 if( object->isGlobalBounds() ) 613 continue; 614 615 const Box3F& bounds = object->getObjBox(); 616 VectorF extent = bounds.getExtents(); 617 VectorF scaledextent = object->getScale() * extent; 618 619 VectorF scale = (newsize + scaledextent) / scaledextent; 620 object->setScale( object->getScale() * scale ); 621 } 622} 623 624//----------------------------------------------------------------------------- 625 626void WorldEditorSelection::setSize(const VectorF & newsize) 627{ 628 for( iterator iter = begin(); iter != end(); ++ iter ) 629 { 630 SceneObject* object = dynamic_cast< SceneObject* >( *iter ); 631 if( !object ) 632 continue; 633 634 if( object->isGlobalBounds() ) 635 continue; 636 637 const Box3F& bounds = object->getObjBox(); 638 VectorF extent = bounds.getExtents(); 639 640 VectorF scale = newsize / extent; 641 object->setScale( scale ); 642 } 643} 644 645//============================================================================= 646// Console Methods. 647//============================================================================= 648// MARK: ---- Console Methods ---- 649 650//----------------------------------------------------------------------------- 651 652DefineEngineMethod( WorldEditorSelection, containsGlobalBounds, bool, (),, "True if an object with global bounds is contained in the selection." ) 653{ 654 return object->containsGlobalBounds(); 655} 656 657//----------------------------------------------------------------------------- 658 659DefineEngineMethod( WorldEditorSelection, getCentroid, Point3F, (),, "Return the median of all object positions in the selection." ) 660{ 661 const Point3F& centroid = object->getCentroid(); 662 return centroid; 663} 664 665//----------------------------------------------------------------------------- 666 667DefineEngineMethod( WorldEditorSelection, getBoxCentroid, Point3F, (),, "Return the center of the bounding box around the selection." ) 668{ 669 const Point3F& boxCentroid = object->getBoxCentroid(); 670 return boxCentroid; 671} 672 673//----------------------------------------------------------------------------- 674 675DefineEngineMethod(WorldEditorSelection, offset, void, (Point3F delta, F32 gridSnap), (0.0f), "Move all objects in the selection by the given delta.") 676{ 677 object->offset( delta, gridSnap ); 678 WorldEditor::updateClientTransforms( object ); 679} 680 681//----------------------------------------------------------------------------- 682 683DefineEngineMethod( WorldEditorSelection, union, void, (SimSet* selection),, "Add all objects in the given set to this selection." ) 684{ 685 if( !selection) 686 { 687 Con::errorf( "WorldEditorSelection::union - no SimSet"); 688 return; 689 } 690 691 const U32 numObjects = selection->size(); 692 for( U32 i = 0; i < numObjects; ++ i ) 693 object->addObject( selection->at( i ) ); 694} 695 696//----------------------------------------------------------------------------- 697 698DefineEngineMethod( WorldEditorSelection, subtract, void, (SimSet* selection),, "Remove all objects in the given set from this selection." ) 699{ 700 if( !selection ) 701 { 702 Con::errorf( "WorldEditorSelection::subtract - no SimSet" ); 703 return; 704 } 705 706 const U32 numObjects = selection->size(); 707 for( U32 i = 0; i < numObjects; ++ i ) 708 object->removeObject( selection->at( i ) ); 709} 710