@@ -405,6 +405,53 @@ bool GuiShapeEdPreview::setObjectModel(const char* modelName)
405405 return true ;
406406}
407407
408+ bool GuiShapeEdPreview::findCompanionShape (const Torque::Path& dsqPath, Torque::Path& outShapePath)
409+ {
410+ // AssimpLoader and ColladaLoader exports as "modelname_sequencename.dsq" alongside "modelname.cached.dts"
411+ // so strip everything from the last underscore to find the base name
412+ String fileName = dsqPath.getFileName ();
413+ String::SizeType sep = fileName.find (' _' ,0 , String::Right);
414+
415+ if (sep != String::NPos)
416+ {
417+ Torque::Path candidate (dsqPath);
418+ candidate.setFileName (fileName.substr (0 , sep));
419+
420+ candidate.setExtension (" cached.dts" );
421+ if (Torque::FS::IsFile (candidate.getFullPath ()))
422+ {
423+ outShapePath = candidate;
424+ return true ;
425+ }
426+
427+ candidate.setExtension (" dts" );
428+ if (Torque::FS::IsFile (candidate.getFullPath ()))
429+ {
430+ outShapePath = candidate;
431+ return true ;
432+ }
433+ }
434+
435+ // fallback: same filename, just swap extension
436+ Torque::Path direct (dsqPath);
437+
438+ direct.setExtension (" cached.dts" );
439+ if (Torque::FS::IsFile (direct.getFullPath ()))
440+ {
441+ outShapePath = direct;
442+ return true ;
443+ }
444+
445+ direct.setExtension (" dts" );
446+ if (Torque::FS::IsFile (direct.getFullPath ()))
447+ {
448+ outShapePath = direct;
449+ return true ;
450+ }
451+
452+ return false ;
453+ }
454+
408455bool GuiShapeEdPreview::setObjectShapeAsset (const char * assetId)
409456{
410457 SAFE_DELETE (mModel );
@@ -422,16 +469,74 @@ bool GuiShapeEdPreview::setObjectShapeAsset(const char* assetId)
422469 ShapeAsset* asset = AssetDatabase.acquireAsset <ShapeAsset>(id);
423470 modelName = asset->getShapeFile ();
424471 AssetDatabase.releaseAsset (id);
472+ return setObjectModel (modelName);
425473 }
426474 else if (assetType == StringTable->insert (" ShapeAnimationAsset" ))
427475 {
428476 ShapeAnimationAsset* asset = AssetDatabase.acquireAsset <ShapeAnimationAsset>(id);
429- modelName = asset->getAnimationPath ();
477+ StringTableEntry animPath = asset->getAnimationPath ();
430478 AssetDatabase.releaseAsset (id);
479+ Torque::Path dsqPath (animPath);
480+ String fileExt = String::ToLower (dsqPath.getExtension ());
481+
482+ if (fileExt != String (" dsq" ))
483+ {
484+ return setObjectModel (animPath);
485+ }
486+
487+ Torque::Path shapePath;
488+ if (!findCompanionShape (dsqPath, shapePath))
489+ {
490+ Con::warnf (" GuiShapeEdPreview::setObjectShapeAsset - "
491+ " No companion shape found for '%s'" , animPath);
492+ return false ;
493+ }
494+
495+ if (!setObjectModel (shapePath.getFullPath ()))
496+ {
497+ Con::warnf (" GuiShapeEdPreview::setObjectShapeAsset - "
498+ " Could not load companion shape for '%s'" , animPath);
499+ return false ;
500+ }
501+
502+ FileStream dsqStream;
503+ if (!dsqStream.open (animPath, Torque::FS::File::Read))
504+ {
505+ Con::warnf (" GuiShapeEdPreview::setObjectShapeAsset - "
506+ " Could not open '%s'" , animPath);
507+ SAFE_DELETE (mModel );
508+ return false ;
509+ }
510+
511+ TSShape* shape = mModel ->getShape ();
512+
513+ bool ok = shape->importSequences (&dsqStream, String (animPath));
514+ dsqStream.close ();
515+
516+ if (!ok)
517+ {
518+ Con::warnf (" GuiShapeEdPreview::setObjectShapeAsset - "
519+ " importSequences failed for '%s'" , animPath);
520+ SAFE_DELETE (mModel );
521+ return false ;
522+ }
523+
524+ setAllMeshesHidden (true );
525+ mRenderNodes = true ;
526+ if (shape->sequences .size () > 0 )
527+ {
528+ // importSequences appends, so the new one is always last
529+ const String& seqName = shape->getSequenceName (shape->sequences .size () - 1 );
530+ addThread ();
531+ mActiveThread = 0 ;
532+ setActiveThreadSequence (seqName.c_str (), 0 .0f , 0 .0f , true );
533+ }
534+
535+ return true ;
431536 }
432537 }
433538
434- return setObjectModel (modelName) ;
539+ return false ;
435540}
436541
437542void GuiShapeEdPreview::_onResourceChanged (const Torque::Path& path)
0 commit comments