diff --git a/Engine/source/T3D/assets/ShapeAsset.cpp b/Engine/source/T3D/assets/ShapeAsset.cpp index 99becc8a6..c532d2370 100644 --- a/Engine/source/T3D/assets/ShapeAsset.cpp +++ b/Engine/source/T3D/assets/ShapeAsset.cpp @@ -129,6 +129,13 @@ ShapeAsset::ShapeAsset() mConstructorFileName = StringTable->EmptyString(); mFilePath = StringTable->EmptyString(); mConstructorFilePath = StringTable->EmptyString(); + + mDiffuseImposterFileName = StringTable->EmptyString(); + mDiffuseImposterPath = StringTable->EmptyString(); + mNormalImposterFileName = StringTable->EmptyString(); + mNormalImposterPath = StringTable->EmptyString(); + + mLoadedState = AssetErrCode::NotLoaded; } @@ -162,6 +169,12 @@ void ShapeAsset::initPersistFields() &setShapeFile, &getShapeFile, "Path to the shape file we want to render"); addProtectedField("constuctorFileName", TypeAssetLooseFilePath, Offset(mConstructorFileName, ShapeAsset), &setShapeConstructorFile, &getShapeConstructorFile, "Path to the shape file we want to render"); + + addProtectedField("diffuseImposterFileName", TypeAssetLooseFilePath, Offset(mDiffuseImposterFileName, ShapeAsset), + &setDiffuseImposterFile, &getDiffuseImposterFile, "Path to the diffuse imposter file we want to render"); + addProtectedField("normalImposterFileName", TypeAssetLooseFilePath, Offset(mNormalImposterFileName, ShapeAsset), + &setNormalImposterFile, &getNormalImposterFile, "Path to the normal imposter file we want to render"); + } void ShapeAsset::setDataField(StringTableEntry slotName, StringTableEntry array, StringTableEntry value) @@ -193,6 +206,20 @@ void ShapeAsset::initializeAsset() mConstructorFilePath = getOwned() ? expandAssetFilePath(mConstructorFilePath) : mConstructorFilePath; + mDiffuseImposterPath = getOwned() ? expandAssetFilePath(mDiffuseImposterFileName) : mDiffuseImposterFileName; + if (mDiffuseImposterPath == StringTable->EmptyString()) + { + String diffusePath = String(mFilePath) + "_imposter.dds"; + mDiffuseImposterPath = StringTable->insert(diffusePath.c_str()); + } + + mNormalImposterPath = getOwned() ? expandAssetFilePath(mNormalImposterFileName) : mNormalImposterFileName; + if (mNormalImposterPath == StringTable->EmptyString()) + { + String normalPath = String(mFilePath) + "_imposter_normals.dds"; + mNormalImposterPath = StringTable->insert(normalPath.c_str()); + } + loadShape(); } @@ -232,6 +259,42 @@ void ShapeAsset::setShapeConstructorFile(const char* pShapeConstructorFile) refreshAsset(); } +void ShapeAsset::setDiffuseImposterFile(const char* pImageFile) +{ + // Sanity! + AssertFatal(pImageFile != NULL, "Cannot use a NULL image file."); + + // Fetch image file. + pImageFile = StringTable->insert(pImageFile, true); + + // Ignore no change, + if (pImageFile == mDiffuseImposterFileName) + return; + + mDiffuseImposterFileName = getOwned() ? expandAssetFilePath(pImageFile) : pImageFile; + + // Refresh the asset. + refreshAsset(); +} + +void ShapeAsset::setNormalImposterFile(const char* pImageFile) +{ + // Sanity! + AssertFatal(pImageFile != NULL, "Cannot use a NULL image file."); + + // Fetch image file. + pImageFile = StringTable->insert(pImageFile, true); + + // Ignore no change, + if (pImageFile == mNormalImposterFileName) + return; + + mNormalImposterFileName = getOwned() ? expandAssetFilePath(pImageFile) : pImageFile; + + // Refresh the asset. + refreshAsset(); +} + void ShapeAsset::_onResourceChanged(const Torque::Path &path) { if (path != Torque::Path(mFilePath) ) @@ -292,6 +355,10 @@ bool ShapeAsset::loadShape() return false; //if it failed to load, bail out } + mShape->setupBillboardDetails(mFilePath, mDiffuseImposterPath, mNormalImposterPath); + + //If they exist, grab our imposters here and bind them to our shapeAsset + bool hasBlends = false; //Now that we've successfully loaded our shape and have any materials and animations loaded diff --git a/Engine/source/T3D/assets/ShapeAsset.h b/Engine/source/T3D/assets/ShapeAsset.h index 36a0a68c1..7288ec515 100644 --- a/Engine/source/T3D/assets/ShapeAsset.h +++ b/Engine/source/T3D/assets/ShapeAsset.h @@ -73,6 +73,12 @@ protected: StringTableEntry mConstructorFilePath; Resource mShape; + StringTableEntry mDiffuseImposterFileName; + StringTableEntry mDiffuseImposterPath; + + StringTableEntry mNormalImposterFileName; + StringTableEntry mNormalImposterPath; + //Material assets we're dependent on and use Vector mMaterialAssetIds; Vector> mMaterialAssets; @@ -170,6 +176,15 @@ public: inline StringTableEntry getShapeFilePath(void) const { return mFilePath; }; inline StringTableEntry getShapeConstructorFilePath(void) const { return mConstructorFilePath; }; + //Imposter images + void setDiffuseImposterFile(const char* pImageFile); + inline StringTableEntry getDiffuseImposterFile(void) const { return mDiffuseImposterFileName; }; + inline StringTableEntry getDiffuseImposterFilePath(void) const { return mDiffuseImposterPath; }; + + void setNormalImposterFile(const char* pImageFile); + inline StringTableEntry getNormalImposterFile(void) const { return mNormalImposterFileName; }; + inline StringTableEntry getNormalImposterFilePath(void) const { return mNormalImposterPath; }; + static U32 getAssetByFilename(StringTableEntry fileName, AssetPtr* shapeAsset); static StringTableEntry getAssetIdByFilename(StringTableEntry fileName); @@ -188,6 +203,10 @@ protected: static bool setShapeConstructorFile(void* obj, const char* index, const char* data) { static_cast(obj)->setShapeConstructorFile(data); return false; } static const char* getShapeConstructorFile(void* obj, const char* data) { return static_cast(obj)->getShapeConstructorFile(); } + static bool setDiffuseImposterFile(void* obj, StringTableEntry index, StringTableEntry data) { static_cast(obj)->setDiffuseImposterFile(data); return false; } + static const char* getDiffuseImposterFile(void* obj, const char* data) { return static_cast(obj)->getDiffuseImposterFile(); } + static bool setNormalImposterFile(void* obj, StringTableEntry index, StringTableEntry data) { static_cast(obj)->setNormalImposterFile(data); return false; } + static const char* getNormalImposterFile(void* obj, const char* data) { return static_cast(obj)->getNormalImposterFile(); } }; #ifdef TORQUE_TOOLS diff --git a/Engine/source/T3D/assets/assetImporter.cpp b/Engine/source/T3D/assets/assetImporter.cpp index 5d3d66180..7c0bda9ae 100644 --- a/Engine/source/T3D/assets/assetImporter.cpp +++ b/Engine/source/T3D/assets/assetImporter.cpp @@ -3062,6 +3062,15 @@ Torque::Path AssetImporter::importShapeAsset(AssetImportObject* assetItem) } } + if (Con::getBoolVariable("$TSLastDetail::dumpImposters", false)) + { + String imposterPath = assetItem->assetName + "_imposter.png"; + String normalsPath = assetItem->assetName + "_imposter_normals.png"; + + newAsset->setDiffuseImposterFile(imposterPath.c_str()); + newAsset->setNormalImposterFile(normalsPath.c_str()); + } + Taml tamlWriter; bool importSuccessful = tamlWriter.write(newAsset, tamlPath.c_str()); diff --git a/Engine/source/ts/tsLastDetail.cpp b/Engine/source/ts/tsLastDetail.cpp index 9af2c22fa..950b4c5f6 100644 --- a/Engine/source/ts/tsLastDetail.cpp +++ b/Engine/source/ts/tsLastDetail.cpp @@ -86,6 +86,8 @@ TSLastDetail::TSLastDetail( TSShape *shape, mCenter = mShape->center; mCachePath = cachePath; + mDiffusePath = mCachePath + "_imposter.dds"; + mNormalPath = mCachePath + "_imposter_normals.dds"; mMaterial = NULL; mMatInstance = NULL; @@ -94,6 +96,38 @@ TSLastDetail::TSLastDetail( TSShape *shape, smLastDetails.push_back( this ); } +TSLastDetail::TSLastDetail(TSShape* shape, + const String& cachePath, + const String& diffusePath, + const String& normalPath, + U32 numEquatorSteps, + U32 numPolarSteps, + F32 polarAngle, + bool includePoles, + S32 dl, S32 dim) +{ + mNumEquatorSteps = getMax(numEquatorSteps, (U32)1); + mNumPolarSteps = numPolarSteps; + mPolarAngle = polarAngle; + mIncludePoles = includePoles; + mShape = shape; + mDl = dl; + mDim = getMax(dim, (S32)32); + + mRadius = mShape->mRadius; + mCenter = mShape->center; + + mCachePath = cachePath; + mDiffusePath = diffusePath; + mNormalPath = normalPath; + + mMaterial = NULL; + mMatInstance = NULL; + + // Store this in the static list. + smLastDetails.push_back(this); +} + TSLastDetail::~TSLastDetail() { SAFE_DELETE( mMatInstance ); @@ -189,7 +223,8 @@ void TSLastDetail::update( bool forceUpdate ) // Do we need to update the imposter? const String diffuseMapPath = _getDiffuseMapPath(); - if ( forceUpdate || + bool isFile = Platform::isFile(diffuseMapPath.c_str()); + if ( forceUpdate || !Platform::isFile(diffuseMapPath.c_str()) || Platform::compareModifiedTimes( diffuseMapPath, shapeFile ) <= 0 ) _update(); @@ -218,8 +253,9 @@ void TSLastDetail::update( bool forceUpdate ) // Setup the material for this imposter. mMaterial = MATMGR->allocateAndRegister( String::EmptyString ); mMaterial->mAutoGenerated = true; - mMaterial->_setDiffuseMap(diffuseMapPath,0); - mMaterial->_setNormalMap(_getNormalMapPath(), 0); + mMaterial->setDiffuseMapFile(diffuseMapPath, 0); + mMaterial->setNormalMapFile(_getNormalMapPath(), 0); + mMaterial->mImposterLimits.set( (mNumPolarSteps * 2) + 1, mNumEquatorSteps, mPolarAngle, mIncludePoles ); mMaterial->mTranslucent = true; mMaterial->mTranslucentBlendOp = Material::None; @@ -466,8 +502,8 @@ void TSLastDetail::_update() // Should we dump the images? if ( Con::getBoolVariable( "$TSLastDetail::dumpImposters", false ) ) { - String imposterPath = mCachePath + ".imposter.png"; - String normalsPath = mCachePath + ".imposter_normals.png"; + String imposterPath = _getDiffuseMapPath(); + String normalsPath = _getNormalMapPath(); FileStream stream; if ( stream.open( imposterPath, Torque::FS::File::Write ) ) diff --git a/Engine/source/ts/tsLastDetail.h b/Engine/source/ts/tsLastDetail.h index abfb3bac1..05e3c9dcb 100644 --- a/Engine/source/ts/tsLastDetail.h +++ b/Engine/source/ts/tsLastDetail.h @@ -92,6 +92,9 @@ protected: /// where we'll be storing our cache for rendered imposters. String mCachePath; + String mDiffusePath; + String mNormalPath; + /// The shape detail level to capture into /// the imposters. S32 mDl; @@ -148,10 +151,10 @@ protected: void _validateDim(); /// Helper which returns the imposter diffuse map path. - String _getDiffuseMapPath() const { return mCachePath + ".imposter.dds"; } + String _getDiffuseMapPath() const { return mDiffusePath; } /// Helper which returns the imposter normal map path. - String _getNormalMapPath() const { return mCachePath + ".imposter_normals.dds"; } + String _getNormalMapPath() const { return mNormalPath; } public: @@ -164,6 +167,16 @@ public: S32 dl, S32 dim ); + TSLastDetail(TSShape* shape, + const String& cachePath, + const String& diffusePath, + const String& normalPath, + U32 numEquatorSteps, + U32 numPolarSteps, + F32 polarAngle, + bool includePoles, + S32 dl, S32 dim); + ~TSLastDetail(); /// Global preference for rendering imposters to shadows. diff --git a/Engine/source/ts/tsShape.cpp b/Engine/source/ts/tsShape.cpp index 36825014b..e6eb2f4fd 100644 --- a/Engine/source/ts/tsShape.cpp +++ b/Engine/source/ts/tsShape.cpp @@ -969,6 +969,39 @@ void TSShape::setupBillboardDetails( const String &cachePath ) } } +void TSShape::setupBillboardDetails(const String& cachePath, const String& diffsePath, const String& normalPath) +{ + // set up billboard details -- only do this once, meaning that + // if we add a sequence to the shape we don't redo the billboard + // details... + if (!billboardDetails.empty()) + return; + + for (U32 i = 0; i < details.size(); i++) + { + const Detail& det = details[i]; + + if (det.subShapeNum >= 0) + continue; // not a billboard detail + + while (billboardDetails.size() <= i) + billboardDetails.push_back(NULL); + + billboardDetails[i] = new TSLastDetail(this, + cachePath, + diffsePath, + normalPath, + det.bbEquatorSteps, + det.bbPolarSteps, + det.bbPolarAngle, + det.bbIncludePoles, + det.bbDetailLevel, + det.bbDimension); + + billboardDetails[i]->update(); + } +} + void TSShape::initMaterialList() { S32 numSubShapes = subShapeFirstObject.size(); diff --git a/Engine/source/ts/tsShape.h b/Engine/source/ts/tsShape.h index 164e378a9..c2a3fc1c4 100644 --- a/Engine/source/ts/tsShape.h +++ b/Engine/source/ts/tsShape.h @@ -429,6 +429,8 @@ class TSShape void setupBillboardDetails( const String &cachePath ); + void setupBillboardDetails(const String& cachePath, const String& diffsePath, const String& normalPath); + /// Inits object list (no geometry buffers) void initObjects();