From 925087db5b29256c6ccf802c9ede307323639cb9 Mon Sep 17 00:00:00 2001 From: marauder2k7 Date: Wed, 15 Jul 2026 16:39:51 +0100 Subject: [PATCH] ImageAsset sampler description Allows the image asset to dictate what its sampler setup should be. This method allows an image asset to override what the default material setup sets for the samplers settings such as mipbias, address wrapping and filtering. More settings can be added to this i think, or the fields could have better names possibly Also included for testing purposes is a fix on guiInspector where group is null. --- Engine/source/T3D/assets/ImageAsset.cpp | 178 ++++++++++++++++-- Engine/source/T3D/assets/ImageAsset.h | 35 ++++ .../source/T3D/assets/ImageAssetInspectors.h | 21 +++ Engine/source/gui/editor/guiInspector.cpp | 2 + Engine/source/materials/materialDefinition.h | 13 +- Engine/source/materials/processedMaterial.cpp | 6 +- Engine/source/materials/processedMaterial.h | 3 + .../materials/processedShaderMaterial.cpp | 51 ++++- .../materials/processedShaderMaterial.h | 54 +++--- 9 files changed, 320 insertions(+), 43 deletions(-) diff --git a/Engine/source/T3D/assets/ImageAsset.cpp b/Engine/source/T3D/assets/ImageAsset.cpp index 2d068123ca..37a49400a8 100644 --- a/Engine/source/T3D/assets/ImageAsset.cpp +++ b/Engine/source/T3D/assets/ImageAsset.cpp @@ -230,6 +230,11 @@ ImageAsset::ImageAsset() : mImageFile(StringTable->EmptyString()), mUseMips(true), mIsHDRImage(false), + mFilterType(GFXTextureFilter_COUNT), + mAddressMode(GFXAddress_COUNT), + mMaxAnisotropy(0), + mUseMipLODBias(false), + mMipLODBias(0.0f), mImageType(Albedo), mIsNamedTarget(false), mImageWidth(-1), @@ -282,6 +287,30 @@ void ImageAsset::initPersistFields() addProtectedField("useMips", TypeBool, Offset(mUseMips, ImageAsset), &setGenMips, &defaultProtectedGetFn, &writeGenMips, "Generate mip maps?"); addProtectedField("isHDRImage", TypeBool, Offset(mIsHDRImage, ImageAsset), &setTextureHDR, &defaultProtectedGetFn, &writeTextureHDR, "HDR Image?"); + addField("filterType", TYPEID< GFXTextureFilterType >(), Offset(mFilterType, ImageAsset), + "Explicit texture filtering override for this image. Leave as the default " + "(or set to GFXTextureFilterNone) to let the material's own filtering/anisotropy " + "settings apply as normal; set to Point, Linear, or Anisotropic to force that " + "filtering mode whenever this image is bound, regardless of the material."); + + addField("addressMode", TYPEID< GFXTextureAddressMode >(), Offset(mAddressMode, ImageAsset), + "Explicit U/V wrap mode override for this image. Leave as the default " + "(or set to GFXAddressWrap) to let the material decide; set to Clamp, " + "Mirror, Border, or MirrorOnce to force that wrap mode whenever this " + "image is bound, regardless of the material."); + + addField("maxAnisotropy", TypeS32, Offset(mMaxAnisotropy, ImageAsset), + "Explicit anisotropy level override (e.g. 2/4/8/16). 0 (default) means " + "let the material decide. Only has an effect when filterType is " + "Anisotropic (or left unset and the material picks Anisotropic itself)."); + + addField("useMipLODBias", TypeBool, Offset(mUseMipLODBias, ImageAsset), + "Whether mipLODBias below should override the material's default of 0."); + + addField("mipLODBias", TypeF32, Offset(mMipLODBias, ImageAsset), + "Explicit mip LOD bias override for this image. Only applied when " + "useMipLODBias is true."); + addField("imageType", TypeImageAssetType, Offset(mImageType, ImageAsset), "What the main use-case for the image is for."); } bool ImageAsset::onAdd() @@ -377,7 +406,7 @@ StringTableEntry ImageAsset::getAssetIdByFilename(StringTableEntry fileName) return imgAsset; } } - + } } } @@ -489,6 +518,10 @@ void ImageAsset::copyTo(SimObject* object) pAsset->setImageFile(getImageFile()); pAsset->setGenMips(getGenMips()); pAsset->setTextureHDR(getTextureHDR()); + pAsset->setFilterType(getFilterType()); + pAsset->setAddressMode(getAddressMode()); + pAsset->setMaxAnisotropy(getMaxAnisotropy()); + pAsset->setMipLODBias(getUseMipLODBias(), getMipLODBias()); } void ImageAsset::setImageFile(StringTableEntry pImageFile) @@ -537,6 +570,36 @@ void ImageAsset::setTextureHDR(const bool pIsHDR) refreshAsset(); } +void ImageAsset::setupSamplerState(GFXSamplerStateDesc* ssd) const +{ + // FIRST (== GFXTextureFilterNone) and COUNT are both treated as "not + // explicitly set" -- leave the sampler desc alone and let the calling class + // fill out. + if (mFilterType != GFXTextureFilter_FIRST && mFilterType != GFXTextureFilter_COUNT) + { + ssd->minFilter = mFilterType; + ssd->magFilter = mFilterType; + ssd->mipFilter = mFilterType; + + if (mFilterType != GFXTextureFilterAnisotropic) + ssd->maxAnisotropy = 1; + } + + // Same "unset" convention for address mode: FIRST (== GFXAddressWrap) + if (mAddressMode != GFXAddress_FIRST && mAddressMode != GFXAddress_COUNT) + { + ssd->addressModeU = mAddressMode; + ssd->addressModeV = mAddressMode; + } + + // 0 is not a valid anisotropy level, so it doubles as "unset". + if (mMaxAnisotropy > 0) + ssd->maxAnisotropy = mMaxAnisotropy; + + if (mUseMipLODBias) + ssd->mipLODBias = mMipLODBias; +} + U32 ImageAsset::load() { if (mLoadedState == Ok) @@ -595,14 +658,14 @@ GFXTexHandle ImageAsset::getTexture(GFXTextureProfile* requestedProfile) if (mLoadedState == Ok) { - //If we don't have an existing map case to the requested format, we'll just create it and insert it in - GFXTexHandle newTex; - newTex.set(mImageFile, requestedProfile, avar("%s %s() - mTextureObject (line %d)", mImageFile, __FUNCTION__, __LINE__)); - if (newTex) - { - mResourceMap.insert(requestedProfile, newTex); - return newTex; - } + //If we don't have an existing map case to the requested format, we'll just create it and insert it in + GFXTexHandle newTex; + newTex.set(mImageFile, requestedProfile, avar("%s %s() - mTextureObject (line %d)", mImageFile, __FUNCTION__, __LINE__)); + if (newTex) + { + mResourceMap.insert(requestedProfile, newTex); + return newTex; + } } if (smNoImageAssetFallbackAssetPtr.notNull() && smNoImageAssetFallbackAssetPtr != this) @@ -792,7 +855,7 @@ void ImageAsset::populateImage(void) } // we only support 2d textures..... for now ;) - mImageDepth = 1; + mImageDepth = 1; } } @@ -1004,9 +1067,28 @@ GuiControl* GuiInspectorTypeImageAssetPtr::constructEditControl() mEditButton->registerObject(); addObject(mEditButton); + // Create inline filter type popup - lets you see/change the resolved + // ImageAsset's own filtering setting + mFilterTypePopup = new GuiPopUpMenuCtrl(); + mFilterTypePopup->registerObject(); + + if (toolDefaultProfile) + mFilterTypePopup->setControlProfile(toolDefaultProfile); + + GuiControlProfile* toolPopupProfile = NULL; + if (Sim::findObject("ToolsGuiPopUpMenuProfile", toolPopupProfile)) + mFilterTypePopup->setControlProfile(toolPopupProfile); + + dSprintf(szBuffer, sizeof(szBuffer), "%d.onFilterTypeSelected();", getId()); + mFilterTypePopup->setField("Command", szBuffer); + + addObject(mFilterTypePopup); + + updateFilterTypePopup(); + // mUseHeightOverride = true; - mHeightOverride = 72; + mHeightOverride = 96; return retCtrl; } @@ -1031,6 +1113,16 @@ bool GuiInspectorTypeImageAssetPtr::updateRects() mEditButton->resize(Point2I(mEdit->getPosition().x + mEdit->getExtent().x, mEdit->getPosition().y), Point2I(rowSize, rowSize)); + // Filter type popup gets its own row directly under the filename field, + // lined up on the same x-offset and spanning the same width mEdit + + // mEditButton together occupy. + if (mFilterTypePopup) + { + Point2I filterPos(editPos, mEdit->getPosition().y + rowSize + 4); + Point2I filterExtent(fieldExtent.x - editPos - 5, rowSize); + mFilterTypePopup->resize(filterPos, filterExtent); + } + mBrowseButton->setHidden(true); return true; @@ -1114,6 +1206,7 @@ void GuiInspectorTypeImageAssetPtr::updateValue() Parent::updateValue(); updatePreviewImage(); + updateFilterTypePopup(); } void GuiInspectorTypeImageAssetPtr::updatePreviewImage() @@ -1178,6 +1271,69 @@ void GuiInspectorTypeImageAssetPtr::setPreviewImage(StringTableEntry assetId) mPreviewImage->_setBitmap(StringTable->insert("ToolsModule:genericAssetIcon_image")); } +AssetPtr GuiInspectorTypeImageAssetPtr::resolveImageAsset() +{ + const char* assetId = getData(); + if (!assetId || !assetId[0] || ImageAsset::isNamedTarget(StringTable->insert(assetId))) + return NULL; // empty field, or a named render target ($backBuffer etc) - no per-asset filter setting + + AssetPtr imageAsset; + U32 assetState = ImageAsset::getAssetById(assetId, &imageAsset); + if (imageAsset.isNull() || assetState == ImageAsset::Failed) + return NULL; + + return imageAsset; +} + +void GuiInspectorTypeImageAssetPtr::updateFilterTypePopup() +{ + if (!mFilterTypePopup) + return; + + mFilterTypePopup->clear(); + mFilterTypePopup->addEntry("Default", GFXTextureFilter_COUNT); + mFilterTypePopup->addEntry("Point", GFXTextureFilterPoint); + mFilterTypePopup->addEntry("Linear", GFXTextureFilterLinear); + mFilterTypePopup->addEntry("Anisotropic", GFXTextureFilterAnisotropic); + + ImageAsset* imageAsset = resolveImageAsset(); + + // No resolvable asset (empty field, named target, bad ID) - hide it + // rather than show a control that can't do anything meaningful. + mFilterTypePopup->setVisible(imageAsset != NULL); + if (!imageAsset) + return; + + GFXTextureFilterType currentType = imageAsset->getFilterType(); + + // FIRST and None share the value 0 -- both mean "not explicitly set", + // so show the same "Default" entry for either. + if (currentType == GFXTextureFilter_FIRST) + currentType = GFXTextureFilter_COUNT; + + mFilterTypePopup->setSelected(currentType, false); // false: don't re-fire onFilterTypeSelected +} + +void GuiInspectorTypeImageAssetPtr::onFilterTypeSelected() +{ + ImageAsset* imageAsset = resolveImageAsset(); + if (!imageAsset || !mFilterTypePopup) + return; + + imageAsset->setFilterType((GFXTextureFilterType)mFilterTypePopup->getSelected()); + + // Updates the live, in-memory instance immediately (so anything + // currently rendering with it picks it up on the next state block + // rebuild) AND writes it back to disk so the change survives a reload. + if (!imageAsset->saveAsset()) + Con::errorf("GuiInspectorTypeImageAssetPtr::onFilterTypeSelected() - failed to save asset '%s'", imageAsset->getAssetId()); +} + +DefineEngineMethod(GuiInspectorTypeImageAssetPtr, onFilterTypeSelected, void, (), , "@internal - fired when the inline filter type popup's selection changes.") +{ + object->onFilterTypeSelected(); +} + void GuiInspectorTypeImageAssetPtr::setCaption(StringTableEntry caption) { mCaption = caption; diff --git a/Engine/source/T3D/assets/ImageAsset.h b/Engine/source/T3D/assets/ImageAsset.h index 500040dd41..5430f112eb 100644 --- a/Engine/source/T3D/assets/ImageAsset.h +++ b/Engine/source/T3D/assets/ImageAsset.h @@ -49,6 +49,9 @@ #ifndef _GFXDEVICE_H_ #include "gfx/gfxDevice.h" #endif +#ifndef _GFXSTATEBLOCK_H_ +#include "gfx/gfxStateBlock.h" +#endif #ifndef _MATTEXTURETARGET_H_ #include "materials/matTextureTarget.h" #endif @@ -139,6 +142,22 @@ class ImageAsset : public AssetBase StringTableEntry mImageFile; bool mUseMips; bool mIsHDRImage; + + /// Explicit filtering override for this image. Defaults to + /// GFXTextureFilter_COUNT (out of range / unset). + GFXTextureFilterType mFilterType; + + /// Explicit U/V wrap mode override for this image. Same "unset" sentinel + /// convention as mFilterType: GFXAddress_FIRST (== GFXAddressWrap) + GFXTextureAddressMode mAddressMode; + + /// Explicit anisotropy level override. + U32 mMaxAnisotropy; + + /// Whether mMipLODBias below should override the material's default of + /// zero. + bool mUseMipLODBias; + F32 mMipLODBias; ImageTypes mImageType; ImageTextureMap mResourceMap; bool mIsNamedTarget; @@ -179,6 +198,22 @@ class ImageAsset : public AssetBase void setTextureHDR(const bool pIsHDR); inline bool getTextureHDR(void) const { return mIsHDRImage; }; + inline void setFilterType(const GFXTextureFilterType pFilterType) { mFilterType = pFilterType; }; + inline GFXTextureFilterType getFilterType(void) const { return mFilterType; }; + + inline void setAddressMode(const GFXTextureAddressMode pAddressMode) { mAddressMode = pAddressMode; }; + inline GFXTextureAddressMode getAddressMode(void) const { return mAddressMode; }; + + inline void setMaxAnisotropy(const U32 pMaxAnisotropy) { mMaxAnisotropy = pMaxAnisotropy; }; + inline U32 getMaxAnisotropy(void) const { return mMaxAnisotropy; }; + + inline void setMipLODBias(const bool pUseBias, const F32 pBias) { mUseMipLODBias = pUseBias; mMipLODBias = pBias; }; + inline bool getUseMipLODBias(void) const { return mUseMipLODBias; }; + inline F32 getMipLODBias(void) const { return mMipLODBias; }; + + /// Populates the sampler state description based on this asset's own settings + void setupSamplerState(GFXSamplerStateDesc* ssd) const; + GFXTexHandle getTexture(GFXTextureProfile* requestedProfile); static StringTableEntry getImageTypeNameFromType(ImageTypes type); diff --git a/Engine/source/T3D/assets/ImageAssetInspectors.h b/Engine/source/T3D/assets/ImageAssetInspectors.h index 3820ebfa0a..6f7b31ece6 100644 --- a/Engine/source/T3D/assets/ImageAssetInspectors.h +++ b/Engine/source/T3D/assets/ImageAssetInspectors.h @@ -6,6 +6,7 @@ #include "gui/editor/guiInspectorTypes.h" #endif #include +#include #ifdef TORQUE_TOOLS class GuiInspectorTypeImageAssetPtr : public GuiInspectorTypeFileName @@ -18,6 +19,13 @@ class GuiInspectorTypeImageAssetPtr : public GuiInspectorTypeFileName GuiBitmapCtrl* mPreviewImage = NULL; GuiBitmapButtonCtrl* mEditButton = NULL; + /// Lets the user see and change the resolved ImageAsset's own filtering + /// setting (GFXTextureFilterType) right from this field, without having + /// to dig into the asset editor. The ImageAsset is the ground truth here + /// -- this writes directly onto the resolved asset instance, not onto + /// whatever object/field this inspector row is otherwise editing. + GuiPopUpMenuCtrl* mFilterTypePopup = NULL; + bool mIsDeleteButtonVisible; DECLARE_CONOBJECT(GuiInspectorTypeImageAssetPtr); @@ -32,6 +40,19 @@ class GuiInspectorTypeImageAssetPtr : public GuiInspectorTypeFileName void updatePreviewImage(); void setPreviewImage(StringTableEntry assetId); + /// Resolves the ImageAsset currently referenced by this field's value + /// (the asset ID string) to a live pointer. Returns NULL if the field + /// is empty, points at a named target, or the asset can't be found. + AssetPtr resolveImageAsset(); + + /// Rebuilds the filter type popup's entries and syncs its current + /// selection from the resolved ImageAsset's getFilterType(). + void updateFilterTypePopup(); + + /// Fired when the popup's selection changes; writes the chosen filter + /// type directly onto the resolved ImageAsset. + void onFilterTypeSelected(); + /// Sets this control's caption text, usually set within setInspectorField, /// this is exposed in case someone wants to override the normal caption. void setCaption(StringTableEntry caption) override; diff --git a/Engine/source/gui/editor/guiInspector.cpp b/Engine/source/gui/editor/guiInspector.cpp index 5596523d4e..653f56336e 100644 --- a/Engine/source/gui/editor/guiInspector.cpp +++ b/Engine/source/gui/editor/guiInspector.cpp @@ -797,6 +797,8 @@ void GuiInspector::updateVisibility() for (GuiInspectorGroup* group : mGroups) { + if (!group) return; + const AbstractClassRep::Field* g = target->findField(group->getGroupName().c_str()); // if group has its own visibility function let it control it. diff --git a/Engine/source/materials/materialDefinition.h b/Engine/source/materials/materialDefinition.h index b932607836..2646347b0f 100644 --- a/Engine/source/materials/materialDefinition.h +++ b/Engine/source/materials/materialDefinition.h @@ -226,60 +226,71 @@ class Material : public BaseMaterialDefinition inline StringTableEntry getNormalMapAssetId(const U32& index) const { return mNormalMapAssetRef[index].assetId; } void setNormalMap(StringTableEntry assetId, const U32& index) { mNormalMapAssetRef[index] = assetId; } GFXTexHandle getNormalMap(const U32& index) { return mNormalMapAssetRef[index].notNull() ? mNormalMapAssetRef[index].assetPtr->getTexture(&GFXNormalMapProfile) : GFXTexHandle(); } + AssetPtr getNormalMapAsset(const U32& index) { return mNormalMapAssetRef[index].assetPtr; } AssetRef mDetailNormalMapAssetRef[MAX_STAGES]; inline StringTableEntry getDetailNormalMapAssetId(const U32& index) const { return mDetailNormalMapAssetRef[index].assetId; } void setDetailNormalMap(StringTableEntry assetId, const U32& index) { mDetailNormalMapAssetRef[index] = assetId; } GFXTexHandle getDetailNormalMap(const U32& index) { return mDetailNormalMapAssetRef[index].notNull() ? mDetailNormalMapAssetRef[index].assetPtr->getTexture(&GFXNormalMapProfile) : GFXTexHandle(); } + AssetPtr getDetailNormalMapAsset(const U32& index) { return mDetailNormalMapAssetRef[index].assetPtr; } AssetRef mOverlayMapAssetRef[MAX_STAGES]; inline StringTableEntry getOverlayMapAssetId(const U32& index) const { return mOverlayMapAssetRef[index].assetId; } void setOverlayMap(StringTableEntry assetId, const U32& index) { mOverlayMapAssetRef[index] = assetId; } GFXTexHandle getOverlayMap(const U32& index) { return mOverlayMapAssetRef[index].notNull() ? mOverlayMapAssetRef[index].assetPtr->getTexture(&GFXStaticTextureProfile) : GFXTexHandle(); } + AssetPtr getOverlayMapAsset(const U32& index) { return mOverlayMapAssetRef[index].assetPtr; } AssetRef mLightMapAssetRef[MAX_STAGES]; inline StringTableEntry getLightMapAssetId(const U32& index) const { return mLightMapAssetRef[index].assetId; } void setLightMap(StringTableEntry assetId, const U32& index) { mLightMapAssetRef[index] = assetId; } GFXTexHandle getLightMap(const U32& index) { return mLightMapAssetRef[index].notNull() ? mLightMapAssetRef[index].assetPtr->getTexture(&GFXStaticTextureProfile) : GFXTexHandle(); } + AssetPtr getLightMapAsset(const U32& index) { return mLightMapAssetRef[index].assetPtr; } AssetRef mToneMapAssetRef[MAX_STAGES]; inline StringTableEntry getToneMapAssetId(const U32& index) const { return mToneMapAssetRef[index].assetId; } void setToneMap(StringTableEntry assetId, const U32& index) { mToneMapAssetRef[index] = assetId; } GFXTexHandle getToneMap(const U32& index) { return mToneMapAssetRef[index].notNull() ? mToneMapAssetRef[index].assetPtr->getTexture(&GFXStaticTextureProfile) : GFXTexHandle(); } + AssetPtr getToneMapAsset(const U32& index) { return mToneMapAssetRef[index].assetPtr; } AssetRef mDetailMapAssetRef[MAX_STAGES]; inline StringTableEntry getDetailMapAssetId(const U32& index) const { return mDetailMapAssetRef[index].assetId; } void setDetailMap(StringTableEntry assetId, const U32& index) { mDetailMapAssetRef[index] = assetId; } GFXTexHandle getDetailMap(const U32& index) { return mDetailMapAssetRef[index].notNull() ? mDetailMapAssetRef[index].assetPtr->getTexture(&GFXStaticTextureProfile) : GFXTexHandle(); } + AssetPtr getDetailMapAsset(const U32& index) { return mDetailMapAssetRef[index].assetPtr; } AssetRef mORMConfigMapAssetRef[MAX_STAGES]; inline StringTableEntry getORMConfigMapAssetId(const U32& index) const { return mORMConfigMapAssetRef[index].assetId; } void setORMConfigMap(StringTableEntry assetId, const U32& index) { mORMConfigMapAssetRef[index] = assetId; } GFXTexHandle getORMConfigMap(const U32& index) { return getORMConfigMap(&GFXStaticTextureProfile, index); } GFXTexHandle getORMConfigMap(GFXTextureProfile* requestedProfile, const U32& index) { return mORMConfigMapAssetRef[index].notNull() ? mORMConfigMapAssetRef[index].assetPtr->getTexture(requestedProfile) : GFXTexHandle(); } + AssetPtr getORMConfigMapAsset(const U32& index) { return mORMConfigMapAssetRef[index].assetPtr; } AssetRef mAOMapAssetRef[MAX_STAGES]; inline StringTableEntry getAOMapAssetId(const U32& index) const { return mAOMapAssetRef[index].assetId; } void setAOMap(StringTableEntry assetId, const U32& index) { mAOMapAssetRef[index] = assetId; } GFXTexHandle getAOMap(const U32& index) { return mAOMapAssetRef[index].notNull() ? mAOMapAssetRef[index].assetPtr->getTexture(&GFXStaticTextureProfile) : GFXTexHandle(); } + AssetPtr getAOMapAsset(const U32& index) { return mAOMapAssetRef[index].assetPtr; } StringTableEntry getAOMapFile(const U32& index) { return mAOMapAssetRef[index].notNull() ? mAOMapAssetRef[index].assetPtr->getImageFile() : StringTable->EmptyString(); } AssetRef mRoughMapAssetRef[MAX_STAGES]; inline StringTableEntry getRoughMapAssetId(const U32& index) const { return mRoughMapAssetRef[index].assetId; } void setRoughMap(StringTableEntry assetId, const U32& index) { mRoughMapAssetRef[index] = assetId; } GFXTexHandle getRoughMap(const U32& index) { return mRoughMapAssetRef[index].notNull() ? mRoughMapAssetRef[index].assetPtr->getTexture(&GFXStaticTextureProfile) : GFXTexHandle(); } + AssetPtr getRoughMapAsset(const U32& index) { return mRoughMapAssetRef[index].assetPtr; } StringTableEntry getRoughMapFile(const U32& index) { return mRoughMapAssetRef[index].notNull() ? mRoughMapAssetRef[index].assetPtr->getImageFile() : StringTable->EmptyString(); } AssetRef mMetalMapAssetRef[MAX_STAGES]; inline StringTableEntry getMetalMapAssetId(const U32& index) const { return mMetalMapAssetRef[index].assetId; } void setMetalMap(StringTableEntry assetId, const U32& index) { mMetalMapAssetRef[index] = assetId; } GFXTexHandle getMetalMap(const U32& index) { return mMetalMapAssetRef[index].notNull() ? mMetalMapAssetRef[index].assetPtr->getTexture(&GFXStaticTextureProfile) : GFXTexHandle(); } + AssetPtr getMetalMapAsset(const U32& index) { return mMetalMapAssetRef[index].assetPtr; } StringTableEntry getMetalMapFile(const U32& index) { return mMetalMapAssetRef[index].notNull() ? mMetalMapAssetRef[index].assetPtr->getImageFile() : StringTable->EmptyString(); } AssetRef mGlowMapAssetRef[MAX_STAGES]; inline StringTableEntry getGlowMapAssetId(const U32& index) const { return mGlowMapAssetRef[index].assetId; } void setGlowMap(StringTableEntry assetId, const U32& index) { mGlowMapAssetRef[index] = assetId; } GFXTexHandle getGlowMap(const U32& index) { return mGlowMapAssetRef[index].notNull() ? mGlowMapAssetRef[index].assetPtr->getTexture(&GFXStaticTextureProfile) : GFXTexHandle(); } + AssetPtr getGlowMapAsset(const U32& index) { return mGlowMapAssetRef[index].assetPtr; } bool mDiffuseMapSRGB[MAX_STAGES]; // SRGB diffuse bool mIsSRGb[MAX_STAGES]; // SRGB ORM @@ -421,7 +432,7 @@ class Material : public BaseMaterialDefinition ///@} String mMapTo; // map Material to this texture name - + /// /// Material interface /// diff --git a/Engine/source/materials/processedMaterial.cpp b/Engine/source/materials/processedMaterial.cpp index 6dc807df6b..e6ffbb477b 100644 --- a/Engine/source/materials/processedMaterial.cpp +++ b/Engine/source/materials/processedMaterial.cpp @@ -43,10 +43,10 @@ void RenderPassData::reset() for( U32 i = 0; i < Material::MAX_TEX_PER_PASS; ++ i ) { destructInPlace( &mTexSlot[ i ] ); + constructInPlace( &mTexSlot[i] ); mSamplerNames[ i ].clear(); } - dMemset( &mTexSlot, 0, sizeof(mTexSlot) ); dMemset( &mTexType, 0, sizeof(mTexType) ); mCubeMap = NULL; @@ -275,6 +275,10 @@ void ProcessedMaterial::_initPassStateBlock( RenderPassData *rpd, GFXStateBlockD result.samplers[i].minFilter = GFXTextureFilterLinear; result.samplers[i].magFilter = GFXTextureFilterLinear; } + + if (rpd->mTexSlot[i].texImageAsset) + rpd->mTexSlot[i].texImageAsset->setupSamplerState(&result.samplers[i]); + break; } diff --git a/Engine/source/materials/processedMaterial.h b/Engine/source/materials/processedMaterial.h index e93c3558d7..50f80d27ab 100644 --- a/Engine/source/materials/processedMaterial.h +++ b/Engine/source/materials/processedMaterial.h @@ -69,6 +69,9 @@ struct RenderPassData /// @see mTexType NamedTexTargetRef texTarget; + /// The ImageAsset bound to this slot, if any. + AssetPtr texImageAsset; + } mTexSlot[Material::MAX_TEX_PER_PASS]; U32 mTexType[Material::MAX_TEX_PER_PASS]; diff --git a/Engine/source/materials/processedShaderMaterial.cpp b/Engine/source/materials/processedShaderMaterial.cpp index b66fa0d5c1..b118c4c79a 100644 --- a/Engine/source/materials/processedShaderMaterial.cpp +++ b/Engine/source/materials/processedShaderMaterial.cpp @@ -546,7 +546,40 @@ void ProcessedShaderMaterial::_determineFeatures( U32 stageNum, fd.features.filter( features ); } -bool ProcessedShaderMaterial::_createPasses( MaterialFeatureData &stageFeatures, U32 stageNum, const FeatureSet &features ) +AssetPtr ProcessedShaderMaterial::_getStageImageAsset(const FeatureType& type, U32 stageNum) +{ + if (!mMaterial) + return NULL; + + if (type == MFT_DiffuseMap) + return mMaterial->getDiffuseMapAsset(stageNum); + if (type == MFT_NormalMap) + return mMaterial->getNormalMapAsset(stageNum); + if (type == MFT_DetailNormalMap) + return mMaterial->getDetailNormalMapAsset(stageNum); + if (type == MFT_OverlayMap) + return mMaterial->getOverlayMapAsset(stageNum); + if (type == MFT_LightMap) + return mMaterial->getLightMapAsset(stageNum); + if (type == MFT_ToneMap) + return mMaterial->getToneMapAsset(stageNum); + if (type == MFT_DetailMap) + return mMaterial->getDetailMapAsset(stageNum); + if (type == MFT_OrmMap) + return mMaterial->getORMConfigMapAsset(stageNum); + /*if (type == MFT_AOMap) + return mMaterial->getAOMapAsset(stageNum); + if (type == MFT_RoughMap) + return mMaterial->getRoughMapAsset(stageNum); + if (type == MFT_MetalMap) + return mMaterial->getMetalMapAsset(stageNum);*/ + if (type == MFT_GlowMap) + return mMaterial->getGlowMapAsset(stageNum); + + return NULL; +} + +bool ProcessedShaderMaterial::_createPasses(MaterialFeatureData& stageFeatures, U32 stageNum, const FeatureSet& features) { // Creates passes for the given stage ShaderRenderPassData passData; @@ -574,11 +607,19 @@ bool ProcessedShaderMaterial::_createPasses( MaterialFeatureData &stageFeatures, passData.mNumTexReg += numTexReg; passData.mFeatureData.features.addFeature( *info.type ); -#if defined(TORQUE_DEBUG) && defined( TORQUE_OPENGL) U32 oldTexNumber = texIndex; -#endif - info.feature->setTexData( mStages[stageNum], stageFeatures, passData, texIndex ); + info.feature->setTexData(mStages[stageNum], stageFeatures, passData, texIndex); + + if (texIndex != oldTexNumber) + { + AssetPtr stageImageAsset = _getStageImageAsset(*info.type, stageNum); + if (stageImageAsset.notNull()) + { + for (U32 texNum = oldTexNumber; texNum < texIndex; texNum++) + passData.mTexSlot[texNum].texImageAsset = stageImageAsset; + } + } #if defined(TORQUE_DEBUG) && defined( TORQUE_OPENGL) if(oldTexNumber != texIndex) @@ -618,7 +659,7 @@ bool ProcessedShaderMaterial::_createPasses( MaterialFeatureData &stageFeatures, } return true; -} +} void ProcessedShaderMaterial::_initMaterialParameters() { diff --git a/Engine/source/materials/processedShaderMaterial.h b/Engine/source/materials/processedShaderMaterial.h index 438c879e52..967d6bec28 100644 --- a/Engine/source/materials/processedShaderMaterial.h +++ b/Engine/source/materials/processedShaderMaterial.h @@ -58,16 +58,16 @@ class ShaderConstHandles GFXShaderConstHandle* mAccuCoverageSC; GFXShaderConstHandle* mAccuSpecularSC; GFXShaderConstHandle* mFogDataSC; - GFXShaderConstHandle* mFogColorSC; + GFXShaderConstHandle* mFogColorSC; GFXShaderConstHandle* mDetailScaleSC; GFXShaderConstHandle* mVisiblitySC; GFXShaderConstHandle* mColorMultiplySC; GFXShaderConstHandle* mAlphaTestValueSC; GFXShaderConstHandle* mModelViewProjSC; - GFXShaderConstHandle* mWorldViewOnlySC; + GFXShaderConstHandle* mWorldViewOnlySC; GFXShaderConstHandle* mWorldToCameraSC; GFXShaderConstHandle* mCameraToWorldSC; - GFXShaderConstHandle* mWorldToObjSC; + GFXShaderConstHandle* mWorldToObjSC; GFXShaderConstHandle* mViewToObjSC; GFXShaderConstHandle* mInvCameraTransSC; GFXShaderConstHandle* mCameraToScreenSC; @@ -89,13 +89,13 @@ class ShaderConstHandles GFXShaderConstHandle* mBumpAtlasParamsSC; GFXShaderConstHandle* mDiffuseAtlasTileSC; GFXShaderConstHandle* mBumpAtlasTileSC; - GFXShaderConstHandle *mRTSizeSC; - GFXShaderConstHandle *mOneOverRTSizeSC; + GFXShaderConstHandle* mRTSizeSC; + GFXShaderConstHandle* mOneOverRTSizeSC; GFXShaderConstHandle* mDetailBumpStrength; GFXShaderConstHandle* mViewProjSC; - GFXShaderConstHandle *mImposterUVs; - GFXShaderConstHandle *mImposterLimits; + GFXShaderConstHandle* mImposterUVs; + GFXShaderConstHandle* mImposterLimits; // Deferred Shading : Material Info Flags GFXShaderConstHandle* mMatInfoFlagsSC; @@ -108,13 +108,13 @@ class ShaderConstHandles GFXShaderConstHandle* mIsCapturingSC; struct customHandleData { - StringTableEntry handleName; - GFXShaderConstHandle* handle; + StringTableEntry handleName; + GFXShaderConstHandle* handle; }; Vector mCustomHandles; - void init( GFXShader* shader, CustomMaterial* mat = NULL); - + void init(GFXShader* shader, CustomMaterial* mat = NULL); + }; class ShaderRenderPassData : public RenderPassData @@ -139,7 +139,7 @@ class ProcessedShaderMaterial : public ProcessedMaterial public: ProcessedShaderMaterial(); - ProcessedShaderMaterial(Material &mat); + ProcessedShaderMaterial(Material& mat); ~ProcessedShaderMaterial(); // ProcessedMaterial @@ -152,12 +152,12 @@ class ProcessedShaderMaterial : public ProcessedMaterial void setNodeTransforms(const MatrixF *address, const U32 numTransforms, const U32 pass) override; void setCustomShaderData(Vector &shaderData, const U32 pass) override; void setSceneInfo(SceneRenderState *, const SceneData& sgData, U32 pass) override; - void setBuffers(GFXVertexBufferHandleBase* vertBuffer, GFXPrimitiveBufferHandle* primBuffer) override; + void setBuffers(GFXVertexBufferHandleBase* vertBuffer, GFXPrimitiveBufferHandle* primBuffer) override; bool stepInstance() override; void dumpMaterialInfo() override; void getMaterialInfo(GuiTreeViewCtrl* tree, U32 item) override; - MaterialParameters* allocMaterialParameters() override; - MaterialParameters* getDefaultMaterialParameters() override { return mDefaultParameters; } + MaterialParameters* allocMaterialParameters() override; + MaterialParameters* getDefaultMaterialParameters() override { return mDefaultParameters; } MaterialParameterHandle* getMaterialParameterHandle(const String& name) override; U32 getNumStages() override; @@ -183,27 +183,27 @@ class ProcessedShaderMaterial : public ProcessedMaterial ~InstancingState() { - delete [] mBuffer; + delete[] mBuffer; } - void setFormat( const GFXVertexFormat *instFormat, const GFXVertexFormat *vertexFormat ) + void setFormat(const GFXVertexFormat* instFormat, const GFXVertexFormat* vertexFormat) { mInstFormat = instFormat; - mDeclFormat.copy( *vertexFormat ); - mDeclFormat.append( *mInstFormat, 1 ); + mDeclFormat.copy(*vertexFormat); + mDeclFormat.append(*mInstFormat, 1); // Let the declaration know we have instancing. mDeclFormat.enableInstancing(); mDeclFormat.getDecl(); - delete [] mBuffer; - mBuffer = new U8[ mInstFormat->getSizeInBytes() * COUNT ]; + delete[] mBuffer; + mBuffer = new U8[mInstFormat->getSizeInBytes() * COUNT]; mCount = -1; } - bool step( U8 **outPtr ) + bool step(U8** outPtr) { // Are we starting a new draw call? - if ( mCount < 0 ) + if (mCount < 0) { *outPtr = mBuffer; mCount = 0; @@ -240,7 +240,7 @@ class ProcessedShaderMaterial : public ProcessedMaterial /// The instancing state if this material /// supports instancing. InstancingState *mInstancingState; - + /// @name Internal functions /// /// @{ @@ -261,7 +261,11 @@ class ProcessedShaderMaterial : public ProcessedMaterial const FeatureSet &features); /// Creates passes for the given stage - virtual bool _createPasses( MaterialFeatureData &fd, U32 stageNum, const FeatureSet &features ); + virtual bool _createPasses(MaterialFeatureData& fd, U32 stageNum, const FeatureSet& features); + + /// Resolves the ImageAsset (if any) that a given texture feature type + /// draws from for the given stage + virtual AssetPtr _getStageImageAsset(const FeatureType& type, U32 stageNum); /// Fills in the MaterialFeatureData for the given stage virtual void _determineFeatures( U32 stageNum,