3047 lines
129 KiB
C++
3047 lines
129 KiB
C++
/*
|
|
* Copyright (c) <2017> Side Effects Software Inc.
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
* in the Software without restriction, including without limitation the rights
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in all
|
|
* copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
* SOFTWARE.
|
|
*
|
|
*/
|
|
|
|
#include "HoudiniEngineMaterialUtils.h"
|
|
|
|
#include "HoudiniApi.h"
|
|
#include "HoudiniEngineRuntimePrivatePCH.h"
|
|
#include "HoudiniEngine.h"
|
|
#include "HoudiniEngineUtils.h"
|
|
#include "HoudiniEngineBakeUtils.h"
|
|
#include "HoudiniEngineString.h"
|
|
|
|
#include "Materials/Material.h"
|
|
#include "Materials/MaterialInstance.h"
|
|
#include "Materials/MaterialInstanceConstant.h"
|
|
#include "PhysicalMaterials/PhysicalMaterial.h"
|
|
#include "UObject/MetaData.h"
|
|
#if WITH_EDITOR
|
|
#include "Materials/Material.h"
|
|
#include "Materials/MaterialInstance.h"
|
|
#include "Materials/MaterialExpressionTextureSample.h"
|
|
#include "Materials/MaterialExpressionTextureCoordinate.h"
|
|
#include "Materials/MaterialExpressionConstant4Vector.h"
|
|
#include "Materials/MaterialExpressionConstant.h"
|
|
#include "Materials/MaterialExpressionMultiply.h"
|
|
#include "Materials/MaterialExpressionVertexColor.h"
|
|
#include "Materials/MaterialExpressionTextureSampleParameter2D.h"
|
|
#include "Materials/MaterialExpressionVectorParameter.h"
|
|
#include "Materials/MaterialExpressionScalarParameter.h"
|
|
#include "Factories/MaterialFactoryNew.h"
|
|
#include "Factories/MaterialInstanceConstantFactoryNew.h"
|
|
#endif
|
|
|
|
const int32
|
|
FHoudiniEngineMaterialUtils::MaterialExpressionNodeX = -400;
|
|
|
|
const int32
|
|
FHoudiniEngineMaterialUtils::MaterialExpressionNodeY = -150;
|
|
|
|
const int32
|
|
FHoudiniEngineMaterialUtils::MaterialExpressionNodeStepX = 220;
|
|
|
|
const int32
|
|
FHoudiniEngineMaterialUtils::MaterialExpressionNodeStepY = 220;
|
|
|
|
void
|
|
FHoudiniEngineMaterialUtils::HapiCreateMaterials(
|
|
HAPI_NodeId AssetId,
|
|
FHoudiniCookParams& HoudiniCookParams,
|
|
const HAPI_AssetInfo & AssetInfo,
|
|
const TSet< HAPI_NodeId > & UniqueMaterialIds,
|
|
const TSet< HAPI_NodeId > & UniqueInstancerMaterialIds,
|
|
TMap< FString, UMaterialInterface * > & Materials,
|
|
const bool& bForceRecookAll )
|
|
{
|
|
#if WITH_EDITOR
|
|
|
|
// Empty returned materials.
|
|
Materials.Empty();
|
|
|
|
if ( UniqueMaterialIds.Num() == 0 )
|
|
return;
|
|
|
|
// Update context for generated materials (will trigger when object goes out of scope).
|
|
FMaterialUpdateContext MaterialUpdateContext;
|
|
|
|
// Default Houdini material.
|
|
UMaterial * DefaultMaterial = FHoudiniEngine::Get().GetHoudiniDefaultMaterial().Get();
|
|
Materials.Add( HAPI_UNREAL_DEFAULT_MATERIAL_NAME, DefaultMaterial );
|
|
|
|
// Factory to create materials.
|
|
UMaterialFactoryNew * MaterialFactory = NewObject< UMaterialFactoryNew >();
|
|
MaterialFactory->AddToRoot();
|
|
|
|
for ( TSet< HAPI_NodeId >::TConstIterator IterMaterialId( UniqueMaterialIds ); IterMaterialId; ++IterMaterialId )
|
|
{
|
|
HAPI_NodeId MaterialId = *IterMaterialId;
|
|
|
|
// Get material information.
|
|
HAPI_MaterialInfo MaterialInfo;
|
|
FHoudiniApi::MaterialInfo_Init(&MaterialInfo);
|
|
if ( FHoudiniApi::GetMaterialInfo(
|
|
FHoudiniEngine::Get().GetSession(),
|
|
MaterialId, &MaterialInfo ) != HAPI_RESULT_SUCCESS )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// Get node information.
|
|
HAPI_NodeInfo NodeInfo;
|
|
FHoudiniApi::NodeInfo_Init(&NodeInfo);
|
|
if ( FHoudiniApi::GetNodeInfo(
|
|
FHoudiniEngine::Get().GetSession(), MaterialInfo.nodeId, &NodeInfo ) != HAPI_RESULT_SUCCESS )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if ( MaterialInfo.exists )
|
|
{
|
|
FString MaterialShopName = TEXT( "" );
|
|
if ( !FHoudiniEngineMaterialUtils::GetUniqueMaterialShopName( AssetId, MaterialId, MaterialShopName ) )
|
|
continue;
|
|
|
|
bool bCreatedNewMaterial = false;
|
|
UMaterial * Material = HoudiniCookParams.HoudiniCookManager ? Cast< UMaterial >( HoudiniCookParams.HoudiniCookManager->GetAssignmentMaterial( MaterialShopName ) ) : nullptr;
|
|
if ( Material && !Material->IsPendingKill() )
|
|
{
|
|
// If cached material exists and has not changed, we can reuse it.
|
|
if ( !MaterialInfo.hasChanged && !bForceRecookAll )
|
|
{
|
|
// We found cached material, we can reuse it.
|
|
Materials.Add( MaterialShopName, Material );
|
|
continue;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Material was not found, we need to create it.
|
|
FString MaterialName = TEXT( "" );
|
|
EObjectFlags ObjFlags = ( HoudiniCookParams.MaterialAndTextureBakeMode == EBakeMode::Intermediate ) ? RF_Transactional : RF_Public | RF_Standalone;
|
|
|
|
// Create material package and get material name.
|
|
UPackage * MaterialPackage = FHoudiniEngineBakeUtils::BakeCreateMaterialPackageForComponent(
|
|
HoudiniCookParams, MaterialInfo, MaterialName );
|
|
|
|
// Create new material.
|
|
Material = (UMaterial *) MaterialFactory->FactoryCreateNew(
|
|
UMaterial::StaticClass(), MaterialPackage,
|
|
*MaterialName, ObjFlags, NULL, GWarn );
|
|
|
|
// Add meta information to this package.
|
|
FHoudiniEngineBakeUtils::AddHoudiniMetaInformationToPackage(
|
|
MaterialPackage, Material, HAPI_UNREAL_PACKAGE_META_GENERATED_OBJECT, TEXT( "true" ) );
|
|
FHoudiniEngineBakeUtils::AddHoudiniMetaInformationToPackage(
|
|
MaterialPackage, Material, HAPI_UNREAL_PACKAGE_META_GENERATED_NAME, *MaterialName );
|
|
|
|
bCreatedNewMaterial = true;
|
|
}
|
|
|
|
if ( !Material || Material->IsPendingKill() )
|
|
continue;
|
|
|
|
// If this is an instancer material, enable the instancing flag.
|
|
if ( UniqueInstancerMaterialIds.Contains( MaterialId ) )
|
|
Material->bUsedWithInstancedStaticMeshes = true;
|
|
|
|
// Reset material expressions.
|
|
Material->Expressions.Empty();
|
|
|
|
// Generate various components for this material.
|
|
bool bMaterialComponentCreated = false;
|
|
int32 MaterialNodeY = FHoudiniEngineMaterialUtils::MaterialExpressionNodeY;
|
|
|
|
// By default we mark material as opaque. Some of component creators can change this.
|
|
Material->BlendMode = BLEND_Opaque;
|
|
|
|
// Extract diffuse plane.
|
|
bMaterialComponentCreated |= FHoudiniEngineMaterialUtils::CreateMaterialComponentDiffuse(
|
|
HoudiniCookParams, AssetId, Material, MaterialInfo, NodeInfo, MaterialNodeY );
|
|
|
|
// Extract opacity plane.
|
|
bMaterialComponentCreated |= FHoudiniEngineMaterialUtils::CreateMaterialComponentOpacity(
|
|
HoudiniCookParams, AssetId, Material, MaterialInfo, NodeInfo, MaterialNodeY );
|
|
|
|
// Extract opacity mask plane.
|
|
bMaterialComponentCreated |= FHoudiniEngineMaterialUtils::CreateMaterialComponentOpacityMask(
|
|
HoudiniCookParams, AssetId, Material, MaterialInfo, NodeInfo, MaterialNodeY );
|
|
|
|
// Extract normal plane.
|
|
bMaterialComponentCreated |= FHoudiniEngineMaterialUtils::CreateMaterialComponentNormal(
|
|
HoudiniCookParams, AssetId, Material, MaterialInfo, NodeInfo, MaterialNodeY );
|
|
|
|
// Extract specular plane.
|
|
bMaterialComponentCreated |= FHoudiniEngineMaterialUtils::CreateMaterialComponentSpecular(
|
|
HoudiniCookParams, AssetId, Material, MaterialInfo, NodeInfo, MaterialNodeY );
|
|
|
|
// Extract roughness plane.
|
|
bMaterialComponentCreated |= FHoudiniEngineMaterialUtils::CreateMaterialComponentRoughness(
|
|
HoudiniCookParams, AssetId, Material, MaterialInfo, NodeInfo, MaterialNodeY );
|
|
|
|
// Extract metallic plane.
|
|
bMaterialComponentCreated |= FHoudiniEngineMaterialUtils::CreateMaterialComponentMetallic(
|
|
HoudiniCookParams, AssetId, Material, MaterialInfo, NodeInfo, MaterialNodeY );
|
|
|
|
// Extract emissive plane.
|
|
bMaterialComponentCreated |= FHoudiniEngineMaterialUtils::CreateMaterialComponentEmissive(
|
|
HoudiniCookParams, AssetId, Material, MaterialInfo, NodeInfo, MaterialNodeY );
|
|
|
|
// Set other material properties.
|
|
Material->TwoSided = true;
|
|
Material->SetShadingModel( MSM_DefaultLit );
|
|
|
|
// Schedule this material for update.
|
|
MaterialUpdateContext.AddMaterial( Material );
|
|
|
|
// Cache material.
|
|
Materials.Add( MaterialShopName, Material );
|
|
|
|
// Propagate and trigger material updates.
|
|
if ( bCreatedNewMaterial )
|
|
FAssetRegistryModule::AssetCreated( Material );
|
|
|
|
Material->PreEditChange( nullptr );
|
|
Material->PostEditChange();
|
|
Material->MarkPackageDirty();
|
|
}
|
|
else
|
|
{
|
|
// Material does not exist, we will use default Houdini material in this case.
|
|
}
|
|
}
|
|
|
|
MaterialFactory->RemoveFromRoot();
|
|
|
|
#endif
|
|
}
|
|
|
|
|
|
bool
|
|
FHoudiniEngineMaterialUtils::HapiExtractImage(
|
|
HAPI_ParmId NodeParmId, const HAPI_MaterialInfo & MaterialInfo,
|
|
TArray< char > & ImageBuffer, const char * PlaneType, HAPI_ImageDataFormat ImageDataFormat,
|
|
HAPI_ImagePacking ImagePacking, bool bRenderToImage )
|
|
{
|
|
if ( bRenderToImage )
|
|
{
|
|
if ( FHoudiniApi::RenderTextureToImage(
|
|
FHoudiniEngine::Get().GetSession(),
|
|
MaterialInfo.nodeId, NodeParmId ) != HAPI_RESULT_SUCCESS )
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
HAPI_ImageInfo ImageInfo;
|
|
FHoudiniApi::ImageInfo_Init(&ImageInfo);
|
|
if ( FHoudiniApi::GetImageInfo(
|
|
FHoudiniEngine::Get().GetSession(),
|
|
MaterialInfo.nodeId, &ImageInfo ) != HAPI_RESULT_SUCCESS )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
ImageInfo.dataFormat = ImageDataFormat;
|
|
ImageInfo.interleaved = true;
|
|
ImageInfo.packing = ImagePacking;
|
|
|
|
if ( FHoudiniApi::SetImageInfo(
|
|
FHoudiniEngine::Get().GetSession(),
|
|
MaterialInfo.nodeId, &ImageInfo) != HAPI_RESULT_SUCCESS )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
int32 ImageBufferSize = 0;
|
|
if ( FHoudiniApi::ExtractImageToMemory(
|
|
FHoudiniEngine::Get().GetSession(),
|
|
MaterialInfo.nodeId, HAPI_RAW_FORMAT_NAME,
|
|
PlaneType, &ImageBufferSize ) != HAPI_RESULT_SUCCESS )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if ( !ImageBufferSize )
|
|
return false;
|
|
|
|
ImageBuffer.SetNumUninitialized( ImageBufferSize );
|
|
|
|
if ( FHoudiniApi::GetImageMemoryBuffer(
|
|
FHoudiniEngine::Get().GetSession(),
|
|
MaterialInfo.nodeId, &ImageBuffer[ 0 ],
|
|
ImageBufferSize ) != HAPI_RESULT_SUCCESS )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
FHoudiniEngineMaterialUtils::HapiGetImagePlanes(
|
|
HAPI_ParmId NodeParmId, const HAPI_MaterialInfo & MaterialInfo,
|
|
TArray< FString > & ImagePlanes )
|
|
{
|
|
ImagePlanes.Empty();
|
|
int32 ImagePlaneCount = 0;
|
|
|
|
if ( FHoudiniApi::RenderTextureToImage(
|
|
FHoudiniEngine::Get().GetSession(),
|
|
MaterialInfo.nodeId, NodeParmId ) != HAPI_RESULT_SUCCESS )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if ( FHoudiniApi::GetImagePlaneCount(
|
|
FHoudiniEngine::Get().GetSession(),
|
|
MaterialInfo.nodeId, &ImagePlaneCount ) != HAPI_RESULT_SUCCESS )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if ( !ImagePlaneCount )
|
|
return true;
|
|
|
|
TArray< HAPI_StringHandle > ImagePlaneStringHandles;
|
|
ImagePlaneStringHandles.SetNumZeroed( ImagePlaneCount );
|
|
|
|
if ( FHoudiniApi::GetImagePlanes(
|
|
FHoudiniEngine::Get().GetSession(),
|
|
MaterialInfo.nodeId, &ImagePlaneStringHandles[ 0 ], ImagePlaneCount ) != HAPI_RESULT_SUCCESS )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
for ( int32 IdxPlane = 0, IdxPlaneMax = ImagePlaneStringHandles.Num(); IdxPlane < IdxPlaneMax; ++IdxPlane )
|
|
{
|
|
FString ValueString = TEXT( "" );
|
|
FHoudiniEngineString FHoudiniEngineString( ImagePlaneStringHandles[ IdxPlane ] );
|
|
FHoudiniEngineString.ToFString( ValueString );
|
|
ImagePlanes.Add( ValueString );
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
#if WITH_EDITOR
|
|
|
|
bool
|
|
FHoudiniEngineMaterialUtils::CreateMaterialComponentDiffuse(
|
|
FHoudiniCookParams& HoudiniCookParams, const HAPI_NodeId& AssetId,
|
|
UMaterial * Material, const HAPI_MaterialInfo & MaterialInfo,
|
|
const HAPI_NodeInfo & NodeInfo, int32 & MaterialNodeY )
|
|
{
|
|
if (!Material || Material->IsPendingKill())
|
|
return false;
|
|
|
|
HAPI_Result Result = HAPI_RESULT_SUCCESS;
|
|
|
|
EBakeMode BakeMode = HoudiniCookParams.MaterialAndTextureBakeMode;
|
|
EObjectFlags ObjectFlag = ( BakeMode == EBakeMode::CookToTemp ) ? RF_NoFlags : RF_Standalone;
|
|
|
|
// Names of generating Houdini parameters.
|
|
FString GeneratingParameterNameDiffuseTexture = TEXT( "" );
|
|
FString GeneratingParameterNameUniformColor = TEXT( "" );
|
|
FString GeneratingParameterNameVertexColor = TEXT( HAPI_UNREAL_ATTRIB_COLOR );
|
|
|
|
// Diffuse texture creation parameters.
|
|
FCreateTexture2DParameters CreateTexture2DParameters;
|
|
CreateTexture2DParameters.SourceGuidHash = FGuid();
|
|
CreateTexture2DParameters.bUseAlpha = false;
|
|
CreateTexture2DParameters.CompressionSettings = TC_Default;
|
|
CreateTexture2DParameters.bDeferCompression = true;
|
|
CreateTexture2DParameters.bSRGB = true;
|
|
|
|
// Attempt to look up previously created expressions.
|
|
UMaterialExpression * MaterialExpression = Material->BaseColor.Expression;
|
|
|
|
// Locate sampling expression.
|
|
UMaterialExpressionTextureSampleParameter2D * ExpressionTextureSample =
|
|
Cast< UMaterialExpressionTextureSampleParameter2D >( FHoudiniEngineMaterialUtils::MaterialLocateExpression(
|
|
MaterialExpression, UMaterialExpressionTextureSampleParameter2D::StaticClass() ) );
|
|
|
|
// If texture sampling expression does exist, attempt to look up corresponding texture.
|
|
UTexture2D * TextureDiffuse = nullptr;
|
|
if ( ExpressionTextureSample && !ExpressionTextureSample->IsPendingKill() )
|
|
TextureDiffuse = Cast< UTexture2D >( ExpressionTextureSample->Texture );
|
|
|
|
// Locate uniform color expression.
|
|
UMaterialExpressionVectorParameter * ExpressionConstant4Vector =
|
|
Cast< UMaterialExpressionVectorParameter >(FHoudiniEngineMaterialUtils::MaterialLocateExpression(
|
|
MaterialExpression, UMaterialExpressionVectorParameter::StaticClass() ) );
|
|
|
|
// If uniform color expression does not exist, create it.
|
|
if ( !ExpressionConstant4Vector || ExpressionConstant4Vector->IsPendingKill() )
|
|
{
|
|
ExpressionConstant4Vector = NewObject< UMaterialExpressionVectorParameter >(
|
|
Material, UMaterialExpressionVectorParameter::StaticClass(), NAME_None, ObjectFlag );
|
|
ExpressionConstant4Vector->DefaultValue = FLinearColor::White;
|
|
}
|
|
|
|
// Add expression.
|
|
Material->Expressions.Add( ExpressionConstant4Vector );
|
|
|
|
// Locate vertex color expression.
|
|
UMaterialExpressionVertexColor * ExpressionVertexColor =
|
|
Cast< UMaterialExpressionVertexColor >(FHoudiniEngineMaterialUtils::MaterialLocateExpression(
|
|
MaterialExpression, UMaterialExpressionVertexColor::StaticClass() ) );
|
|
|
|
// If vertex color expression does not exist, create it.
|
|
if ( !ExpressionVertexColor || ExpressionVertexColor->IsPendingKill() )
|
|
{
|
|
ExpressionVertexColor = NewObject< UMaterialExpressionVertexColor >(
|
|
Material, UMaterialExpressionVertexColor::StaticClass(), NAME_None, ObjectFlag );
|
|
ExpressionVertexColor->Desc = GeneratingParameterNameVertexColor;
|
|
}
|
|
|
|
// Add expression.
|
|
Material->Expressions.Add( ExpressionVertexColor );
|
|
|
|
// Material should have at least one multiply expression.
|
|
UMaterialExpressionMultiply * MaterialExpressionMultiply = Cast< UMaterialExpressionMultiply >( MaterialExpression );
|
|
if ( !MaterialExpressionMultiply || MaterialExpressionMultiply->IsPendingKill() )
|
|
MaterialExpressionMultiply = NewObject< UMaterialExpressionMultiply >(
|
|
Material, UMaterialExpressionMultiply::StaticClass(), NAME_None, ObjectFlag );
|
|
|
|
// Add expression.
|
|
Material->Expressions.Add( MaterialExpressionMultiply );
|
|
|
|
// See if primary multiplication has secondary multiplication as A input.
|
|
UMaterialExpressionMultiply * MaterialExpressionMultiplySecondary = nullptr;
|
|
if ( MaterialExpressionMultiply->A.Expression )
|
|
MaterialExpressionMultiplySecondary =
|
|
Cast< UMaterialExpressionMultiply >( MaterialExpressionMultiply->A.Expression );
|
|
|
|
// See if a diffuse texture is available.
|
|
HAPI_ParmId ParmDiffuseTextureId =
|
|
FHoudiniEngineUtils::HapiFindParameterByNameOrTag( NodeInfo.id, HAPI_UNREAL_PARAM_MAP_DIFFUSE_0 );
|
|
|
|
if ( ParmDiffuseTextureId >= 0 )
|
|
{
|
|
GeneratingParameterNameDiffuseTexture = TEXT( HAPI_UNREAL_PARAM_MAP_DIFFUSE_0 );
|
|
}
|
|
else
|
|
{
|
|
ParmDiffuseTextureId =
|
|
FHoudiniEngineUtils::HapiFindParameterByNameOrTag( NodeInfo.id, HAPI_UNREAL_PARAM_MAP_DIFFUSE_1 );
|
|
|
|
if ( ParmDiffuseTextureId >= 0 )
|
|
GeneratingParameterNameDiffuseTexture = TEXT( HAPI_UNREAL_PARAM_MAP_DIFFUSE_1 );
|
|
}
|
|
|
|
// See if uniform color is available.
|
|
HAPI_ParmInfo ParmInfoDiffuseColor;
|
|
FHoudiniApi::ParmInfo_Init(&ParmInfoDiffuseColor);
|
|
HAPI_ParmId ParmDiffuseColorId =
|
|
FHoudiniEngineUtils::HapiFindParameterByNameOrTag( NodeInfo.id, HAPI_UNREAL_PARAM_COLOR_DIFFUSE_0, ParmInfoDiffuseColor );
|
|
|
|
if ( ParmDiffuseColorId >= 0 )
|
|
{
|
|
GeneratingParameterNameUniformColor = TEXT( HAPI_UNREAL_PARAM_COLOR_DIFFUSE_0 );
|
|
}
|
|
else
|
|
{
|
|
ParmDiffuseColorId =
|
|
FHoudiniEngineUtils::HapiFindParameterByNameOrTag( NodeInfo.id, HAPI_UNREAL_PARAM_COLOR_DIFFUSE_1, ParmInfoDiffuseColor );
|
|
|
|
if ( ParmDiffuseColorId >= 0 )
|
|
GeneratingParameterNameUniformColor = TEXT( HAPI_UNREAL_PARAM_COLOR_DIFFUSE_1 );
|
|
}
|
|
|
|
// If we have diffuse texture parameter.
|
|
if ( ParmDiffuseTextureId >= 0 )
|
|
{
|
|
TArray< char > ImageBuffer;
|
|
|
|
// Get image planes of diffuse map.
|
|
TArray< FString > DiffuseImagePlanes;
|
|
bool bFoundImagePlanes = FHoudiniEngineMaterialUtils::HapiGetImagePlanes(
|
|
ParmDiffuseTextureId, MaterialInfo, DiffuseImagePlanes );
|
|
|
|
HAPI_ImagePacking ImagePacking = HAPI_IMAGE_PACKING_UNKNOWN;
|
|
const char * PlaneType = "";
|
|
|
|
if ( bFoundImagePlanes && DiffuseImagePlanes.Contains( TEXT( HAPI_UNREAL_MATERIAL_TEXTURE_COLOR ) ) )
|
|
{
|
|
if ( DiffuseImagePlanes.Contains( TEXT( HAPI_UNREAL_MATERIAL_TEXTURE_ALPHA ) ) )
|
|
{
|
|
ImagePacking = HAPI_IMAGE_PACKING_RGBA;
|
|
PlaneType = HAPI_UNREAL_MATERIAL_TEXTURE_COLOR_ALPHA;
|
|
|
|
// Material does use alpha.
|
|
CreateTexture2DParameters.bUseAlpha = true;
|
|
}
|
|
else
|
|
{
|
|
// We still need to have the Alpha plane, just not the CreateTexture2DParameters
|
|
// alpha option. This is because all texture data from Houdini Engine contains
|
|
// the alpha plane by default.
|
|
ImagePacking = HAPI_IMAGE_PACKING_RGBA;
|
|
PlaneType = HAPI_UNREAL_MATERIAL_TEXTURE_COLOR_ALPHA;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
bFoundImagePlanes = false;
|
|
}
|
|
|
|
// Retrieve color plane.
|
|
if ( bFoundImagePlanes && FHoudiniEngineMaterialUtils::HapiExtractImage(
|
|
ParmDiffuseTextureId, MaterialInfo, ImageBuffer, PlaneType,
|
|
HAPI_IMAGE_DATA_INT8, ImagePacking, false ) )
|
|
{
|
|
UPackage * TextureDiffusePackage = nullptr;
|
|
if ( TextureDiffuse && !TextureDiffuse->IsPendingKill() )
|
|
TextureDiffusePackage = Cast< UPackage >( TextureDiffuse->GetOuter() );
|
|
|
|
HAPI_ImageInfo ImageInfo;
|
|
FHoudiniApi::ImageInfo_Init(&ImageInfo);
|
|
Result = FHoudiniApi::GetImageInfo(
|
|
FHoudiniEngine::Get().GetSession(),
|
|
MaterialInfo.nodeId, &ImageInfo );
|
|
|
|
if ( Result == HAPI_RESULT_SUCCESS && ImageInfo.xRes > 0 && ImageInfo.yRes > 0 )
|
|
{
|
|
// Create texture.
|
|
FString TextureDiffuseName;
|
|
bool bCreatedNewTextureDiffuse = false;
|
|
|
|
// Create diffuse texture package, if this is a new diffuse texture.
|
|
if ( !TextureDiffusePackage )
|
|
{
|
|
TextureDiffusePackage = FHoudiniEngineBakeUtils::BakeCreateTexturePackageForComponent(
|
|
HoudiniCookParams,
|
|
MaterialInfo,
|
|
HAPI_UNREAL_PACKAGE_META_GENERATED_TEXTURE_DIFFUSE,
|
|
TextureDiffuseName );
|
|
}
|
|
|
|
// Create diffuse texture, if we need to create one.
|
|
if ( !TextureDiffuse || TextureDiffuse->IsPendingKill() )
|
|
bCreatedNewTextureDiffuse = true;
|
|
|
|
// Get the node path to add it to the meta data
|
|
FString NodePath;
|
|
GetUniqueMaterialShopName( AssetId, MaterialInfo.nodeId, NodePath );
|
|
|
|
// Reuse existing diffuse texture, or create new one.
|
|
TextureDiffuse = FHoudiniEngineMaterialUtils::CreateUnrealTexture(
|
|
TextureDiffuse, ImageInfo,
|
|
TextureDiffusePackage, TextureDiffuseName, ImageBuffer,
|
|
HAPI_UNREAL_PACKAGE_META_GENERATED_TEXTURE_DIFFUSE,
|
|
CreateTexture2DParameters, TEXTUREGROUP_World, NodePath );
|
|
|
|
if ( BakeMode == EBakeMode::CookToTemp )
|
|
TextureDiffuse->SetFlags( RF_Public | RF_Standalone );
|
|
|
|
// Create diffuse sampling expression, if needed.
|
|
if ( !ExpressionTextureSample )
|
|
{
|
|
ExpressionTextureSample = NewObject< UMaterialExpressionTextureSampleParameter2D >(
|
|
Material, UMaterialExpressionTextureSampleParameter2D::StaticClass(), NAME_None, ObjectFlag );
|
|
}
|
|
|
|
// Record generating parameter.
|
|
ExpressionTextureSample->Desc = GeneratingParameterNameDiffuseTexture;
|
|
ExpressionTextureSample->ParameterName = *GeneratingParameterNameDiffuseTexture;
|
|
ExpressionTextureSample->Texture = TextureDiffuse;
|
|
ExpressionTextureSample->SamplerType = SAMPLERTYPE_Color;
|
|
|
|
// Add expression.
|
|
Material->Expressions.Add( ExpressionTextureSample );
|
|
|
|
// Propagate and trigger diffuse texture updates.
|
|
if ( bCreatedNewTextureDiffuse )
|
|
FAssetRegistryModule::AssetCreated( TextureDiffuse );
|
|
|
|
TextureDiffuse->PreEditChange( nullptr );
|
|
TextureDiffuse->PostEditChange();
|
|
TextureDiffuse->MarkPackageDirty();
|
|
}
|
|
}
|
|
}
|
|
|
|
// If we have uniform color parameter.
|
|
if ( ParmDiffuseColorId >= 0 )
|
|
{
|
|
FLinearColor Color = FLinearColor::White;
|
|
|
|
if ( FHoudiniApi::GetParmFloatValues(
|
|
FHoudiniEngine::Get().GetSession(), NodeInfo.id, (float *) &Color.R,
|
|
ParmInfoDiffuseColor.floatValuesIndex, ParmInfoDiffuseColor.size ) == HAPI_RESULT_SUCCESS )
|
|
{
|
|
if ( ParmInfoDiffuseColor.size == 3 )
|
|
Color.A = 1.0f;
|
|
|
|
// Record generating parameter.
|
|
ExpressionConstant4Vector->Desc = GeneratingParameterNameUniformColor;
|
|
ExpressionConstant4Vector->ParameterName = *GeneratingParameterNameUniformColor;
|
|
ExpressionConstant4Vector->DefaultValue = Color;
|
|
}
|
|
}
|
|
|
|
// If we have have texture sample expression present, we need a secondary multiplication expression.
|
|
if ( ExpressionTextureSample )
|
|
{
|
|
if ( !MaterialExpressionMultiplySecondary )
|
|
{
|
|
MaterialExpressionMultiplySecondary = NewObject< UMaterialExpressionMultiply >(
|
|
Material, UMaterialExpressionMultiply::StaticClass(), NAME_None, ObjectFlag );
|
|
|
|
// Add expression.
|
|
Material->Expressions.Add( MaterialExpressionMultiplySecondary );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// If secondary multiplication exists, but we have no sampling, we can free it.
|
|
if ( MaterialExpressionMultiplySecondary )
|
|
{
|
|
MaterialExpressionMultiplySecondary->A.Expression = nullptr;
|
|
MaterialExpressionMultiplySecondary->B.Expression = nullptr;
|
|
MaterialExpressionMultiplySecondary->ConditionalBeginDestroy();
|
|
}
|
|
}
|
|
|
|
float SecondaryExpressionScale = 1.0f;
|
|
if ( MaterialExpressionMultiplySecondary )
|
|
SecondaryExpressionScale = 1.5f;
|
|
|
|
// Create multiplication expression which has uniform color and vertex color.
|
|
MaterialExpressionMultiply->A.Expression = ExpressionConstant4Vector;
|
|
MaterialExpressionMultiply->B.Expression = ExpressionVertexColor;
|
|
|
|
ExpressionConstant4Vector->MaterialExpressionEditorX =
|
|
FHoudiniEngineMaterialUtils::MaterialExpressionNodeX -
|
|
FHoudiniEngineMaterialUtils::MaterialExpressionNodeStepX * SecondaryExpressionScale;
|
|
ExpressionConstant4Vector->MaterialExpressionEditorY = MaterialNodeY;
|
|
MaterialNodeY += FHoudiniEngineMaterialUtils::MaterialExpressionNodeStepY;
|
|
|
|
ExpressionVertexColor->MaterialExpressionEditorX =
|
|
FHoudiniEngineMaterialUtils::MaterialExpressionNodeX -
|
|
FHoudiniEngineMaterialUtils::MaterialExpressionNodeStepX * SecondaryExpressionScale;
|
|
ExpressionVertexColor->MaterialExpressionEditorY = MaterialNodeY;
|
|
MaterialNodeY += FHoudiniEngineMaterialUtils::MaterialExpressionNodeStepY;
|
|
|
|
MaterialExpressionMultiply->MaterialExpressionEditorX = FHoudiniEngineMaterialUtils::MaterialExpressionNodeX;
|
|
MaterialExpressionMultiply->MaterialExpressionEditorY =
|
|
( ExpressionVertexColor->MaterialExpressionEditorY - ExpressionConstant4Vector->MaterialExpressionEditorY ) / 2;
|
|
|
|
// Hook up secondary multiplication expression to first one.
|
|
if ( MaterialExpressionMultiplySecondary )
|
|
{
|
|
MaterialExpressionMultiplySecondary->A.Expression = MaterialExpressionMultiply;
|
|
MaterialExpressionMultiplySecondary->B.Expression = ExpressionTextureSample;
|
|
|
|
ExpressionTextureSample->MaterialExpressionEditorX =
|
|
FHoudiniEngineMaterialUtils::MaterialExpressionNodeX -
|
|
FHoudiniEngineMaterialUtils::MaterialExpressionNodeStepX * SecondaryExpressionScale;
|
|
ExpressionTextureSample->MaterialExpressionEditorY = MaterialNodeY;
|
|
MaterialNodeY += FHoudiniEngineMaterialUtils::MaterialExpressionNodeStepY;
|
|
|
|
MaterialExpressionMultiplySecondary->MaterialExpressionEditorX = FHoudiniEngineMaterialUtils::MaterialExpressionNodeX;
|
|
MaterialExpressionMultiplySecondary->MaterialExpressionEditorY =
|
|
MaterialExpressionMultiply->MaterialExpressionEditorY + FHoudiniEngineMaterialUtils::MaterialExpressionNodeStepY;
|
|
|
|
// Assign expression.
|
|
Material->BaseColor.Expression = MaterialExpressionMultiplySecondary;
|
|
}
|
|
else
|
|
{
|
|
// Assign expression.
|
|
Material->BaseColor.Expression = MaterialExpressionMultiply;
|
|
|
|
MaterialExpressionMultiply->MaterialExpressionEditorX = FHoudiniEngineMaterialUtils::MaterialExpressionNodeX;
|
|
MaterialExpressionMultiply->MaterialExpressionEditorY =
|
|
( ExpressionVertexColor->MaterialExpressionEditorY -
|
|
ExpressionConstant4Vector->MaterialExpressionEditorY ) / 2;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
FHoudiniEngineMaterialUtils::CreateMaterialComponentOpacityMask(
|
|
FHoudiniCookParams& HoudiniCookParams, const HAPI_NodeId& AssetId,
|
|
UMaterial * Material, const HAPI_MaterialInfo & MaterialInfo,
|
|
const HAPI_NodeInfo & NodeInfo, int32 & MaterialNodeY )
|
|
{
|
|
if (!Material || Material->IsPendingKill())
|
|
return false;
|
|
|
|
bool bExpressionCreated = false;
|
|
HAPI_Result Result = HAPI_RESULT_SUCCESS;
|
|
|
|
// Name of generating Houdini parameters.
|
|
FString GeneratingParameterNameTexture = TEXT( "" );
|
|
|
|
UMaterialExpression * MaterialExpression = Material->OpacityMask.Expression;
|
|
|
|
EBakeMode BakeMode = HoudiniCookParams.MaterialAndTextureBakeMode;
|
|
EObjectFlags ObjectFlag = ( BakeMode == EBakeMode::CookToTemp ) ? RF_NoFlags : RF_Standalone;
|
|
|
|
// Opacity expressions.
|
|
UMaterialExpressionTextureSampleParameter2D * ExpressionTextureOpacitySample = nullptr;
|
|
UTexture2D * TextureOpacity = nullptr;
|
|
|
|
// Opacity texture creation parameters.
|
|
FCreateTexture2DParameters CreateTexture2DParameters;
|
|
CreateTexture2DParameters.SourceGuidHash = FGuid();
|
|
CreateTexture2DParameters.bUseAlpha = false;
|
|
CreateTexture2DParameters.CompressionSettings = TC_Grayscale;
|
|
CreateTexture2DParameters.bDeferCompression = true;
|
|
CreateTexture2DParameters.bSRGB = true;
|
|
|
|
// See if opacity texture is available.
|
|
HAPI_ParmId ParmOpacityTextureId =
|
|
FHoudiniEngineUtils::HapiFindParameterByNameOrTag( NodeInfo.id, HAPI_UNREAL_PARAM_MAP_OPACITY_0 );
|
|
|
|
if ( ParmOpacityTextureId >= 0 )
|
|
{
|
|
GeneratingParameterNameTexture = TEXT( HAPI_UNREAL_PARAM_MAP_OPACITY_0 );
|
|
}
|
|
else
|
|
{
|
|
ParmOpacityTextureId =
|
|
FHoudiniEngineUtils::HapiFindParameterByNameOrTag( NodeInfo.id, HAPI_UNREAL_PARAM_MAP_OPACITY_1 );
|
|
|
|
if ( ParmOpacityTextureId >= 0 )
|
|
GeneratingParameterNameTexture = TEXT( HAPI_UNREAL_PARAM_MAP_OPACITY_1 );
|
|
}
|
|
|
|
// If we have opacity texture parameter.
|
|
if ( ParmOpacityTextureId >= 0 )
|
|
{
|
|
TArray< char > ImageBuffer;
|
|
|
|
// Get image planes of opacity map.
|
|
TArray< FString > OpacityImagePlanes;
|
|
bool bFoundImagePlanes = FHoudiniEngineMaterialUtils::HapiGetImagePlanes(
|
|
ParmOpacityTextureId, MaterialInfo, OpacityImagePlanes );
|
|
|
|
HAPI_ImagePacking ImagePacking = HAPI_IMAGE_PACKING_UNKNOWN;
|
|
const char * PlaneType = "";
|
|
|
|
bool bColorAlphaFound = ( OpacityImagePlanes.Contains( TEXT( HAPI_UNREAL_MATERIAL_TEXTURE_ALPHA ) ) && OpacityImagePlanes.Contains( TEXT( HAPI_UNREAL_MATERIAL_TEXTURE_COLOR ) ) );
|
|
|
|
if ( bFoundImagePlanes && bColorAlphaFound )
|
|
{
|
|
ImagePacking = HAPI_IMAGE_PACKING_RGBA;
|
|
PlaneType = HAPI_UNREAL_MATERIAL_TEXTURE_COLOR_ALPHA;
|
|
CreateTexture2DParameters.bUseAlpha = true;
|
|
}
|
|
else
|
|
{
|
|
bFoundImagePlanes = false;
|
|
}
|
|
|
|
if ( bFoundImagePlanes && FHoudiniEngineMaterialUtils::HapiExtractImage(
|
|
ParmOpacityTextureId, MaterialInfo, ImageBuffer, PlaneType,
|
|
HAPI_IMAGE_DATA_INT8, ImagePacking, false ) )
|
|
{
|
|
// Locate sampling expression.
|
|
ExpressionTextureOpacitySample = Cast< UMaterialExpressionTextureSampleParameter2D >(
|
|
FHoudiniEngineMaterialUtils::MaterialLocateExpression(
|
|
MaterialExpression, UMaterialExpressionTextureSampleParameter2D::StaticClass() ) );
|
|
|
|
// Locate opacity texture, if valid.
|
|
if ( ExpressionTextureOpacitySample )
|
|
TextureOpacity = Cast< UTexture2D >( ExpressionTextureOpacitySample->Texture );
|
|
|
|
UPackage * TextureOpacityPackage = nullptr;
|
|
if ( TextureOpacity )
|
|
TextureOpacityPackage = Cast< UPackage >( TextureOpacity->GetOuter() );
|
|
|
|
HAPI_ImageInfo ImageInfo;
|
|
Result = FHoudiniApi::GetImageInfo(
|
|
FHoudiniEngine::Get().GetSession(),
|
|
MaterialInfo.nodeId, &ImageInfo );
|
|
|
|
if ( Result == HAPI_RESULT_SUCCESS && ImageInfo.xRes > 0 && ImageInfo.yRes > 0 )
|
|
{
|
|
// Create texture.
|
|
FString TextureOpacityName;
|
|
bool bCreatedNewTextureOpacity = false;
|
|
|
|
// Create opacity texture package, if this is a new opacity texture.
|
|
if ( !TextureOpacityPackage )
|
|
{
|
|
TextureOpacityPackage = FHoudiniEngineBakeUtils::BakeCreateTexturePackageForComponent(
|
|
HoudiniCookParams,
|
|
MaterialInfo,
|
|
HAPI_UNREAL_PACKAGE_META_GENERATED_TEXTURE_OPACITY_MASK,
|
|
TextureOpacityName );
|
|
}
|
|
|
|
// Create opacity texture, if we need to create one.
|
|
if ( !TextureOpacity )
|
|
bCreatedNewTextureOpacity = true;
|
|
|
|
// Get the node path to add it to the meta data
|
|
FString NodePath;
|
|
GetUniqueMaterialShopName( AssetId, MaterialInfo.nodeId, NodePath );
|
|
|
|
// Reuse existing opacity texture, or create new one.
|
|
TextureOpacity = FHoudiniEngineMaterialUtils::CreateUnrealTexture(
|
|
TextureOpacity, ImageInfo,
|
|
TextureOpacityPackage, TextureOpacityName, ImageBuffer,
|
|
HAPI_UNREAL_PACKAGE_META_GENERATED_TEXTURE_OPACITY_MASK,
|
|
CreateTexture2DParameters,
|
|
TEXTUREGROUP_World, NodePath );
|
|
|
|
if ( BakeMode == EBakeMode::CookToTemp )
|
|
TextureOpacity->SetFlags(RF_Public | RF_Standalone);
|
|
|
|
// Create opacity sampling expression, if needed.
|
|
if ( !ExpressionTextureOpacitySample )
|
|
{
|
|
ExpressionTextureOpacitySample = NewObject< UMaterialExpressionTextureSampleParameter2D >(
|
|
Material, UMaterialExpressionTextureSampleParameter2D::StaticClass(), NAME_None, ObjectFlag );
|
|
}
|
|
|
|
// Record generating parameter.
|
|
ExpressionTextureOpacitySample->Desc = GeneratingParameterNameTexture;
|
|
ExpressionTextureOpacitySample->ParameterName = *GeneratingParameterNameTexture;
|
|
ExpressionTextureOpacitySample->Texture = TextureOpacity;
|
|
ExpressionTextureOpacitySample->SamplerType = SAMPLERTYPE_Grayscale;
|
|
|
|
// Offset node placement.
|
|
ExpressionTextureOpacitySample->MaterialExpressionEditorX =
|
|
FHoudiniEngineMaterialUtils::MaterialExpressionNodeX;
|
|
ExpressionTextureOpacitySample->MaterialExpressionEditorY = MaterialNodeY;
|
|
MaterialNodeY += FHoudiniEngineMaterialUtils::MaterialExpressionNodeStepY;
|
|
|
|
// Add expression.
|
|
Material->Expressions.Add( ExpressionTextureOpacitySample );
|
|
|
|
// We need to set material type to masked.
|
|
TArray< FExpressionOutput > ExpressionOutputs = ExpressionTextureOpacitySample->GetOutputs();
|
|
FExpressionOutput* ExpressionOutput = ExpressionOutputs.GetData();
|
|
|
|
Material->OpacityMask.Expression = ExpressionTextureOpacitySample;
|
|
Material->BlendMode = BLEND_Masked;
|
|
|
|
Material->OpacityMask.Mask = ExpressionOutput->Mask;
|
|
Material->OpacityMask.MaskR = 1;
|
|
Material->OpacityMask.MaskG = 0;
|
|
Material->OpacityMask.MaskB = 0;
|
|
Material->OpacityMask.MaskA = 0;
|
|
|
|
// Propagate and trigger opacity texture updates.
|
|
if ( bCreatedNewTextureOpacity )
|
|
FAssetRegistryModule::AssetCreated( TextureOpacity );
|
|
|
|
TextureOpacity->PreEditChange( nullptr );
|
|
TextureOpacity->PostEditChange();
|
|
TextureOpacity->MarkPackageDirty();
|
|
|
|
bExpressionCreated = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return bExpressionCreated;
|
|
}
|
|
|
|
bool
|
|
FHoudiniEngineMaterialUtils::CreateMaterialComponentOpacity(
|
|
FHoudiniCookParams& HoudiniCookParams, const HAPI_NodeId& AssetId,
|
|
UMaterial * Material, const HAPI_MaterialInfo & MaterialInfo,
|
|
const HAPI_NodeInfo & NodeInfo, int32 & MaterialNodeY )
|
|
{
|
|
if (!Material || Material->IsPendingKill())
|
|
return false;
|
|
|
|
bool bExpressionCreated = false;
|
|
HAPI_Result Result = HAPI_RESULT_SUCCESS;
|
|
float OpacityValue = 1.0f;
|
|
bool bNeedsTranslucency = false;
|
|
|
|
EBakeMode BakeMode = HoudiniCookParams.MaterialAndTextureBakeMode;
|
|
EObjectFlags ObjectFlag = ( BakeMode == EBakeMode::CookToTemp ) ? RF_NoFlags : RF_Standalone;
|
|
|
|
// Name of generating Houdini parameters.
|
|
FString GeneratingParameterNameScalar = TEXT( "" );
|
|
FString GeneratingParameterNameTexture = TEXT( "" );
|
|
|
|
UMaterialExpression * MaterialExpression = Material->Opacity.Expression;
|
|
|
|
// Opacity expressions.
|
|
UMaterialExpressionTextureSampleParameter2D * ExpressionTextureOpacitySample = nullptr;
|
|
UMaterialExpressionScalarParameter * ExpressionScalarOpacity = nullptr;
|
|
UTexture2D * TextureOpacity = nullptr;
|
|
|
|
// Opacity texture creation parameters.
|
|
FCreateTexture2DParameters CreateTexture2DParameters;
|
|
CreateTexture2DParameters.SourceGuidHash = FGuid();
|
|
CreateTexture2DParameters.bUseAlpha = false;
|
|
CreateTexture2DParameters.CompressionSettings = TC_Grayscale;
|
|
CreateTexture2DParameters.bDeferCompression = true;
|
|
CreateTexture2DParameters.bSRGB = true;
|
|
|
|
// If opacity sampling expression was not created, check if diffuse contains an alpha plane.
|
|
if ( !ExpressionTextureOpacitySample )
|
|
{
|
|
UMaterialExpression * MaterialExpressionDiffuse = Material->BaseColor.Expression;
|
|
if ( MaterialExpressionDiffuse )
|
|
{
|
|
// Locate diffuse sampling expression.
|
|
UMaterialExpressionTextureSampleParameter2D * ExpressionTextureDiffuseSample =
|
|
Cast< UMaterialExpressionTextureSampleParameter2D >(
|
|
FHoudiniEngineMaterialUtils::MaterialLocateExpression(
|
|
MaterialExpressionDiffuse,
|
|
UMaterialExpressionTextureSampleParameter2D::StaticClass() ) );
|
|
|
|
// See if there's an alpha plane in this expression's texture.
|
|
if ( ExpressionTextureDiffuseSample )
|
|
{
|
|
UTexture2D * DiffuseTexture = Cast< UTexture2D >( ExpressionTextureDiffuseSample->Texture );
|
|
if ( DiffuseTexture && !DiffuseTexture->CompressionNoAlpha )
|
|
{
|
|
// The diffuse texture has an alpha channel (that wasn't discarded), so we can use it
|
|
ExpressionTextureOpacitySample = ExpressionTextureDiffuseSample;
|
|
bNeedsTranslucency = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Retrieve opacity uniform parameter.
|
|
HAPI_ParmInfo ParmInfoOpacityValue;
|
|
FHoudiniApi::ParmInfo_Init(&ParmInfoOpacityValue);
|
|
HAPI_ParmId ParmOpacityValueId =
|
|
FHoudiniEngineUtils::HapiFindParameterByNameOrTag( NodeInfo.id, HAPI_UNREAL_PARAM_ALPHA_0, ParmInfoOpacityValue );
|
|
|
|
if ( ParmOpacityValueId >= 0 )
|
|
{
|
|
GeneratingParameterNameScalar = TEXT( HAPI_UNREAL_PARAM_ALPHA_0 );
|
|
}
|
|
else
|
|
{
|
|
ParmOpacityValueId =
|
|
FHoudiniEngineUtils::HapiFindParameterByNameOrTag( NodeInfo.id, HAPI_UNREAL_PARAM_ALPHA_1, ParmInfoOpacityValue );
|
|
|
|
if ( ParmOpacityValueId >= 0 )
|
|
GeneratingParameterNameScalar = TEXT( HAPI_UNREAL_PARAM_ALPHA_1 );
|
|
}
|
|
|
|
if ( ParmOpacityValueId >= 0 )
|
|
{
|
|
if (ParmInfoOpacityValue.size > 0 && ParmInfoOpacityValue.floatValuesIndex >= 0 )
|
|
{
|
|
float OpacityValueRetrieved = 1.0f;
|
|
if ( FHoudiniApi::GetParmFloatValues(
|
|
FHoudiniEngine::Get().GetSession(), NodeInfo.id,
|
|
(float *) &OpacityValue, ParmInfoOpacityValue.floatValuesIndex, 1 ) == HAPI_RESULT_SUCCESS )
|
|
{
|
|
if ( !ExpressionScalarOpacity )
|
|
{
|
|
ExpressionScalarOpacity = NewObject< UMaterialExpressionScalarParameter >(
|
|
Material, UMaterialExpressionScalarParameter::StaticClass(), NAME_None, ObjectFlag );
|
|
}
|
|
|
|
// Clamp retrieved value.
|
|
OpacityValueRetrieved = FMath::Clamp< float >( OpacityValueRetrieved, 0.0f, 1.0f );
|
|
OpacityValue = OpacityValueRetrieved;
|
|
|
|
// Set expression fields.
|
|
ExpressionScalarOpacity->DefaultValue = OpacityValue;
|
|
ExpressionScalarOpacity->SliderMin = 0.0f;
|
|
ExpressionScalarOpacity->SliderMax = 1.0f;
|
|
ExpressionScalarOpacity->Desc = GeneratingParameterNameScalar;
|
|
ExpressionScalarOpacity->ParameterName = *GeneratingParameterNameScalar;
|
|
|
|
// Add expression.
|
|
Material->Expressions.Add( ExpressionScalarOpacity );
|
|
|
|
// If alpha is less than 1, we need translucency.
|
|
bNeedsTranslucency |= ( OpacityValue != 1.0f );
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( bNeedsTranslucency )
|
|
Material->BlendMode = BLEND_Translucent;
|
|
|
|
if ( ExpressionScalarOpacity && ExpressionTextureOpacitySample )
|
|
{
|
|
// We have both alpha and alpha uniform, attempt to locate multiply expression.
|
|
UMaterialExpressionMultiply * ExpressionMultiply =
|
|
Cast< UMaterialExpressionMultiply >(
|
|
FHoudiniEngineMaterialUtils::MaterialLocateExpression(
|
|
MaterialExpression,
|
|
UMaterialExpressionMultiply::StaticClass() ) );
|
|
|
|
if ( !ExpressionMultiply )
|
|
ExpressionMultiply = NewObject< UMaterialExpressionMultiply >(
|
|
Material, UMaterialExpressionMultiply::StaticClass(), NAME_None, ObjectFlag );
|
|
|
|
Material->Expressions.Add( ExpressionMultiply );
|
|
|
|
TArray< FExpressionOutput > ExpressionOutputs = ExpressionTextureOpacitySample->GetOutputs();
|
|
FExpressionOutput * ExpressionOutput = ExpressionOutputs.GetData();
|
|
|
|
ExpressionMultiply->A.Expression = ExpressionTextureOpacitySample;
|
|
ExpressionMultiply->B.Expression = ExpressionScalarOpacity;
|
|
|
|
Material->Opacity.Expression = ExpressionMultiply;
|
|
Material->Opacity.Mask = ExpressionOutput->Mask;
|
|
Material->Opacity.MaskR = 0;
|
|
Material->Opacity.MaskG = 0;
|
|
Material->Opacity.MaskB = 0;
|
|
Material->Opacity.MaskA = 1;
|
|
|
|
ExpressionMultiply->MaterialExpressionEditorX = FHoudiniEngineMaterialUtils::MaterialExpressionNodeX;
|
|
ExpressionMultiply->MaterialExpressionEditorY = MaterialNodeY;
|
|
|
|
ExpressionScalarOpacity->MaterialExpressionEditorX =
|
|
FHoudiniEngineMaterialUtils::MaterialExpressionNodeX - FHoudiniEngineMaterialUtils::MaterialExpressionNodeStepX;
|
|
ExpressionScalarOpacity->MaterialExpressionEditorY = MaterialNodeY;
|
|
MaterialNodeY += FHoudiniEngineMaterialUtils::MaterialExpressionNodeStepY;
|
|
|
|
bExpressionCreated = true;
|
|
}
|
|
else if ( ExpressionScalarOpacity )
|
|
{
|
|
Material->Opacity.Expression = ExpressionScalarOpacity;
|
|
|
|
ExpressionScalarOpacity->MaterialExpressionEditorX = FHoudiniEngineMaterialUtils::MaterialExpressionNodeX;
|
|
ExpressionScalarOpacity->MaterialExpressionEditorY = MaterialNodeY;
|
|
MaterialNodeY += FHoudiniEngineMaterialUtils::MaterialExpressionNodeStepY;
|
|
|
|
bExpressionCreated = true;
|
|
}
|
|
else if ( ExpressionTextureOpacitySample )
|
|
{
|
|
TArray<FExpressionOutput> ExpressionOutputs = ExpressionTextureOpacitySample->GetOutputs();
|
|
FExpressionOutput * ExpressionOutput = ExpressionOutputs.GetData();
|
|
|
|
Material->Opacity.Expression = ExpressionTextureOpacitySample;
|
|
Material->Opacity.Mask = ExpressionOutput->Mask;
|
|
Material->Opacity.MaskR = 0;
|
|
Material->Opacity.MaskG = 0;
|
|
Material->Opacity.MaskB = 0;
|
|
Material->Opacity.MaskA = 1;
|
|
|
|
bExpressionCreated = true;
|
|
}
|
|
|
|
return bExpressionCreated;
|
|
}
|
|
|
|
bool
|
|
FHoudiniEngineMaterialUtils::CreateMaterialComponentNormal(
|
|
FHoudiniCookParams& HoudiniCookParams, const HAPI_NodeId& AssetId,
|
|
UMaterial * Material, const HAPI_MaterialInfo & MaterialInfo,
|
|
const HAPI_NodeInfo & NodeInfo, int32 & MaterialNodeY )
|
|
{
|
|
if (!Material || Material->IsPendingKill())
|
|
return false;
|
|
|
|
bool bExpressionCreated = false;
|
|
bool bTangentSpaceNormal = true;
|
|
HAPI_Result Result = HAPI_RESULT_SUCCESS;
|
|
|
|
EBakeMode BakeMode = HoudiniCookParams.MaterialAndTextureBakeMode;
|
|
EObjectFlags ObjectFlag = ( BakeMode == EBakeMode::CookToTemp ) ? RF_NoFlags : RF_Standalone;
|
|
|
|
// Name of generating Houdini parameter.
|
|
FString GeneratingParameterName = TEXT( "" );
|
|
|
|
// Normal texture creation parameters.
|
|
FCreateTexture2DParameters CreateTexture2DParameters;
|
|
CreateTexture2DParameters.SourceGuidHash = FGuid();
|
|
CreateTexture2DParameters.bUseAlpha = false;
|
|
CreateTexture2DParameters.CompressionSettings = TC_Normalmap;
|
|
CreateTexture2DParameters.bDeferCompression = true;
|
|
CreateTexture2DParameters.bSRGB = false;
|
|
|
|
// See if separate normal texture is available.
|
|
HAPI_ParmId ParmNameNormalId =
|
|
FHoudiniEngineUtils::HapiFindParameterByNameOrTag( NodeInfo.id, HAPI_UNREAL_PARAM_MAP_NORMAL_0 );
|
|
|
|
if ( ParmNameNormalId >= 0 )
|
|
{
|
|
GeneratingParameterName = TEXT( HAPI_UNREAL_PARAM_MAP_NORMAL_0 );
|
|
}
|
|
else
|
|
{
|
|
ParmNameNormalId =
|
|
FHoudiniEngineUtils::HapiFindParameterByNameOrTag( NodeInfo.id, HAPI_UNREAL_PARAM_MAP_NORMAL_1 );
|
|
|
|
if ( ParmNameNormalId >= 0 )
|
|
GeneratingParameterName = TEXT( HAPI_UNREAL_PARAM_MAP_NORMAL_1 );
|
|
}
|
|
|
|
if ( ParmNameNormalId >= 0 )
|
|
{
|
|
// Retrieve space for this normal texture.
|
|
HAPI_ParmInfo ParmInfoNormalType;
|
|
FHoudiniApi::ParmInfo_Init(&ParmInfoNormalType);
|
|
int32 ParmNormalTypeId =
|
|
FHoudiniEngineUtils::HapiFindParameterByNameOrTag( NodeInfo.id, HAPI_UNREAL_PARAM_MAP_NORMAL_TYPE, ParmInfoNormalType );
|
|
|
|
// Retrieve value for normal type choice list (if exists).
|
|
|
|
if ( ParmNormalTypeId >= 0 )
|
|
{
|
|
FString NormalType = TEXT( HAPI_UNREAL_PARAM_MAP_NORMAL_TYPE_TANGENT );
|
|
|
|
if ( ParmInfoNormalType.size > 0 && ParmInfoNormalType.stringValuesIndex >= 0 )
|
|
{
|
|
HAPI_StringHandle StringHandle;
|
|
if ( FHoudiniApi::GetParmStringValues(
|
|
FHoudiniEngine::Get().GetSession(),
|
|
NodeInfo.id, false, &StringHandle, ParmInfoNormalType.stringValuesIndex, ParmInfoNormalType.size ) == HAPI_RESULT_SUCCESS )
|
|
{
|
|
// Get the actual string value.
|
|
FString NormalTypeString = TEXT( "" );
|
|
FHoudiniEngineString HoudiniEngineString( StringHandle );
|
|
if ( HoudiniEngineString.ToFString( NormalTypeString ) )
|
|
NormalType = NormalTypeString;
|
|
}
|
|
}
|
|
|
|
// Check if we require world space normals.
|
|
if ( NormalType.Equals( TEXT( HAPI_UNREAL_PARAM_MAP_NORMAL_TYPE_WORLD ), ESearchCase::IgnoreCase ) )
|
|
bTangentSpaceNormal = false;
|
|
}
|
|
|
|
TArray< char > ImageBuffer;
|
|
|
|
// Retrieve color plane.
|
|
if (FHoudiniEngineMaterialUtils::HapiExtractImage(
|
|
ParmNameNormalId, MaterialInfo, ImageBuffer,
|
|
HAPI_UNREAL_MATERIAL_TEXTURE_COLOR, HAPI_IMAGE_DATA_INT8, HAPI_IMAGE_PACKING_RGBA, true ) )
|
|
{
|
|
UMaterialExpressionTextureSampleParameter2D * ExpressionNormal =
|
|
Cast< UMaterialExpressionTextureSampleParameter2D >( Material->Normal.Expression );
|
|
|
|
UTexture2D * TextureNormal = nullptr;
|
|
if ( ExpressionNormal )
|
|
{
|
|
TextureNormal = Cast< UTexture2D >( ExpressionNormal->Texture );
|
|
}
|
|
else
|
|
{
|
|
// Otherwise new expression is of a different type.
|
|
if ( Material->Normal.Expression )
|
|
{
|
|
Material->Normal.Expression->ConditionalBeginDestroy();
|
|
Material->Normal.Expression = nullptr;
|
|
}
|
|
}
|
|
|
|
UPackage * TextureNormalPackage = nullptr;
|
|
if ( TextureNormal )
|
|
TextureNormalPackage = Cast< UPackage >( TextureNormal->GetOuter() );
|
|
|
|
HAPI_ImageInfo ImageInfo;
|
|
FHoudiniApi::ImageInfo_Init(&ImageInfo);
|
|
Result = FHoudiniApi::GetImageInfo(
|
|
FHoudiniEngine::Get().GetSession(),
|
|
MaterialInfo.nodeId, &ImageInfo );
|
|
|
|
if ( Result == HAPI_RESULT_SUCCESS && ImageInfo.xRes > 0 && ImageInfo.yRes > 0 )
|
|
{
|
|
// Create texture.
|
|
FString TextureNormalName;
|
|
bool bCreatedNewTextureNormal = false;
|
|
|
|
// Create normal texture package, if this is a new normal texture.
|
|
if ( !TextureNormalPackage )
|
|
{
|
|
TextureNormalPackage =
|
|
FHoudiniEngineBakeUtils::BakeCreateTexturePackageForComponent(
|
|
HoudiniCookParams,
|
|
MaterialInfo,
|
|
HAPI_UNREAL_PACKAGE_META_GENERATED_TEXTURE_NORMAL,
|
|
TextureNormalName );
|
|
}
|
|
|
|
// Create normal texture, if we need to create one.
|
|
if ( !TextureNormal )
|
|
bCreatedNewTextureNormal = true;
|
|
|
|
// Get the node path to add it to the meta data
|
|
FString NodePath;
|
|
GetUniqueMaterialShopName( AssetId, MaterialInfo.nodeId, NodePath );
|
|
|
|
// Reuse existing normal texture, or create new one.
|
|
TextureNormal = FHoudiniEngineMaterialUtils::CreateUnrealTexture(
|
|
TextureNormal, ImageInfo,
|
|
TextureNormalPackage, TextureNormalName, ImageBuffer,
|
|
HAPI_UNREAL_PACKAGE_META_GENERATED_TEXTURE_NORMAL,
|
|
CreateTexture2DParameters,
|
|
TEXTUREGROUP_WorldNormalMap,
|
|
NodePath );
|
|
|
|
if ( BakeMode == EBakeMode::CookToTemp )
|
|
TextureNormal->SetFlags(RF_Public | RF_Standalone);
|
|
|
|
// Create normal sampling expression, if needed.
|
|
if ( !ExpressionNormal )
|
|
ExpressionNormal = NewObject< UMaterialExpressionTextureSampleParameter2D >(
|
|
Material, UMaterialExpressionTextureSampleParameter2D::StaticClass(), NAME_None, ObjectFlag );
|
|
|
|
// Record generating parameter.
|
|
ExpressionNormal->Desc = GeneratingParameterName;
|
|
ExpressionNormal->ParameterName = *GeneratingParameterName;
|
|
|
|
ExpressionNormal->Texture = TextureNormal;
|
|
ExpressionNormal->SamplerType = SAMPLERTYPE_Normal;
|
|
|
|
// Offset node placement.
|
|
ExpressionNormal->MaterialExpressionEditorX = FHoudiniEngineMaterialUtils::MaterialExpressionNodeX;
|
|
ExpressionNormal->MaterialExpressionEditorY = MaterialNodeY;
|
|
MaterialNodeY += FHoudiniEngineMaterialUtils::MaterialExpressionNodeStepY;
|
|
|
|
// Set normal space.
|
|
Material->bTangentSpaceNormal = bTangentSpaceNormal;
|
|
|
|
// Assign expression to material.
|
|
Material->Expressions.Add(ExpressionNormal);
|
|
Material->Normal.Expression = ExpressionNormal;
|
|
|
|
bExpressionCreated = true;
|
|
|
|
// Propagate and trigger normal texture updates.
|
|
if (bCreatedNewTextureNormal)
|
|
FAssetRegistryModule::AssetCreated(TextureNormal);
|
|
|
|
TextureNormal->PreEditChange(nullptr);
|
|
TextureNormal->PostEditChange();
|
|
TextureNormal->MarkPackageDirty();
|
|
}
|
|
}
|
|
}
|
|
|
|
// If separate normal map was not found, see if normal plane exists in diffuse map.
|
|
if ( !bExpressionCreated )
|
|
{
|
|
// See if diffuse texture is available.
|
|
HAPI_ParmId ParmNameBaseId =
|
|
FHoudiniEngineUtils::HapiFindParameterByNameOrTag( NodeInfo.id, HAPI_UNREAL_PARAM_MAP_DIFFUSE_0 );
|
|
|
|
if ( ParmNameBaseId >= 0 )
|
|
{
|
|
GeneratingParameterName = TEXT( HAPI_UNREAL_PARAM_MAP_DIFFUSE_0 );
|
|
}
|
|
else
|
|
{
|
|
ParmNameBaseId =
|
|
FHoudiniEngineUtils::HapiFindParameterByNameOrTag( NodeInfo.id, HAPI_UNREAL_PARAM_MAP_DIFFUSE_1 );
|
|
|
|
if ( ParmNameBaseId >= 0 )
|
|
GeneratingParameterName = TEXT( HAPI_UNREAL_PARAM_MAP_DIFFUSE_1 );
|
|
}
|
|
|
|
if ( ParmNameBaseId >= 0 )
|
|
{
|
|
// Normal plane is available in diffuse map.
|
|
|
|
TArray< char > ImageBuffer;
|
|
|
|
// Retrieve color plane - this will contain normal data.
|
|
if ( FHoudiniEngineMaterialUtils::HapiExtractImage(
|
|
ParmNameBaseId, MaterialInfo, ImageBuffer,
|
|
HAPI_UNREAL_MATERIAL_TEXTURE_NORMAL, HAPI_IMAGE_DATA_INT8, HAPI_IMAGE_PACKING_RGB, true ) )
|
|
{
|
|
UMaterialExpressionTextureSampleParameter2D * ExpressionNormal =
|
|
Cast< UMaterialExpressionTextureSampleParameter2D >( Material->Normal.Expression );
|
|
|
|
UTexture2D * TextureNormal = nullptr;
|
|
if ( ExpressionNormal )
|
|
{
|
|
TextureNormal = Cast< UTexture2D >( ExpressionNormal->Texture );
|
|
}
|
|
else
|
|
{
|
|
// Otherwise new expression is of a different type.
|
|
if ( Material->Normal.Expression )
|
|
{
|
|
Material->Normal.Expression->ConditionalBeginDestroy();
|
|
Material->Normal.Expression = nullptr;
|
|
}
|
|
}
|
|
|
|
UPackage * TextureNormalPackage = nullptr;
|
|
if ( TextureNormal )
|
|
TextureNormalPackage = Cast< UPackage >( TextureNormal->GetOuter() );
|
|
|
|
HAPI_ImageInfo ImageInfo;
|
|
FHoudiniApi::ImageInfo_Init(&ImageInfo);
|
|
Result = FHoudiniApi::GetImageInfo(
|
|
FHoudiniEngine::Get().GetSession(),
|
|
MaterialInfo.nodeId, &ImageInfo );
|
|
|
|
if ( Result == HAPI_RESULT_SUCCESS && ImageInfo.xRes > 0 && ImageInfo.yRes > 0 )
|
|
{
|
|
// Create texture.
|
|
FString TextureNormalName;
|
|
bool bCreatedNewTextureNormal = false;
|
|
|
|
// Create normal texture package, if this is a new normal texture.
|
|
if ( !TextureNormalPackage )
|
|
{
|
|
TextureNormalPackage = FHoudiniEngineBakeUtils::BakeCreateTexturePackageForComponent(
|
|
HoudiniCookParams,
|
|
MaterialInfo,
|
|
HAPI_UNREAL_PACKAGE_META_GENERATED_TEXTURE_NORMAL,
|
|
TextureNormalName );
|
|
}
|
|
|
|
// Create normal texture, if we need to create one.
|
|
if ( !TextureNormal )
|
|
bCreatedNewTextureNormal = true;
|
|
|
|
// Get the node path to add it to the meta data
|
|
FString NodePath;
|
|
GetUniqueMaterialShopName( AssetId, MaterialInfo.nodeId, NodePath );
|
|
|
|
// Reuse existing normal texture, or create new one.
|
|
TextureNormal = FHoudiniEngineMaterialUtils::CreateUnrealTexture(
|
|
TextureNormal, ImageInfo,
|
|
TextureNormalPackage, TextureNormalName, ImageBuffer,
|
|
HAPI_UNREAL_PACKAGE_META_GENERATED_TEXTURE_NORMAL, CreateTexture2DParameters,
|
|
TEXTUREGROUP_WorldNormalMap, NodePath );
|
|
|
|
if ( BakeMode == EBakeMode::CookToTemp )
|
|
TextureNormal->SetFlags( RF_Public | RF_Standalone );
|
|
|
|
// Create normal sampling expression, if needed.
|
|
if ( !ExpressionNormal )
|
|
ExpressionNormal = NewObject< UMaterialExpressionTextureSampleParameter2D >(
|
|
Material, UMaterialExpressionTextureSampleParameter2D::StaticClass(), NAME_None, ObjectFlag );
|
|
|
|
// Record generating parameter.
|
|
ExpressionNormal->Desc = GeneratingParameterName;
|
|
ExpressionNormal->ParameterName = *GeneratingParameterName;
|
|
|
|
ExpressionNormal->Texture = TextureNormal;
|
|
ExpressionNormal->SamplerType = SAMPLERTYPE_Normal;
|
|
|
|
// Offset node placement.
|
|
ExpressionNormal->MaterialExpressionEditorX = FHoudiniEngineMaterialUtils::MaterialExpressionNodeX;
|
|
ExpressionNormal->MaterialExpressionEditorY = MaterialNodeY;
|
|
MaterialNodeY += FHoudiniEngineMaterialUtils::MaterialExpressionNodeStepY;
|
|
|
|
// Set normal space.
|
|
Material->bTangentSpaceNormal = bTangentSpaceNormal;
|
|
|
|
// Assign expression to material.
|
|
Material->Expressions.Add( ExpressionNormal );
|
|
Material->Normal.Expression = ExpressionNormal;
|
|
|
|
// Propagate and trigger diffuse texture updates.
|
|
if ( bCreatedNewTextureNormal )
|
|
FAssetRegistryModule::AssetCreated( TextureNormal );
|
|
|
|
TextureNormal->PreEditChange( nullptr );
|
|
TextureNormal->PostEditChange();
|
|
TextureNormal->MarkPackageDirty();
|
|
|
|
bExpressionCreated = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return bExpressionCreated;
|
|
}
|
|
|
|
bool
|
|
FHoudiniEngineMaterialUtils::CreateMaterialComponentSpecular(
|
|
FHoudiniCookParams& HoudiniCookParams, const HAPI_NodeId& AssetId,
|
|
UMaterial * Material, const HAPI_MaterialInfo & MaterialInfo,
|
|
const HAPI_NodeInfo & NodeInfo, int32 & MaterialNodeY )
|
|
{
|
|
if (!Material || Material->IsPendingKill())
|
|
return false;
|
|
|
|
bool bExpressionCreated = false;
|
|
HAPI_Result Result = HAPI_RESULT_SUCCESS;
|
|
|
|
EBakeMode BakeMode = HoudiniCookParams.MaterialAndTextureBakeMode;
|
|
EObjectFlags ObjectFlag = ( BakeMode == EBakeMode::CookToTemp ) ? RF_NoFlags : RF_Standalone;
|
|
|
|
// Name of generating Houdini parameter.
|
|
FString GeneratingParameterName = TEXT( "" );
|
|
|
|
// Specular texture creation parameters.
|
|
FCreateTexture2DParameters CreateTexture2DParameters;
|
|
CreateTexture2DParameters.SourceGuidHash = FGuid();
|
|
CreateTexture2DParameters.bUseAlpha = false;
|
|
CreateTexture2DParameters.CompressionSettings = TC_Grayscale;
|
|
CreateTexture2DParameters.bDeferCompression = true;
|
|
CreateTexture2DParameters.bSRGB = false;
|
|
|
|
// See if specular texture is available.
|
|
HAPI_ParmId ParmNameSpecularId =
|
|
FHoudiniEngineUtils::HapiFindParameterByNameOrTag( NodeInfo.id, HAPI_UNREAL_PARAM_MAP_SPECULAR_0 );
|
|
|
|
if ( ParmNameSpecularId >= 0 )
|
|
{
|
|
GeneratingParameterName = TEXT( HAPI_UNREAL_PARAM_MAP_SPECULAR_0 );
|
|
}
|
|
else
|
|
{
|
|
ParmNameSpecularId =
|
|
FHoudiniEngineUtils::HapiFindParameterByNameOrTag( NodeInfo.id, HAPI_UNREAL_PARAM_MAP_SPECULAR_1 );
|
|
|
|
if ( ParmNameSpecularId >= 0 )
|
|
GeneratingParameterName = TEXT( HAPI_UNREAL_PARAM_MAP_SPECULAR_1 );
|
|
}
|
|
|
|
if ( ParmNameSpecularId >= 0 )
|
|
{
|
|
TArray< char > ImageBuffer;
|
|
|
|
// Retrieve color plane.
|
|
if ( FHoudiniEngineMaterialUtils::HapiExtractImage(
|
|
ParmNameSpecularId, MaterialInfo, ImageBuffer,
|
|
HAPI_UNREAL_MATERIAL_TEXTURE_COLOR, HAPI_IMAGE_DATA_INT8, HAPI_IMAGE_PACKING_RGBA, true ) )
|
|
{
|
|
UMaterialExpressionTextureSampleParameter2D * ExpressionSpecular =
|
|
Cast< UMaterialExpressionTextureSampleParameter2D >( Material->Specular.Expression );
|
|
|
|
UTexture2D * TextureSpecular = nullptr;
|
|
if ( ExpressionSpecular )
|
|
{
|
|
TextureSpecular = Cast< UTexture2D >( ExpressionSpecular->Texture );
|
|
}
|
|
else
|
|
{
|
|
// Otherwise new expression is of a different type.
|
|
if ( Material->Specular.Expression )
|
|
{
|
|
Material->Specular.Expression->ConditionalBeginDestroy();
|
|
Material->Specular.Expression = nullptr;
|
|
}
|
|
}
|
|
|
|
UPackage * TextureSpecularPackage = nullptr;
|
|
if ( TextureSpecular )
|
|
TextureSpecularPackage = Cast< UPackage >( TextureSpecular->GetOuter() );
|
|
|
|
HAPI_ImageInfo ImageInfo;
|
|
FHoudiniApi::ImageInfo_Init(&ImageInfo);
|
|
Result = FHoudiniApi::GetImageInfo(
|
|
FHoudiniEngine::Get().GetSession(),
|
|
MaterialInfo.nodeId, &ImageInfo );
|
|
|
|
if ( Result == HAPI_RESULT_SUCCESS && ImageInfo.xRes > 0 && ImageInfo.yRes > 0 )
|
|
{
|
|
// Create texture.
|
|
FString TextureSpecularName;
|
|
bool bCreatedNewTextureSpecular = false;
|
|
|
|
// Create specular texture package, if this is a new specular texture.
|
|
if ( !TextureSpecularPackage )
|
|
{
|
|
TextureSpecularPackage = FHoudiniEngineBakeUtils::BakeCreateTexturePackageForComponent(
|
|
HoudiniCookParams,
|
|
MaterialInfo,
|
|
HAPI_UNREAL_PACKAGE_META_GENERATED_TEXTURE_SPECULAR,
|
|
TextureSpecularName );
|
|
}
|
|
|
|
// Create specular texture, if we need to create one.
|
|
if ( !TextureSpecular )
|
|
bCreatedNewTextureSpecular = true;
|
|
|
|
// Get the node path to add it to the meta data
|
|
FString NodePath;
|
|
GetUniqueMaterialShopName( AssetId, MaterialInfo.nodeId, NodePath );
|
|
|
|
// Reuse existing specular texture, or create new one.
|
|
TextureSpecular = FHoudiniEngineMaterialUtils::CreateUnrealTexture(
|
|
TextureSpecular, ImageInfo,
|
|
TextureSpecularPackage, TextureSpecularName, ImageBuffer,
|
|
HAPI_UNREAL_PACKAGE_META_GENERATED_TEXTURE_SPECULAR,
|
|
CreateTexture2DParameters,
|
|
TEXTUREGROUP_World, NodePath );
|
|
|
|
if ( BakeMode == EBakeMode::CookToTemp )
|
|
TextureSpecular->SetFlags( RF_Public | RF_Standalone );
|
|
|
|
// Create specular sampling expression, if needed.
|
|
if ( !ExpressionSpecular )
|
|
{
|
|
ExpressionSpecular = NewObject< UMaterialExpressionTextureSampleParameter2D >(
|
|
Material, UMaterialExpressionTextureSampleParameter2D::StaticClass(), NAME_None, ObjectFlag );
|
|
}
|
|
|
|
// Record generating parameter.
|
|
ExpressionSpecular->Desc = GeneratingParameterName;
|
|
ExpressionSpecular->ParameterName = *GeneratingParameterName;
|
|
|
|
ExpressionSpecular->Texture = TextureSpecular;
|
|
ExpressionSpecular->SamplerType = SAMPLERTYPE_LinearGrayscale;
|
|
|
|
// Offset node placement.
|
|
ExpressionSpecular->MaterialExpressionEditorX = FHoudiniEngineMaterialUtils::MaterialExpressionNodeX;
|
|
ExpressionSpecular->MaterialExpressionEditorY = MaterialNodeY;
|
|
MaterialNodeY += FHoudiniEngineMaterialUtils::MaterialExpressionNodeStepY;
|
|
|
|
// Assign expression to material.
|
|
Material->Expressions.Add( ExpressionSpecular );
|
|
Material->Specular.Expression = ExpressionSpecular;
|
|
|
|
bExpressionCreated = true;
|
|
|
|
// Propagate and trigger specular texture updates.
|
|
if (bCreatedNewTextureSpecular)
|
|
FAssetRegistryModule::AssetCreated(TextureSpecular);
|
|
|
|
TextureSpecular->PreEditChange(nullptr);
|
|
TextureSpecular->PostEditChange();
|
|
TextureSpecular->MarkPackageDirty();
|
|
}
|
|
}
|
|
}
|
|
|
|
HAPI_ParmInfo ParmInfoSpecularColor;
|
|
FHoudiniApi::ParmInfo_Init(&ParmInfoSpecularColor);
|
|
HAPI_ParmId ParmNameSpecularColorId =
|
|
FHoudiniEngineUtils::HapiFindParameterByNameOrTag( NodeInfo.id, HAPI_UNREAL_PARAM_COLOR_SPECULAR_0, ParmInfoSpecularColor );
|
|
|
|
if( ParmNameSpecularColorId >= 0 )
|
|
{
|
|
GeneratingParameterName = TEXT( HAPI_UNREAL_PARAM_COLOR_SPECULAR_0 );
|
|
}
|
|
else
|
|
{
|
|
ParmNameSpecularColorId =
|
|
FHoudiniEngineUtils::HapiFindParameterByNameOrTag( NodeInfo.id, HAPI_UNREAL_PARAM_COLOR_SPECULAR_1, ParmInfoSpecularColor );
|
|
|
|
if ( ParmNameSpecularColorId >= 0 )
|
|
GeneratingParameterName = TEXT( HAPI_UNREAL_PARAM_COLOR_SPECULAR_1 );
|
|
}
|
|
|
|
if ( !bExpressionCreated && ParmNameSpecularColorId >= 0 )
|
|
{
|
|
// Specular color is available.
|
|
|
|
FLinearColor Color = FLinearColor::White;
|
|
|
|
if ( FHoudiniApi::GetParmFloatValues(
|
|
FHoudiniEngine::Get().GetSession(), NodeInfo.id, (float*) &Color.R,
|
|
ParmInfoSpecularColor.floatValuesIndex, ParmInfoSpecularColor.size ) == HAPI_RESULT_SUCCESS )
|
|
{
|
|
if (ParmInfoSpecularColor.size == 3 )
|
|
Color.A = 1.0f;
|
|
|
|
UMaterialExpressionVectorParameter * ExpressionSpecularColor =
|
|
Cast< UMaterialExpressionVectorParameter >( Material->Specular.Expression );
|
|
|
|
// Create color const expression and add it to material, if we don't have one.
|
|
if ( !ExpressionSpecularColor )
|
|
{
|
|
// Otherwise new expression is of a different type.
|
|
if ( Material->Specular.Expression )
|
|
{
|
|
Material->Specular.Expression->ConditionalBeginDestroy();
|
|
Material->Specular.Expression = nullptr;
|
|
}
|
|
|
|
ExpressionSpecularColor = NewObject< UMaterialExpressionVectorParameter >(
|
|
Material, UMaterialExpressionVectorParameter::StaticClass(), NAME_None, ObjectFlag );
|
|
}
|
|
|
|
// Record generating parameter.
|
|
ExpressionSpecularColor->Desc = GeneratingParameterName;
|
|
ExpressionSpecularColor->ParameterName = *GeneratingParameterName;
|
|
|
|
ExpressionSpecularColor->DefaultValue = Color;
|
|
|
|
// Offset node placement.
|
|
ExpressionSpecularColor->MaterialExpressionEditorX = FHoudiniEngineMaterialUtils::MaterialExpressionNodeX;
|
|
ExpressionSpecularColor->MaterialExpressionEditorY = MaterialNodeY;
|
|
MaterialNodeY += FHoudiniEngineMaterialUtils::MaterialExpressionNodeStepY;
|
|
|
|
// Assign expression to material.
|
|
Material->Expressions.Add( ExpressionSpecularColor );
|
|
Material->Specular.Expression = ExpressionSpecularColor;
|
|
|
|
bExpressionCreated = true;
|
|
}
|
|
}
|
|
|
|
return bExpressionCreated;
|
|
}
|
|
|
|
bool
|
|
FHoudiniEngineMaterialUtils::CreateMaterialComponentRoughness(
|
|
FHoudiniCookParams& HoudiniCookParams, const HAPI_NodeId& AssetId,
|
|
UMaterial * Material, const HAPI_MaterialInfo & MaterialInfo,
|
|
const HAPI_NodeInfo & NodeInfo, int32 & MaterialNodeY )
|
|
{
|
|
if (!Material || Material->IsPendingKill())
|
|
return false;
|
|
|
|
bool bExpressionCreated = false;
|
|
HAPI_Result Result = HAPI_RESULT_SUCCESS;
|
|
|
|
EBakeMode BakeMode = HoudiniCookParams.MaterialAndTextureBakeMode;
|
|
EObjectFlags ObjectFlag = ( BakeMode == EBakeMode::CookToTemp ) ? RF_NoFlags : RF_Standalone;
|
|
|
|
// Name of generating Houdini parameter.
|
|
FString GeneratingParameterName = TEXT( "" );
|
|
|
|
// Roughness texture creation parameters.
|
|
FCreateTexture2DParameters CreateTexture2DParameters;
|
|
CreateTexture2DParameters.SourceGuidHash = FGuid();
|
|
CreateTexture2DParameters.bUseAlpha = false;
|
|
CreateTexture2DParameters.CompressionSettings = TC_Grayscale;
|
|
CreateTexture2DParameters.bDeferCompression = true;
|
|
CreateTexture2DParameters.bSRGB = false;
|
|
|
|
// See if roughness texture is available.
|
|
HAPI_ParmId ParmNameRoughnessId =
|
|
FHoudiniEngineUtils::HapiFindParameterByNameOrTag( NodeInfo.id, HAPI_UNREAL_PARAM_MAP_ROUGHNESS_0 );
|
|
|
|
if ( ParmNameRoughnessId >= 0 )
|
|
{
|
|
GeneratingParameterName = TEXT( HAPI_UNREAL_PARAM_MAP_ROUGHNESS_0 );
|
|
}
|
|
else
|
|
{
|
|
ParmNameRoughnessId =
|
|
FHoudiniEngineUtils::HapiFindParameterByNameOrTag( NodeInfo.id, HAPI_UNREAL_PARAM_MAP_ROUGHNESS_1 );
|
|
|
|
if ( ParmNameRoughnessId >= 0 )
|
|
GeneratingParameterName = TEXT( HAPI_UNREAL_PARAM_MAP_ROUGHNESS_1 );
|
|
}
|
|
|
|
if ( ParmNameRoughnessId >= 0 )
|
|
{
|
|
TArray< char > ImageBuffer;
|
|
|
|
// Retrieve color plane.
|
|
if ( FHoudiniEngineMaterialUtils::HapiExtractImage(
|
|
ParmNameRoughnessId, MaterialInfo, ImageBuffer,
|
|
HAPI_UNREAL_MATERIAL_TEXTURE_COLOR, HAPI_IMAGE_DATA_INT8, HAPI_IMAGE_PACKING_RGBA, true ) )
|
|
{
|
|
UMaterialExpressionTextureSampleParameter2D* ExpressionRoughness =
|
|
Cast< UMaterialExpressionTextureSampleParameter2D >( Material->Roughness.Expression );
|
|
|
|
UTexture2D* TextureRoughness = nullptr;
|
|
if ( ExpressionRoughness )
|
|
{
|
|
TextureRoughness = Cast< UTexture2D >( ExpressionRoughness->Texture );
|
|
}
|
|
else
|
|
{
|
|
// Otherwise new expression is of a different type.
|
|
if ( Material->Roughness.Expression )
|
|
{
|
|
Material->Roughness.Expression->ConditionalBeginDestroy();
|
|
Material->Roughness.Expression = nullptr;
|
|
}
|
|
}
|
|
|
|
UPackage * TextureRoughnessPackage = nullptr;
|
|
if ( TextureRoughness )
|
|
TextureRoughnessPackage = Cast< UPackage >( TextureRoughness->GetOuter() );
|
|
|
|
HAPI_ImageInfo ImageInfo;
|
|
FHoudiniApi::ImageInfo_Init(&ImageInfo);
|
|
Result = FHoudiniApi::GetImageInfo(
|
|
FHoudiniEngine::Get().GetSession(),
|
|
MaterialInfo.nodeId, &ImageInfo );
|
|
|
|
if ( Result == HAPI_RESULT_SUCCESS && ImageInfo.xRes > 0 && ImageInfo.yRes > 0 )
|
|
{
|
|
// Create texture.
|
|
FString TextureRoughnessName;
|
|
bool bCreatedNewTextureRoughness = false;
|
|
|
|
// Create roughness texture package, if this is a new roughness texture.
|
|
if ( !TextureRoughnessPackage )
|
|
{
|
|
TextureRoughnessPackage = FHoudiniEngineBakeUtils::BakeCreateTexturePackageForComponent(
|
|
HoudiniCookParams,
|
|
MaterialInfo,
|
|
HAPI_UNREAL_PACKAGE_META_GENERATED_TEXTURE_ROUGHNESS,
|
|
TextureRoughnessName );
|
|
}
|
|
|
|
// Create roughness texture, if we need to create one.
|
|
if ( !TextureRoughness )
|
|
bCreatedNewTextureRoughness = true;
|
|
|
|
// Get the node path to add it to the meta data
|
|
FString NodePath;
|
|
GetUniqueMaterialShopName( AssetId, MaterialInfo.nodeId, NodePath );
|
|
|
|
// Reuse existing roughness texture, or create new one.
|
|
TextureRoughness = FHoudiniEngineMaterialUtils::CreateUnrealTexture(
|
|
TextureRoughness, ImageInfo,
|
|
TextureRoughnessPackage, TextureRoughnessName, ImageBuffer,
|
|
HAPI_UNREAL_PACKAGE_META_GENERATED_TEXTURE_ROUGHNESS,
|
|
CreateTexture2DParameters,
|
|
TEXTUREGROUP_World, NodePath );
|
|
|
|
if ( BakeMode == EBakeMode::CookToTemp )
|
|
TextureRoughness->SetFlags( RF_Public | RF_Standalone );
|
|
|
|
// Create roughness sampling expression, if needed.
|
|
if ( !ExpressionRoughness )
|
|
ExpressionRoughness = NewObject< UMaterialExpressionTextureSampleParameter2D >(
|
|
Material, UMaterialExpressionTextureSampleParameter2D::StaticClass(), NAME_None, ObjectFlag );
|
|
|
|
// Record generating parameter.
|
|
ExpressionRoughness->Desc = GeneratingParameterName;
|
|
ExpressionRoughness->ParameterName = *GeneratingParameterName;
|
|
|
|
ExpressionRoughness->Texture = TextureRoughness;
|
|
ExpressionRoughness->SamplerType = SAMPLERTYPE_LinearGrayscale;
|
|
|
|
// Offset node placement.
|
|
ExpressionRoughness->MaterialExpressionEditorX = FHoudiniEngineMaterialUtils::MaterialExpressionNodeX;
|
|
ExpressionRoughness->MaterialExpressionEditorY = MaterialNodeY;
|
|
MaterialNodeY += FHoudiniEngineMaterialUtils::MaterialExpressionNodeStepY;
|
|
|
|
// Assign expression to material.
|
|
Material->Expressions.Add( ExpressionRoughness );
|
|
Material->Roughness.Expression = ExpressionRoughness;
|
|
|
|
bExpressionCreated = true;
|
|
|
|
// Propagate and trigger roughness texture updates.
|
|
if (bCreatedNewTextureRoughness)
|
|
FAssetRegistryModule::AssetCreated(TextureRoughness);
|
|
|
|
TextureRoughness->PreEditChange(nullptr);
|
|
TextureRoughness->PostEditChange();
|
|
TextureRoughness->MarkPackageDirty();
|
|
}
|
|
}
|
|
}
|
|
|
|
HAPI_ParmInfo ParmInfoRoughnessValue;
|
|
FHoudiniApi::ParmInfo_Init(&ParmInfoRoughnessValue);
|
|
HAPI_ParmId ParmNameRoughnessValueId =
|
|
FHoudiniEngineUtils::HapiFindParameterByNameOrTag( NodeInfo.id, HAPI_UNREAL_PARAM_VALUE_ROUGHNESS_0, ParmInfoRoughnessValue );
|
|
|
|
if ( ParmNameRoughnessValueId >= 0 )
|
|
{
|
|
GeneratingParameterName = TEXT( HAPI_UNREAL_PARAM_VALUE_ROUGHNESS_0 );
|
|
}
|
|
else
|
|
{
|
|
ParmNameRoughnessValueId =
|
|
FHoudiniEngineUtils::HapiFindParameterByNameOrTag( NodeInfo.id, HAPI_UNREAL_PARAM_VALUE_ROUGHNESS_1, ParmInfoRoughnessValue );
|
|
|
|
if ( ParmNameRoughnessValueId >= 0 )
|
|
GeneratingParameterName = TEXT( HAPI_UNREAL_PARAM_VALUE_ROUGHNESS_1 );
|
|
}
|
|
|
|
if ( !bExpressionCreated && ParmNameRoughnessValueId >= 0 )
|
|
{
|
|
// Roughness value is available.
|
|
|
|
float RoughnessValue = 0.0f;
|
|
|
|
if ( FHoudiniApi::GetParmFloatValues(
|
|
FHoudiniEngine::Get().GetSession(), NodeInfo.id, (float *) &RoughnessValue,
|
|
ParmInfoRoughnessValue.floatValuesIndex, 1 ) == HAPI_RESULT_SUCCESS )
|
|
{
|
|
UMaterialExpressionScalarParameter * ExpressionRoughnessValue =
|
|
Cast< UMaterialExpressionScalarParameter >( Material->Roughness.Expression );
|
|
|
|
// Clamp retrieved value.
|
|
RoughnessValue = FMath::Clamp< float >( RoughnessValue, 0.0f, 1.0f );
|
|
|
|
// Create color const expression and add it to material, if we don't have one.
|
|
if ( !ExpressionRoughnessValue )
|
|
{
|
|
// Otherwise new expression is of a different type.
|
|
if ( Material->Roughness.Expression )
|
|
{
|
|
Material->Roughness.Expression->ConditionalBeginDestroy();
|
|
Material->Roughness.Expression = nullptr;
|
|
}
|
|
|
|
ExpressionRoughnessValue = NewObject< UMaterialExpressionScalarParameter >(
|
|
Material, UMaterialExpressionScalarParameter::StaticClass(), NAME_None, ObjectFlag );
|
|
}
|
|
|
|
// Record generating parameter.
|
|
ExpressionRoughnessValue->Desc = GeneratingParameterName;
|
|
ExpressionRoughnessValue->ParameterName = *GeneratingParameterName;
|
|
|
|
ExpressionRoughnessValue->DefaultValue = RoughnessValue;
|
|
ExpressionRoughnessValue->SliderMin = 0.0f;
|
|
ExpressionRoughnessValue->SliderMax = 1.0f;
|
|
|
|
// Offset node placement.
|
|
ExpressionRoughnessValue->MaterialExpressionEditorX = FHoudiniEngineMaterialUtils::MaterialExpressionNodeX;
|
|
ExpressionRoughnessValue->MaterialExpressionEditorY = MaterialNodeY;
|
|
MaterialNodeY += FHoudiniEngineMaterialUtils::MaterialExpressionNodeStepY;
|
|
|
|
// Assign expression to material.
|
|
Material->Expressions.Add( ExpressionRoughnessValue );
|
|
Material->Roughness.Expression = ExpressionRoughnessValue;
|
|
|
|
bExpressionCreated = true;
|
|
}
|
|
}
|
|
|
|
return bExpressionCreated;
|
|
}
|
|
|
|
bool
|
|
FHoudiniEngineMaterialUtils::CreateMaterialComponentMetallic(
|
|
FHoudiniCookParams& HoudiniCookParams, const HAPI_NodeId& AssetId,
|
|
UMaterial * Material, const HAPI_MaterialInfo & MaterialInfo,
|
|
const HAPI_NodeInfo & NodeInfo, int32 & MaterialNodeY )
|
|
{
|
|
if (!Material || Material->IsPendingKill())
|
|
return false;
|
|
|
|
bool bExpressionCreated = false;
|
|
HAPI_Result Result = HAPI_RESULT_SUCCESS;
|
|
|
|
EBakeMode BakeMode = HoudiniCookParams.MaterialAndTextureBakeMode;
|
|
EObjectFlags ObjectFlag = ( BakeMode == EBakeMode::CookToTemp ) ? RF_NoFlags : RF_Standalone;
|
|
|
|
// Name of generating Houdini parameter.
|
|
FString GeneratingParameterName = TEXT( "" );
|
|
|
|
// Metallic texture creation parameters.
|
|
FCreateTexture2DParameters CreateTexture2DParameters;
|
|
CreateTexture2DParameters.SourceGuidHash = FGuid();
|
|
CreateTexture2DParameters.bUseAlpha = false;
|
|
CreateTexture2DParameters.CompressionSettings = TC_Grayscale;
|
|
CreateTexture2DParameters.bDeferCompression = true;
|
|
CreateTexture2DParameters.bSRGB = false;
|
|
|
|
// See if metallic texture is available.
|
|
HAPI_ParmId ParmNameMetallicId =
|
|
FHoudiniEngineUtils::HapiFindParameterByNameOrTag( NodeInfo.id, HAPI_UNREAL_PARAM_MAP_METALLIC );
|
|
|
|
if ( ParmNameMetallicId >= 0 )
|
|
{
|
|
GeneratingParameterName = TEXT( HAPI_UNREAL_PARAM_MAP_METALLIC );
|
|
}
|
|
|
|
if ( ParmNameMetallicId >= 0 )
|
|
{
|
|
TArray< char > ImageBuffer;
|
|
|
|
// Retrieve color plane.
|
|
if ( FHoudiniEngineMaterialUtils::HapiExtractImage(
|
|
ParmNameMetallicId, MaterialInfo, ImageBuffer,
|
|
HAPI_UNREAL_MATERIAL_TEXTURE_COLOR, HAPI_IMAGE_DATA_INT8, HAPI_IMAGE_PACKING_RGBA, true ) )
|
|
{
|
|
UMaterialExpressionTextureSampleParameter2D * ExpressionMetallic =
|
|
Cast< UMaterialExpressionTextureSampleParameter2D >( Material->Metallic.Expression );
|
|
|
|
UTexture2D * TextureMetallic = nullptr;
|
|
if ( ExpressionMetallic )
|
|
{
|
|
TextureMetallic = Cast< UTexture2D >( ExpressionMetallic->Texture );
|
|
}
|
|
else
|
|
{
|
|
// Otherwise new expression is of a different type.
|
|
if ( Material->Metallic.Expression )
|
|
{
|
|
Material->Metallic.Expression->ConditionalBeginDestroy();
|
|
Material->Metallic.Expression = nullptr;
|
|
}
|
|
}
|
|
|
|
UPackage * TextureMetallicPackage = nullptr;
|
|
if ( TextureMetallic )
|
|
TextureMetallicPackage = Cast< UPackage >( TextureMetallic->GetOuter() );
|
|
|
|
HAPI_ImageInfo ImageInfo;
|
|
FHoudiniApi::ImageInfo_Init(&ImageInfo);
|
|
Result = FHoudiniApi::GetImageInfo(
|
|
FHoudiniEngine::Get().GetSession(),
|
|
MaterialInfo.nodeId, &ImageInfo );
|
|
|
|
if ( Result == HAPI_RESULT_SUCCESS && ImageInfo.xRes > 0 && ImageInfo.yRes > 0 )
|
|
{
|
|
// Create texture.
|
|
FString TextureMetallicName;
|
|
bool bCreatedNewTextureMetallic = false;
|
|
|
|
// Create metallic texture package, if this is a new metallic texture.
|
|
if ( !TextureMetallicPackage )
|
|
{
|
|
TextureMetallicPackage = FHoudiniEngineBakeUtils::BakeCreateTexturePackageForComponent(
|
|
HoudiniCookParams,
|
|
MaterialInfo,
|
|
HAPI_UNREAL_PACKAGE_META_GENERATED_TEXTURE_METALLIC,
|
|
TextureMetallicName );
|
|
}
|
|
|
|
// Create metallic texture, if we need to create one.
|
|
if ( !TextureMetallic )
|
|
bCreatedNewTextureMetallic = true;
|
|
|
|
// Get the node path to add it to the meta data
|
|
FString NodePath;
|
|
GetUniqueMaterialShopName( AssetId, MaterialInfo.nodeId, NodePath );
|
|
|
|
// Reuse existing metallic texture, or create new one.
|
|
TextureMetallic = FHoudiniEngineMaterialUtils::CreateUnrealTexture(
|
|
TextureMetallic, ImageInfo,
|
|
TextureMetallicPackage, TextureMetallicName, ImageBuffer,
|
|
HAPI_UNREAL_PACKAGE_META_GENERATED_TEXTURE_METALLIC,
|
|
CreateTexture2DParameters,
|
|
TEXTUREGROUP_World, NodePath );
|
|
|
|
if ( BakeMode == EBakeMode::CookToTemp )
|
|
TextureMetallic->SetFlags( RF_Public | RF_Standalone );
|
|
|
|
// Create metallic sampling expression, if needed.
|
|
if ( !ExpressionMetallic )
|
|
ExpressionMetallic = NewObject< UMaterialExpressionTextureSampleParameter2D >(
|
|
Material, UMaterialExpressionTextureSampleParameter2D::StaticClass(), NAME_None, ObjectFlag );
|
|
|
|
// Record generating parameter.
|
|
ExpressionMetallic->Desc = GeneratingParameterName;
|
|
ExpressionMetallic->ParameterName = *GeneratingParameterName;
|
|
|
|
ExpressionMetallic->Texture = TextureMetallic;
|
|
ExpressionMetallic->SamplerType = SAMPLERTYPE_LinearGrayscale;
|
|
|
|
// Offset node placement.
|
|
ExpressionMetallic->MaterialExpressionEditorX = FHoudiniEngineMaterialUtils::MaterialExpressionNodeX;
|
|
ExpressionMetallic->MaterialExpressionEditorY = MaterialNodeY;
|
|
MaterialNodeY += FHoudiniEngineMaterialUtils::MaterialExpressionNodeStepY;
|
|
|
|
// Assign expression to material.
|
|
Material->Expressions.Add( ExpressionMetallic );
|
|
Material->Metallic.Expression = ExpressionMetallic;
|
|
|
|
bExpressionCreated = true;
|
|
|
|
// Propagate and trigger metallic texture updates.
|
|
if (bCreatedNewTextureMetallic)
|
|
FAssetRegistryModule::AssetCreated(TextureMetallic);
|
|
|
|
TextureMetallic->PreEditChange(nullptr);
|
|
TextureMetallic->PostEditChange();
|
|
TextureMetallic->MarkPackageDirty();
|
|
}
|
|
}
|
|
}
|
|
|
|
HAPI_ParmInfo ParmInfoMetallic;
|
|
FHoudiniApi::ParmInfo_Init(&ParmInfoMetallic);
|
|
HAPI_ParmId ParmNameMetallicValueIdx =
|
|
FHoudiniEngineUtils::HapiFindParameterByNameOrTag( NodeInfo.id, HAPI_UNREAL_PARAM_VALUE_METALLIC, ParmInfoMetallic );
|
|
|
|
if ( ParmNameMetallicValueIdx >= 0 )
|
|
GeneratingParameterName = TEXT( HAPI_UNREAL_PARAM_VALUE_METALLIC );
|
|
|
|
if ( !bExpressionCreated && ParmNameMetallicValueIdx >= 0 )
|
|
{
|
|
// Metallic value is available.
|
|
|
|
float MetallicValue = 0.0f;
|
|
|
|
if ( FHoudiniApi::GetParmFloatValues(
|
|
FHoudiniEngine::Get().GetSession(), NodeInfo.id, (float *) &MetallicValue,
|
|
ParmInfoMetallic.floatValuesIndex, 1 ) == HAPI_RESULT_SUCCESS )
|
|
{
|
|
UMaterialExpressionScalarParameter * ExpressionMetallicValue =
|
|
Cast< UMaterialExpressionScalarParameter >( Material->Metallic.Expression );
|
|
|
|
// Clamp retrieved value.
|
|
MetallicValue = FMath::Clamp< float >( MetallicValue, 0.0f, 1.0f );
|
|
|
|
// Create color const expression and add it to material, if we don't have one.
|
|
if ( !ExpressionMetallicValue )
|
|
{
|
|
// Otherwise new expression is of a different type.
|
|
if ( Material->Metallic.Expression )
|
|
{
|
|
Material->Metallic.Expression->ConditionalBeginDestroy();
|
|
Material->Metallic.Expression = nullptr;
|
|
}
|
|
|
|
ExpressionMetallicValue = NewObject< UMaterialExpressionScalarParameter >(
|
|
Material, UMaterialExpressionScalarParameter::StaticClass(), NAME_None, ObjectFlag );
|
|
}
|
|
|
|
// Record generating parameter.
|
|
ExpressionMetallicValue->Desc = GeneratingParameterName;
|
|
ExpressionMetallicValue->ParameterName = *GeneratingParameterName;
|
|
|
|
ExpressionMetallicValue->DefaultValue = MetallicValue;
|
|
ExpressionMetallicValue->SliderMin = 0.0f;
|
|
ExpressionMetallicValue->SliderMax = 1.0f;
|
|
|
|
// Offset node placement.
|
|
ExpressionMetallicValue->MaterialExpressionEditorX = FHoudiniEngineMaterialUtils::MaterialExpressionNodeX;
|
|
ExpressionMetallicValue->MaterialExpressionEditorY = MaterialNodeY;
|
|
MaterialNodeY += FHoudiniEngineMaterialUtils::MaterialExpressionNodeStepY;
|
|
|
|
// Assign expression to material.
|
|
Material->Expressions.Add( ExpressionMetallicValue );
|
|
Material->Metallic.Expression = ExpressionMetallicValue;
|
|
|
|
bExpressionCreated = true;
|
|
}
|
|
}
|
|
|
|
return bExpressionCreated;
|
|
}
|
|
|
|
|
|
bool
|
|
FHoudiniEngineMaterialUtils::CreateMaterialComponentEmissive(
|
|
FHoudiniCookParams& HoudiniCookParams, const HAPI_NodeId& AssetId,
|
|
UMaterial * Material, const HAPI_MaterialInfo & MaterialInfo,
|
|
const HAPI_NodeInfo & NodeInfo, int32 & MaterialNodeY )
|
|
{
|
|
if (!Material || Material->IsPendingKill())
|
|
return false;
|
|
|
|
bool bExpressionCreated = false;
|
|
HAPI_Result Result = HAPI_RESULT_SUCCESS;
|
|
|
|
EBakeMode BakeMode = HoudiniCookParams.MaterialAndTextureBakeMode;
|
|
EObjectFlags ObjectFlag = ( BakeMode == EBakeMode::CookToTemp ) ? RF_NoFlags : RF_Standalone;
|
|
|
|
// Name of generating Houdini parameter.
|
|
FString GeneratingParameterName = TEXT("");
|
|
|
|
// Emissive texture creation parameters.
|
|
FCreateTexture2DParameters CreateTexture2DParameters;
|
|
CreateTexture2DParameters.SourceGuidHash = FGuid();
|
|
CreateTexture2DParameters.bUseAlpha = false;
|
|
CreateTexture2DParameters.CompressionSettings = TC_Grayscale;
|
|
CreateTexture2DParameters.bDeferCompression = true;
|
|
CreateTexture2DParameters.bSRGB = false;
|
|
|
|
// See if emissive texture is available.
|
|
HAPI_ParmId ParmNameEmissiveId =
|
|
FHoudiniEngineUtils::HapiFindParameterByNameOrTag( NodeInfo.id, HAPI_UNREAL_PARAM_MAP_EMISSIVE );
|
|
|
|
if ( ParmNameEmissiveId >= 0 )
|
|
{
|
|
GeneratingParameterName = TEXT( HAPI_UNREAL_PARAM_MAP_EMISSIVE );
|
|
}
|
|
|
|
if ( ParmNameEmissiveId >= 0 )
|
|
{
|
|
TArray< char > ImageBuffer;
|
|
|
|
// Retrieve color plane.
|
|
if ( FHoudiniEngineMaterialUtils::HapiExtractImage(
|
|
ParmNameEmissiveId, MaterialInfo, ImageBuffer,
|
|
HAPI_UNREAL_MATERIAL_TEXTURE_COLOR, HAPI_IMAGE_DATA_INT8, HAPI_IMAGE_PACKING_RGBA, true ) )
|
|
{
|
|
UMaterialExpressionTextureSampleParameter2D * ExpressionEmissive =
|
|
Cast< UMaterialExpressionTextureSampleParameter2D >( Material->EmissiveColor.Expression );
|
|
|
|
UTexture2D * TextureEmissive = nullptr;
|
|
if ( ExpressionEmissive )
|
|
{
|
|
TextureEmissive = Cast< UTexture2D >( ExpressionEmissive->Texture );
|
|
}
|
|
else
|
|
{
|
|
// Otherwise new expression is of a different type.
|
|
if ( Material->EmissiveColor.Expression )
|
|
{
|
|
Material->EmissiveColor.Expression->ConditionalBeginDestroy();
|
|
Material->EmissiveColor.Expression = nullptr;
|
|
}
|
|
}
|
|
|
|
UPackage * TextureEmissivePackage = nullptr;
|
|
if ( TextureEmissive )
|
|
TextureEmissivePackage = Cast< UPackage >( TextureEmissive->GetOuter() );
|
|
|
|
HAPI_ImageInfo ImageInfo;
|
|
FHoudiniApi::ImageInfo_Init(&ImageInfo);
|
|
Result = FHoudiniApi::GetImageInfo(
|
|
FHoudiniEngine::Get().GetSession(),
|
|
MaterialInfo.nodeId, &ImageInfo );
|
|
|
|
if ( Result == HAPI_RESULT_SUCCESS && ImageInfo.xRes > 0 && ImageInfo.yRes > 0 )
|
|
{
|
|
// Create texture.
|
|
FString TextureEmissiveName;
|
|
bool bCreatedNewTextureEmissive = false;
|
|
|
|
// Create emissive texture package, if this is a new emissive texture.
|
|
if ( !TextureEmissivePackage )
|
|
{
|
|
TextureEmissivePackage = FHoudiniEngineBakeUtils::BakeCreateTexturePackageForComponent(
|
|
HoudiniCookParams,
|
|
MaterialInfo,
|
|
HAPI_UNREAL_PACKAGE_META_GENERATED_TEXTURE_EMISSIVE,
|
|
TextureEmissiveName );
|
|
}
|
|
|
|
// Create emissive texture, if we need to create one.
|
|
if ( !TextureEmissive )
|
|
bCreatedNewTextureEmissive = true;
|
|
|
|
// Get the node path to add it to the meta data
|
|
FString NodePath;
|
|
GetUniqueMaterialShopName( AssetId, MaterialInfo.nodeId, NodePath );
|
|
|
|
// Reuse existing emissive texture, or create new one.
|
|
TextureEmissive = FHoudiniEngineMaterialUtils::CreateUnrealTexture(
|
|
TextureEmissive, ImageInfo,
|
|
TextureEmissivePackage, TextureEmissiveName, ImageBuffer,
|
|
HAPI_UNREAL_PACKAGE_META_GENERATED_TEXTURE_EMISSIVE,
|
|
CreateTexture2DParameters,
|
|
TEXTUREGROUP_World, NodePath );
|
|
|
|
if ( BakeMode == EBakeMode::CookToTemp )
|
|
TextureEmissive->SetFlags( RF_Public | RF_Standalone );
|
|
|
|
// Create emissive sampling expression, if needed.
|
|
if ( !ExpressionEmissive )
|
|
ExpressionEmissive = NewObject< UMaterialExpressionTextureSampleParameter2D >(
|
|
Material, UMaterialExpressionTextureSampleParameter2D::StaticClass(), NAME_None, ObjectFlag );
|
|
|
|
// Record generating parameter.
|
|
ExpressionEmissive->Desc = GeneratingParameterName;
|
|
ExpressionEmissive->ParameterName = *GeneratingParameterName;
|
|
|
|
ExpressionEmissive->Texture = TextureEmissive;
|
|
ExpressionEmissive->SamplerType = SAMPLERTYPE_LinearGrayscale;
|
|
|
|
// Offset node placement.
|
|
ExpressionEmissive->MaterialExpressionEditorX = FHoudiniEngineMaterialUtils::MaterialExpressionNodeX;
|
|
ExpressionEmissive->MaterialExpressionEditorY = MaterialNodeY;
|
|
MaterialNodeY += FHoudiniEngineMaterialUtils::MaterialExpressionNodeStepY;
|
|
|
|
// Assign expression to material.
|
|
Material->Expressions.Add( ExpressionEmissive );
|
|
Material->EmissiveColor.Expression = ExpressionEmissive;
|
|
|
|
bExpressionCreated = true;
|
|
|
|
// Propagate and trigger metallic texture updates.
|
|
if (bCreatedNewTextureEmissive)
|
|
FAssetRegistryModule::AssetCreated(TextureEmissive);
|
|
|
|
TextureEmissive->PreEditChange(nullptr);
|
|
TextureEmissive->PostEditChange();
|
|
TextureEmissive->MarkPackageDirty();
|
|
}
|
|
}
|
|
}
|
|
|
|
HAPI_ParmInfo ParmInfoEmissive;
|
|
FHoudiniApi::ParmInfo_Init(&ParmInfoEmissive);
|
|
HAPI_ParmId ParmNameEmissiveValueId =
|
|
FHoudiniEngineUtils::HapiFindParameterByNameOrTag( NodeInfo.id, HAPI_UNREAL_PARAM_VALUE_EMISSIVE_0, ParmInfoEmissive );
|
|
|
|
if ( ParmNameEmissiveValueId >= 0 )
|
|
GeneratingParameterName = TEXT( HAPI_UNREAL_PARAM_VALUE_EMISSIVE_0 );
|
|
else
|
|
{
|
|
ParmNameEmissiveValueId =
|
|
FHoudiniEngineUtils::HapiFindParameterByNameOrTag( NodeInfo.id, HAPI_UNREAL_PARAM_VALUE_EMISSIVE_1, ParmInfoEmissive );
|
|
|
|
if ( ParmNameEmissiveValueId >= 0 )
|
|
GeneratingParameterName = TEXT( HAPI_UNREAL_PARAM_VALUE_EMISSIVE_1 );
|
|
}
|
|
|
|
if ( !bExpressionCreated && ParmNameEmissiveValueId >= 0 )
|
|
{
|
|
// Emissive color is available.
|
|
|
|
FLinearColor Color = FLinearColor::White;
|
|
|
|
if ( FHoudiniApi::GetParmFloatValues(
|
|
FHoudiniEngine::Get().GetSession(), NodeInfo.id, (float*)&Color.R,
|
|
ParmInfoEmissive.floatValuesIndex, ParmInfoEmissive.size ) == HAPI_RESULT_SUCCESS )
|
|
{
|
|
if ( ParmInfoEmissive.size == 3 )
|
|
Color.A = 1.0f;
|
|
|
|
UMaterialExpressionConstant4Vector * ExpressionEmissiveColor =
|
|
Cast< UMaterialExpressionConstant4Vector >( Material->EmissiveColor.Expression );
|
|
|
|
// Create color const expression and add it to material, if we don't have one.
|
|
if ( !ExpressionEmissiveColor )
|
|
{
|
|
// Otherwise new expression is of a different type.
|
|
if ( Material->EmissiveColor.Expression )
|
|
{
|
|
Material->EmissiveColor.Expression->ConditionalBeginDestroy();
|
|
Material->EmissiveColor.Expression = nullptr;
|
|
}
|
|
|
|
ExpressionEmissiveColor = NewObject< UMaterialExpressionConstant4Vector >(
|
|
Material, UMaterialExpressionConstant4Vector::StaticClass(), NAME_None, ObjectFlag );
|
|
}
|
|
|
|
// Record generating parameter.
|
|
ExpressionEmissiveColor->Desc = GeneratingParameterName;
|
|
if ( ExpressionEmissiveColor->CanRenameNode() )
|
|
ExpressionEmissiveColor->SetEditableName( *GeneratingParameterName );
|
|
|
|
ExpressionEmissiveColor->Constant = Color;
|
|
|
|
// Offset node placement.
|
|
ExpressionEmissiveColor->MaterialExpressionEditorX = FHoudiniEngineMaterialUtils::MaterialExpressionNodeX;
|
|
ExpressionEmissiveColor->MaterialExpressionEditorY = MaterialNodeY;
|
|
MaterialNodeY += FHoudiniEngineMaterialUtils::MaterialExpressionNodeStepY;
|
|
|
|
// Assign expression to material.
|
|
Material->Expressions.Add( ExpressionEmissiveColor );
|
|
Material->EmissiveColor.Expression = ExpressionEmissiveColor;
|
|
|
|
bExpressionCreated = true;
|
|
}
|
|
}
|
|
|
|
return bExpressionCreated;
|
|
}
|
|
|
|
UTexture2D *
|
|
FHoudiniEngineMaterialUtils::CreateUnrealTexture(
|
|
UTexture2D * ExistingTexture, const HAPI_ImageInfo & ImageInfo,
|
|
UPackage * Package, const FString & TextureName,
|
|
const TArray< char > & ImageBuffer, const FString & TextureType,
|
|
const FCreateTexture2DParameters & TextureParameters, TextureGroup LODGroup, const FString& NodePath )
|
|
{
|
|
if (!Package || Package->IsPendingKill())
|
|
return nullptr;
|
|
|
|
UTexture2D * Texture = nullptr;
|
|
if ( ExistingTexture )
|
|
{
|
|
Texture = ExistingTexture;
|
|
}
|
|
else
|
|
{
|
|
// Create new texture object.
|
|
Texture = NewObject< UTexture2D >(
|
|
Package, UTexture2D::StaticClass(), *TextureName,
|
|
RF_Transactional );
|
|
|
|
// Assign texture group.
|
|
Texture->LODGroup = LODGroup;
|
|
}
|
|
|
|
// Add/Update meta information to package.
|
|
FHoudiniEngineBakeUtils::AddHoudiniMetaInformationToPackage(
|
|
Package, Texture, HAPI_UNREAL_PACKAGE_META_GENERATED_OBJECT, TEXT( "true" ) );
|
|
FHoudiniEngineBakeUtils::AddHoudiniMetaInformationToPackage(
|
|
Package, Texture, HAPI_UNREAL_PACKAGE_META_GENERATED_NAME, *TextureName );
|
|
FHoudiniEngineBakeUtils::AddHoudiniMetaInformationToPackage(
|
|
Package, Texture, HAPI_UNREAL_PACKAGE_META_GENERATED_TEXTURE_TYPE, *TextureType );
|
|
FHoudiniEngineBakeUtils::AddHoudiniMetaInformationToPackage(
|
|
Package, Texture, HAPI_UNREAL_PACKAGE_META_NODE_PATH, *NodePath );
|
|
|
|
// Initialize texture source.
|
|
Texture->Source.Init( ImageInfo.xRes, ImageInfo.yRes, 1, 1, TSF_BGRA8 );
|
|
|
|
// Lock the texture.
|
|
uint8 * MipData = Texture->Source.LockMip( 0 );
|
|
|
|
// Create base map.
|
|
uint8* DestPtr = nullptr;
|
|
uint32 SrcWidth = ImageInfo.xRes;
|
|
uint32 SrcHeight = ImageInfo.yRes;
|
|
const char * SrcData = &ImageBuffer[ 0 ];
|
|
|
|
for ( uint32 y = 0; y < SrcHeight; y++ )
|
|
{
|
|
DestPtr = &MipData[ ( SrcHeight - 1 - y ) * SrcWidth * sizeof( FColor ) ];
|
|
|
|
for ( uint32 x = 0; x < SrcWidth; x++ )
|
|
{
|
|
uint32 DataOffset = y * SrcWidth * 4 + x * 4;
|
|
|
|
*DestPtr++ = *(uint8*)( SrcData + DataOffset + 2 ); // B
|
|
*DestPtr++ = *(uint8*)( SrcData + DataOffset + 1 ); // G
|
|
*DestPtr++ = *(uint8*)( SrcData + DataOffset + 0 ); // R
|
|
|
|
if ( TextureParameters.bUseAlpha )
|
|
*DestPtr++ = *(uint8*)( SrcData + DataOffset + 3 ); // A
|
|
else
|
|
*DestPtr++ = 0xFF;
|
|
}
|
|
}
|
|
|
|
bool bHasAlphaValue = false;
|
|
if ( TextureParameters.bUseAlpha )
|
|
{
|
|
// See if there is an actual alpha value in the texture or if we can ignore the texture alpha
|
|
for ( uint32 y = 0; y < SrcHeight; y++ )
|
|
{
|
|
for ( uint32 x = 0; x < SrcWidth; x++ )
|
|
{
|
|
uint32 DataOffset = y * SrcWidth * 4 + x * 4;
|
|
if (*(uint8*)(SrcData + DataOffset + 3) != 0xFF)
|
|
{
|
|
bHasAlphaValue = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( bHasAlphaValue )
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Unlock the texture.
|
|
Texture->Source.UnlockMip( 0 );
|
|
|
|
// Texture creation parameters.
|
|
Texture->SRGB = TextureParameters.bSRGB;
|
|
Texture->CompressionSettings = TextureParameters.CompressionSettings;
|
|
Texture->CompressionNoAlpha = !bHasAlphaValue;
|
|
Texture->DeferCompression = TextureParameters.bDeferCompression;
|
|
|
|
// Set the Source Guid/Hash if specified.
|
|
/*
|
|
if ( TextureParameters.SourceGuidHash.IsValid() )
|
|
{
|
|
Texture->Source.SetId( TextureParameters.SourceGuidHash, true );
|
|
}
|
|
*/
|
|
|
|
Texture->PostEditChange();
|
|
|
|
return Texture;
|
|
}
|
|
|
|
#endif
|
|
|
|
bool
|
|
FHoudiniEngineMaterialUtils::GetUniqueMaterialShopName( HAPI_NodeId AssetId, HAPI_NodeId MaterialId, FString & Name )
|
|
{
|
|
if ( AssetId < 0 || MaterialId < 0 )
|
|
return false;
|
|
|
|
HAPI_AssetInfo AssetInfo;
|
|
FHoudiniApi::AssetInfo_Init(&AssetInfo);
|
|
HOUDINI_CHECK_ERROR_RETURN( FHoudiniApi::GetAssetInfo(
|
|
FHoudiniEngine::Get().GetSession(), AssetId, &AssetInfo ), false );
|
|
|
|
HAPI_MaterialInfo MaterialInfo;
|
|
FHoudiniApi::MaterialInfo_Init(&MaterialInfo);
|
|
HOUDINI_CHECK_ERROR_RETURN( FHoudiniApi::GetMaterialInfo(
|
|
FHoudiniEngine::Get().GetSession(), MaterialId,
|
|
&MaterialInfo ), false );
|
|
|
|
HAPI_NodeInfo AssetNodeInfo;
|
|
FHoudiniApi::NodeInfo_Init(&AssetNodeInfo);
|
|
HOUDINI_CHECK_ERROR_RETURN( FHoudiniApi::GetNodeInfo(
|
|
FHoudiniEngine::Get().GetSession(), AssetInfo.nodeId,
|
|
&AssetNodeInfo ), false );
|
|
|
|
HAPI_NodeInfo MaterialNodeInfo;
|
|
FHoudiniApi::NodeInfo_Init(&MaterialNodeInfo);
|
|
HOUDINI_CHECK_ERROR_RETURN( FHoudiniApi::GetNodeInfo(
|
|
FHoudiniEngine::Get().GetSession(), MaterialInfo.nodeId,
|
|
&MaterialNodeInfo ), false );
|
|
|
|
FString AssetNodeName = TEXT( "" );
|
|
FString MaterialNodeName = TEXT( "" );
|
|
|
|
{
|
|
FHoudiniEngineString HoudiniEngineString( AssetNodeInfo.internalNodePathSH );
|
|
if ( !HoudiniEngineString.ToFString( AssetNodeName ) )
|
|
return false;
|
|
}
|
|
|
|
{
|
|
FHoudiniEngineString HoudiniEngineString( MaterialNodeInfo.internalNodePathSH );
|
|
if ( !HoudiniEngineString.ToFString( MaterialNodeName ) )
|
|
return false;
|
|
}
|
|
|
|
if ( AssetNodeName.Len() > 0 && MaterialNodeName.Len() > 0 )
|
|
{
|
|
// Remove AssetNodeName part from MaterialNodeName. Extra position is for separator.
|
|
Name = MaterialNodeName.Mid( AssetNodeName.Len() + 1 );
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
UMaterialExpression *
|
|
FHoudiniEngineMaterialUtils::MaterialLocateExpression( UMaterialExpression * Expression, UClass * ExpressionClass )
|
|
{
|
|
if ( !Expression )
|
|
return nullptr;
|
|
#if WITH_EDITOR
|
|
if ( ExpressionClass == Expression->GetClass() )
|
|
return Expression;
|
|
|
|
|
|
// If this is a channel multiply expression, we can recurse.
|
|
UMaterialExpressionMultiply * MaterialExpressionMultiply = Cast< UMaterialExpressionMultiply >( Expression );
|
|
if ( MaterialExpressionMultiply )
|
|
{
|
|
{
|
|
UMaterialExpression * MaterialExpression = MaterialExpressionMultiply->A.Expression;
|
|
if ( MaterialExpression )
|
|
{
|
|
if ( MaterialExpression->GetClass() == ExpressionClass )
|
|
return MaterialExpression;
|
|
|
|
MaterialExpression = FHoudiniEngineMaterialUtils::MaterialLocateExpression(
|
|
Cast< UMaterialExpressionMultiply >( MaterialExpression ), ExpressionClass );
|
|
|
|
if ( MaterialExpression )
|
|
return MaterialExpression;
|
|
}
|
|
}
|
|
|
|
{
|
|
UMaterialExpression * MaterialExpression = MaterialExpressionMultiply->B.Expression;
|
|
if ( MaterialExpression )
|
|
{
|
|
if ( MaterialExpression->GetClass() == ExpressionClass )
|
|
return MaterialExpression;
|
|
|
|
MaterialExpression = FHoudiniEngineMaterialUtils::MaterialLocateExpression(
|
|
Cast< UMaterialExpressionMultiply >( MaterialExpression ), ExpressionClass );
|
|
|
|
if ( MaterialExpression )
|
|
return MaterialExpression;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
|
|
bool
|
|
FHoudiniEngineMaterialUtils::CreateMaterialInstances(
|
|
const FHoudiniGeoPartObject& HoudiniGeoPartObject, FHoudiniCookParams& CookParams,
|
|
UMaterialInstance*& CreatedMaterialInstance, UMaterialInterface*& OriginalMaterialInterface,
|
|
std::string AttributeName, int32 MaterialIndex)
|
|
{
|
|
#if WITH_EDITOR
|
|
if ( !HoudiniGeoPartObject.IsValid() )
|
|
return false;
|
|
|
|
// First, make sure this geopartObj has a material instance attribute
|
|
if ( !FHoudiniEngineUtils::HapiCheckAttributeExists(
|
|
HoudiniGeoPartObject.AssetId, HoudiniGeoPartObject.ObjectId,
|
|
HoudiniGeoPartObject.GeoId, HoudiniGeoPartObject.PartId,
|
|
AttributeName.c_str() ) )
|
|
return false;
|
|
|
|
// Get the material instance attribute info
|
|
HAPI_AttributeInfo AttribMaterialInstances;
|
|
FHoudiniApi::AttributeInfo_Init(&AttribMaterialInstances);
|
|
//FMemory::Memzero< HAPI_AttributeInfo >( AttribMaterialInstances );
|
|
|
|
TArray< FString > MaterialInstances;
|
|
FHoudiniEngineUtils::HapiGetAttributeDataAsString(
|
|
HoudiniGeoPartObject.AssetId, HoudiniGeoPartObject.ObjectId,
|
|
HoudiniGeoPartObject.GeoId, HoudiniGeoPartObject.PartId,
|
|
AttributeName.c_str(),
|
|
AttribMaterialInstances, MaterialInstances );
|
|
|
|
// No material instance attribute
|
|
if ( !AttribMaterialInstances.exists || MaterialInstances.Num() <= 0 )
|
|
return false;
|
|
|
|
// Get the material name from the material_instance attribute
|
|
// Since the material instance attribute can be set per primitive, it's going to be very difficult to know
|
|
// exactly where to look for the nth material instance, so we'll have to iterate on them to find it.
|
|
// In order for the material slot to be created, the material instance attribute had to be different,
|
|
// so we'll use this to hopefully fetch the right value.
|
|
// This is pretty hacky and we should probably require an extra material_instance_index attribute instead.
|
|
// but still a better solution than ignore all the material slots but the first
|
|
int32 MaterialIndexToAttributeIndex = 0;
|
|
if ( MaterialIndex > 0 && AttribMaterialInstances.owner == HAPI_ATTROWNER_PRIM )
|
|
{
|
|
int32 CurrentMaterialIndex = 0;
|
|
FString CurrentMatName = MaterialInstances[ 0 ];
|
|
for ( int32 n = 0; n < MaterialInstances.Num(); n++ )
|
|
{
|
|
if ( MaterialInstances[ n ].Equals( CurrentMatName ) )
|
|
continue;
|
|
|
|
CurrentMatName = MaterialInstances[ n ];
|
|
CurrentMaterialIndex++;
|
|
|
|
if ( CurrentMaterialIndex == MaterialIndex )
|
|
{
|
|
MaterialIndexToAttributeIndex = n;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
const FString & MaterialName = MaterialInstances[ MaterialIndexToAttributeIndex ];
|
|
if ( MaterialName.IsEmpty() )
|
|
return false;
|
|
|
|
// Trying to find the material we want to create an instance of
|
|
OriginalMaterialInterface = Cast< UMaterialInterface >(
|
|
StaticLoadObject( UMaterialInterface::StaticClass(), nullptr, *MaterialName, nullptr, LOAD_NoWarn, nullptr ) );
|
|
|
|
// The source material wasn't found
|
|
if ( !OriginalMaterialInterface || OriginalMaterialInterface->IsPendingKill() )
|
|
return false;
|
|
|
|
// Create/Retrieve the package for the MI
|
|
FString MaterialInstanceName;
|
|
FString MaterialInstanceNamePrefix = UPackageTools::SanitizePackageName(OriginalMaterialInterface->GetName() + TEXT("_instance_") + FString::FromInt(MaterialIndex) );
|
|
|
|
// See if we can find the package in the cooked temp package cache
|
|
UPackage * MaterialInstancePackage = nullptr;
|
|
TWeakObjectPtr< UPackage > * FoundPointer = CookParams.CookedTemporaryPackages->Find( MaterialInstanceNamePrefix );
|
|
if ( FoundPointer && (*FoundPointer).IsValid() )
|
|
{
|
|
// We found an already existing package for the M_I
|
|
MaterialInstancePackage = (*FoundPointer).Get();
|
|
MaterialInstanceName = MaterialInstancePackage->GetName();
|
|
}
|
|
else
|
|
{
|
|
// We Couldnt find the corresponding M_I package, so create a new one
|
|
MaterialInstancePackage = FHoudiniEngineBakeUtils::BakeCreateTextureOrMaterialPackageForComponent(
|
|
CookParams, MaterialInstanceNamePrefix, MaterialInstanceName );
|
|
}
|
|
|
|
// Couldn't create a package for the Material Instance
|
|
if ( !MaterialInstancePackage )
|
|
return false;
|
|
|
|
//UMaterialInterface * MaterialInstanceInterface = Cast< UMaterialInterface >(
|
|
// StaticLoadObject(UMaterialInterface::StaticClass(), nullptr, *MaterialInstanceNameString, nullptr, LOAD_NoWarn, nullptr));
|
|
// Trying to load the material instance from the package
|
|
bool bNewMaterialCreated = false;
|
|
UMaterialInstanceConstant* NewMaterialInstance = LoadObject<UMaterialInstanceConstant>( MaterialInstancePackage, *MaterialInstanceName, nullptr, LOAD_None, nullptr );
|
|
if ( !NewMaterialInstance )
|
|
{
|
|
// Factory to create materials.
|
|
UMaterialInstanceConstantFactoryNew* MaterialInstanceFactory = NewObject< UMaterialInstanceConstantFactoryNew >();
|
|
if ( !MaterialInstanceFactory )
|
|
return false;
|
|
|
|
// Create the new material instance
|
|
MaterialInstanceFactory->AddToRoot();
|
|
MaterialInstanceFactory->InitialParent = OriginalMaterialInterface;
|
|
NewMaterialInstance = ( UMaterialInstanceConstant* )MaterialInstanceFactory->FactoryCreateNew(
|
|
UMaterialInstanceConstant::StaticClass(), MaterialInstancePackage, FName( *MaterialInstanceName ),
|
|
RF_Public | RF_Standalone, NULL, GWarn );
|
|
|
|
if ( NewMaterialInstance )
|
|
bNewMaterialCreated = true;
|
|
|
|
MaterialInstanceFactory->RemoveFromRoot();
|
|
|
|
/*
|
|
// Creating a new material instance
|
|
NewMaterialInstance = NewObject< UMaterialInstanceConstant >( MaterialInstancePackage, FName( *MaterialInstanceName ), RF_Public );
|
|
checkf( NewMaterialInstance, TEXT( "Failed to create instanced material" ) );
|
|
NewMaterialInstance->Parent = Material;
|
|
|
|
// Notify registry that we have created a new duplicate material.
|
|
FAssetRegistryModule::AssetCreated( NewMaterialInstance );
|
|
|
|
// Dirty the material package.
|
|
NewMaterialInstance->MarkPackageDirty();
|
|
|
|
// Reset any derived state
|
|
//MaterialInstance->ForceRecompileForRendering();
|
|
*/
|
|
}
|
|
|
|
if ( !OriginalMaterialInterface || !NewMaterialInstance )
|
|
return false;
|
|
|
|
// Update context for generated materials (will trigger when object goes out of scope).
|
|
FMaterialUpdateContext MaterialUpdateContext;
|
|
|
|
bool bModifiedMaterialParameters = false;
|
|
// See if we need to override some of the material instance's parameters
|
|
TArray< UGenericAttribute > AllMatParams;
|
|
// Get the detail material parameters
|
|
int ParamCount = FHoudiniEngineUtils::GetGenericAttributeList( HoudiniGeoPartObject, HAPI_UNREAL_ATTRIB_GENERIC_MAT_PARAM_PREFIX, AllMatParams, HAPI_ATTROWNER_DETAIL );
|
|
// Then the primitive material parameters
|
|
ParamCount += FHoudiniEngineUtils::GetGenericAttributeList( HoudiniGeoPartObject, HAPI_UNREAL_ATTRIB_GENERIC_MAT_PARAM_PREFIX, AllMatParams, HAPI_ATTROWNER_PRIM, MaterialIndexToAttributeIndex );
|
|
for ( int32 ParamIdx = 0; ParamIdx < AllMatParams.Num(); ParamIdx++ )
|
|
{
|
|
// Try to update the material instance parameter corresponding to the attribute
|
|
if ( UpdateMaterialInstanceParameter( AllMatParams[ ParamIdx ], NewMaterialInstance, CookParams ) )
|
|
bModifiedMaterialParameters = true;
|
|
}
|
|
|
|
// Schedule this material for update if needed.
|
|
if ( bNewMaterialCreated || bModifiedMaterialParameters )
|
|
MaterialUpdateContext.AddMaterialInstance( NewMaterialInstance );
|
|
|
|
if ( bNewMaterialCreated )
|
|
{
|
|
// Add meta information to this package.
|
|
FHoudiniEngineBakeUtils::AddHoudiniMetaInformationToPackage(
|
|
MaterialInstancePackage, NewMaterialInstance, HAPI_UNREAL_PACKAGE_META_GENERATED_OBJECT, TEXT( "true" ) );
|
|
FHoudiniEngineBakeUtils::AddHoudiniMetaInformationToPackage(
|
|
MaterialInstancePackage, NewMaterialInstance, HAPI_UNREAL_PACKAGE_META_GENERATED_NAME, *MaterialInstanceName );
|
|
|
|
// Notify registry that we have created a new duplicate material.
|
|
FAssetRegistryModule::AssetCreated( NewMaterialInstance );
|
|
}
|
|
|
|
if ( bNewMaterialCreated || bModifiedMaterialParameters )
|
|
{
|
|
// Dirty the material
|
|
NewMaterialInstance->MarkPackageDirty();
|
|
|
|
// Update the material instance
|
|
NewMaterialInstance->InitStaticPermutation();
|
|
NewMaterialInstance->PreEditChange(nullptr);
|
|
NewMaterialInstance->PostEditChange();
|
|
/*
|
|
// Automatically save the package to avoid further issue
|
|
MaterialInstancePackage->SetDirtyFlag( true );
|
|
MaterialInstancePackage->FullyLoad();
|
|
UPackage::SavePackage(
|
|
MaterialInstancePackage, nullptr, EObjectFlags::RF_Public | EObjectFlags::RF_Standalone,
|
|
*FPackageName::LongPackageNameToFilename( MaterialInstancePackage->GetName(), FPackageName::GetAssetPackageExtension() ) );
|
|
*/
|
|
}
|
|
|
|
// Update the return pointers
|
|
CreatedMaterialInstance = NewMaterialInstance;
|
|
|
|
return true;
|
|
#else
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
bool
|
|
FHoudiniEngineMaterialUtils::UpdateMaterialInstanceParameter( UGenericAttribute MaterialParameter, UMaterialInstanceConstant* MaterialInstance, FHoudiniCookParams& CookParams )
|
|
{
|
|
bool bParameterUpdated = false;
|
|
|
|
#if WITH_EDITOR
|
|
if ( !MaterialInstance )
|
|
return false;
|
|
|
|
if ( MaterialParameter.AttributeName.IsEmpty() )
|
|
return false;
|
|
|
|
// The default material instance parameters needs to be handled manually as they cant be changed via generic SetParameters functions
|
|
if ( MaterialParameter.AttributeName.Compare( "CastShadowAsMasked", ESearchCase::IgnoreCase ) == 0 )
|
|
{
|
|
bool Value = MaterialParameter.GetBoolValue();
|
|
|
|
// Update the parameter value only if necessary
|
|
if ( MaterialInstance->GetOverrideCastShadowAsMasked() && ( MaterialInstance->GetCastShadowAsMasked() == Value ) )
|
|
return false;
|
|
|
|
MaterialInstance->SetOverrideCastShadowAsMasked( true );
|
|
MaterialInstance->SetCastShadowAsMasked( Value );
|
|
bParameterUpdated = true;
|
|
}
|
|
else if ( MaterialParameter.AttributeName.Compare( "EmissiveBoost", ESearchCase::IgnoreCase ) == 0 )
|
|
{
|
|
float Value = (float)MaterialParameter.GetDoubleValue();
|
|
|
|
// Update the parameter value only if necessary
|
|
if ( MaterialInstance->GetOverrideEmissiveBoost() && ( MaterialInstance->GetEmissiveBoost() == Value ) )
|
|
return false;
|
|
|
|
MaterialInstance->SetOverrideEmissiveBoost( true );
|
|
MaterialInstance->SetEmissiveBoost( Value );
|
|
bParameterUpdated = true;
|
|
}
|
|
else if ( MaterialParameter.AttributeName.Compare( "DiffuseBoost", ESearchCase::IgnoreCase ) == 0 )
|
|
{
|
|
float Value = (float)MaterialParameter.GetDoubleValue();
|
|
|
|
// Update the parameter value only if necessary
|
|
if ( MaterialInstance->GetOverrideDiffuseBoost() && ( MaterialInstance->GetDiffuseBoost() == Value ) )
|
|
return false;
|
|
|
|
MaterialInstance->SetOverrideDiffuseBoost( true );
|
|
MaterialInstance->SetDiffuseBoost( Value );
|
|
bParameterUpdated = true;
|
|
}
|
|
else if ( MaterialParameter.AttributeName.Compare( "ExportResolutionScale", ESearchCase::IgnoreCase ) == 0 )
|
|
{
|
|
float Value = (float)MaterialParameter.GetDoubleValue();
|
|
|
|
// Update the parameter value only if necessary
|
|
if (MaterialInstance->GetOverrideExportResolutionScale() && ( MaterialInstance->GetExportResolutionScale() == Value ) )
|
|
return false;
|
|
|
|
MaterialInstance->SetOverrideExportResolutionScale( true );
|
|
MaterialInstance->SetExportResolutionScale( Value );
|
|
bParameterUpdated = true;
|
|
}
|
|
else if ( MaterialParameter.AttributeName.Compare( "OpacityMaskClipValue", ESearchCase::IgnoreCase ) == 0 )
|
|
{
|
|
float Value = (float)MaterialParameter.GetDoubleValue();
|
|
|
|
// Update the parameter value only if necessary
|
|
if ( MaterialInstance->BasePropertyOverrides.bOverride_OpacityMaskClipValue && ( MaterialInstance->BasePropertyOverrides.OpacityMaskClipValue == Value ) )
|
|
return false;
|
|
|
|
MaterialInstance->BasePropertyOverrides.bOverride_OpacityMaskClipValue = true;
|
|
MaterialInstance->BasePropertyOverrides.OpacityMaskClipValue = Value;
|
|
bParameterUpdated = true;
|
|
}
|
|
else if ( MaterialParameter.AttributeName.Compare( "BlendMode", ESearchCase::IgnoreCase ) == 0 )
|
|
{
|
|
EBlendMode EnumValue = (EBlendMode)MaterialParameter.GetIntValue();
|
|
if ( MaterialParameter.AttributeType == HAPI_STORAGETYPE_STRING )
|
|
{
|
|
FString StringValue = MaterialParameter.GetStringValue();
|
|
if ( StringValue.Compare( "Opaque", ESearchCase::IgnoreCase ) == 0 )
|
|
EnumValue = EBlendMode::BLEND_Opaque;
|
|
else if ( StringValue.Compare( "Masked", ESearchCase::IgnoreCase ) == 0 )
|
|
EnumValue = EBlendMode::BLEND_Masked;
|
|
else if ( StringValue.Compare( "Translucent", ESearchCase::IgnoreCase ) == 0 )
|
|
EnumValue = EBlendMode::BLEND_Translucent;
|
|
else if ( StringValue.Compare( "Additive", ESearchCase::IgnoreCase ) == 0 )
|
|
EnumValue = EBlendMode::BLEND_Additive;
|
|
else if ( StringValue.Compare( "Modulate", ESearchCase::IgnoreCase ) == 0 )
|
|
EnumValue = EBlendMode::BLEND_Modulate;
|
|
else if ( StringValue.StartsWith( "Alpha", ESearchCase::IgnoreCase ) )
|
|
EnumValue = EBlendMode::BLEND_AlphaComposite;
|
|
}
|
|
|
|
// Update the parameter value only if necessary
|
|
if ( MaterialInstance->BasePropertyOverrides.bOverride_BlendMode && ( MaterialInstance->BasePropertyOverrides.BlendMode == EnumValue ) )
|
|
return false;
|
|
|
|
MaterialInstance->BasePropertyOverrides.bOverride_BlendMode = true;
|
|
MaterialInstance->BasePropertyOverrides.BlendMode = EnumValue;
|
|
bParameterUpdated = true;
|
|
}
|
|
else if ( MaterialParameter.AttributeName.Compare( "ShadingModel", ESearchCase::IgnoreCase ) == 0 )
|
|
{
|
|
EMaterialShadingModel EnumValue = (EMaterialShadingModel)MaterialParameter.GetIntValue();
|
|
if ( MaterialParameter.AttributeType == HAPI_STORAGETYPE_STRING )
|
|
{
|
|
FString StringValue = MaterialParameter.GetStringValue();
|
|
if ( StringValue.Compare( "Unlit", ESearchCase::IgnoreCase ) == 0 )
|
|
EnumValue = EMaterialShadingModel::MSM_Unlit;
|
|
else if ( StringValue.StartsWith( "Default", ESearchCase::IgnoreCase ) )
|
|
EnumValue = EMaterialShadingModel::MSM_DefaultLit;
|
|
else if ( StringValue.Compare( "Subsurface", ESearchCase::IgnoreCase ) == 0 )
|
|
EnumValue = EMaterialShadingModel::MSM_Subsurface;
|
|
else if ( StringValue.StartsWith( "Preintegrated", ESearchCase::IgnoreCase ) )
|
|
EnumValue = EMaterialShadingModel::MSM_PreintegratedSkin;
|
|
else if ( StringValue.StartsWith( "Clear", ESearchCase::IgnoreCase ) )
|
|
EnumValue = EMaterialShadingModel::MSM_ClearCoat;
|
|
else if ( StringValue.Compare( "SubsurfaceProfile", ESearchCase::IgnoreCase ) == 0 )
|
|
EnumValue = EMaterialShadingModel::MSM_SubsurfaceProfile;
|
|
else if ( StringValue.Compare( "TwoSidedFoliage", ESearchCase::IgnoreCase ) == 0 )
|
|
EnumValue = EMaterialShadingModel::MSM_TwoSidedFoliage;
|
|
else if ( StringValue.Compare( "Hair", ESearchCase::IgnoreCase ) == 0 )
|
|
EnumValue = EMaterialShadingModel::MSM_Hair;
|
|
else if ( StringValue.Compare( "Cloth", ESearchCase::IgnoreCase ) == 0 )
|
|
EnumValue = EMaterialShadingModel::MSM_Cloth;
|
|
else if ( StringValue.Compare( "Eye", ESearchCase::IgnoreCase ) == 0 )
|
|
EnumValue = EMaterialShadingModel::MSM_Eye;
|
|
}
|
|
|
|
// Update the parameter value only if necessary
|
|
if ( MaterialInstance->BasePropertyOverrides.bOverride_ShadingModel && ( MaterialInstance->BasePropertyOverrides.ShadingModel == EnumValue ) )
|
|
return false;
|
|
|
|
MaterialInstance->BasePropertyOverrides.bOverride_ShadingModel = true;
|
|
MaterialInstance->BasePropertyOverrides.ShadingModel = EnumValue;
|
|
bParameterUpdated = true;
|
|
}
|
|
else if ( MaterialParameter.AttributeName.Compare( "TwoSided", ESearchCase::IgnoreCase ) == 0 )
|
|
{
|
|
bool Value = MaterialParameter.GetBoolValue();
|
|
|
|
// Update the parameter value only if necessary
|
|
if ( MaterialInstance->BasePropertyOverrides.bOverride_TwoSided && ( MaterialInstance->BasePropertyOverrides.TwoSided == Value ) )
|
|
return false;
|
|
|
|
MaterialInstance->BasePropertyOverrides.bOverride_TwoSided = true;
|
|
MaterialInstance->BasePropertyOverrides.TwoSided = Value;
|
|
bParameterUpdated = true;
|
|
}
|
|
else if ( MaterialParameter.AttributeName.Compare( "DitheredLODTransition", ESearchCase::IgnoreCase ) == 0 )
|
|
{
|
|
bool Value = MaterialParameter.GetBoolValue();
|
|
|
|
// Update the parameter value only if necessary
|
|
if ( MaterialInstance->BasePropertyOverrides.bOverride_DitheredLODTransition && ( MaterialInstance->BasePropertyOverrides.DitheredLODTransition == Value ) )
|
|
return false;
|
|
|
|
MaterialInstance->BasePropertyOverrides.bOverride_DitheredLODTransition = true;
|
|
MaterialInstance->BasePropertyOverrides.DitheredLODTransition = Value;
|
|
bParameterUpdated = true;
|
|
}
|
|
else if ( MaterialParameter.AttributeName.Compare( "PhysMaterial", ESearchCase::IgnoreCase ) == 0 )
|
|
{
|
|
// Try to load a Material corresponding to the parameter value
|
|
FString ParamValue = MaterialParameter.GetStringValue();
|
|
UPhysicalMaterial* FoundPhysMaterial = Cast< UPhysicalMaterial >(
|
|
StaticLoadObject( UPhysicalMaterial::StaticClass(), nullptr, *ParamValue, nullptr, LOAD_NoWarn, nullptr ) );
|
|
|
|
// Update the parameter value if necessary
|
|
if (!FoundPhysMaterial || (MaterialInstance->PhysMaterial == FoundPhysMaterial))
|
|
return false;
|
|
|
|
MaterialInstance->PhysMaterial = FoundPhysMaterial;
|
|
bParameterUpdated = true;
|
|
}
|
|
|
|
if (bParameterUpdated)
|
|
return true;
|
|
|
|
// Handling custom parameters
|
|
FName CurrentMatParamName = FName( *MaterialParameter.AttributeName );
|
|
if ( MaterialParameter.AttributeType == HAPI_STORAGETYPE_STRING )
|
|
{
|
|
// String attributes are used for textures parameters
|
|
// We need to find the texture corresponding to the param
|
|
UTexture* FoundTexture = nullptr;
|
|
FString ParamValue = MaterialParameter.GetStringValue();
|
|
|
|
// Texture can either be already existing texture assets in UE4, or a newly generated textures by this asset
|
|
// Try to find the texture corresponding to the param value in the existing assets first.
|
|
FoundTexture = Cast< UTexture >(
|
|
StaticLoadObject( UTexture::StaticClass(), nullptr, *ParamValue, nullptr, LOAD_NoWarn, nullptr ) );
|
|
|
|
if ( !FoundTexture )
|
|
{
|
|
// We couldn't find a texture corresponding to the parameter in the existing UE4 assets
|
|
// Try to find the corresponding texture in the cooked temporary package we just generated
|
|
FoundTexture = FHoudiniEngineMaterialUtils::FindGeneratedTexture( ParamValue, CookParams );
|
|
}
|
|
|
|
// Do not update if unnecessary
|
|
|
|
if ( FoundTexture )
|
|
{
|
|
// Do not update if unnecessary
|
|
UTexture* OldTexture = nullptr;
|
|
bool FoundOldParam = MaterialInstance->GetTextureParameterValue( CurrentMatParamName, OldTexture );
|
|
if ( FoundOldParam && ( OldTexture == FoundTexture ) )
|
|
return false;
|
|
|
|
MaterialInstance->SetTextureParameterValueEditorOnly( CurrentMatParamName, FoundTexture );
|
|
bParameterUpdated = true;
|
|
}
|
|
}
|
|
else if ( MaterialParameter.AttributeTupleSize == 1 )
|
|
{
|
|
// Single attributes are either for scalar parameters or static switches
|
|
float OldValue;
|
|
bool FoundOldScalarParam = MaterialInstance->GetScalarParameterValue( CurrentMatParamName, OldValue );
|
|
if (FoundOldScalarParam)
|
|
{
|
|
// The material parameter is a scalar
|
|
float NewValue = (float)MaterialParameter.GetDoubleValue();
|
|
|
|
// Do not update if unnecessary
|
|
if (OldValue == NewValue)
|
|
return false;
|
|
|
|
MaterialInstance->SetScalarParameterValueEditorOnly(CurrentMatParamName, NewValue);
|
|
bParameterUpdated = true;
|
|
}
|
|
else
|
|
{
|
|
// See if the underlying parameter is a static switch
|
|
bool NewBoolValue = MaterialParameter.GetBoolValue();
|
|
|
|
// We need to iterate over the material's static parameter set
|
|
FStaticParameterSet StaticParameters;
|
|
MaterialInstance->GetStaticParameterValues(StaticParameters);
|
|
|
|
for (int32 SwitchParameterIdx = 0; SwitchParameterIdx < StaticParameters.StaticSwitchParameters.Num(); ++SwitchParameterIdx)
|
|
{
|
|
FStaticSwitchParameter& SwitchParameter = StaticParameters.StaticSwitchParameters[SwitchParameterIdx];
|
|
if (SwitchParameter.ParameterInfo.Name != CurrentMatParamName)
|
|
continue;
|
|
|
|
if (SwitchParameter.Value == NewBoolValue)
|
|
return false;
|
|
|
|
SwitchParameter.Value = NewBoolValue;
|
|
SwitchParameter.bOverride = true;
|
|
|
|
MaterialInstance->UpdateStaticPermutation(StaticParameters);
|
|
bParameterUpdated = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Tuple attributes are for vector parameters
|
|
FLinearColor NewLinearColor;
|
|
// if the attribute is stored in an int, we'll have to convert a color to a linear color
|
|
if ( MaterialParameter.AttributeType == HAPI_STORAGETYPE_INT || MaterialParameter.AttributeType == HAPI_STORAGETYPE_INT64 )
|
|
{
|
|
FColor IntColor;
|
|
IntColor.R = (int8)MaterialParameter.GetIntValue( 0 );
|
|
IntColor.G = (int8)MaterialParameter.GetIntValue( 1 );
|
|
IntColor.B = (int8)MaterialParameter.GetIntValue( 2 );
|
|
if ( MaterialParameter.AttributeTupleSize >= 4 )
|
|
IntColor.A = (int8)MaterialParameter.GetIntValue( 3 );
|
|
else
|
|
IntColor.A = 1;
|
|
|
|
NewLinearColor = FLinearColor( IntColor );
|
|
}
|
|
else
|
|
{
|
|
NewLinearColor.R = (float)MaterialParameter.GetDoubleValue( 0 );
|
|
NewLinearColor.G = (float)MaterialParameter.GetDoubleValue( 1 );
|
|
NewLinearColor.B = (float)MaterialParameter.GetDoubleValue( 2 );
|
|
if ( MaterialParameter.AttributeTupleSize >= 4 )
|
|
NewLinearColor.A = (float)MaterialParameter.GetDoubleValue( 3 );
|
|
}
|
|
|
|
// Do not update if unnecessary
|
|
FLinearColor OldValue;
|
|
bool FoundOldParam = MaterialInstance->GetVectorParameterValue( CurrentMatParamName, OldValue );
|
|
if ( FoundOldParam && ( OldValue == NewLinearColor ) )
|
|
return false;
|
|
|
|
MaterialInstance->SetVectorParameterValueEditorOnly( CurrentMatParamName, NewLinearColor );
|
|
bParameterUpdated = true;
|
|
}
|
|
#endif
|
|
|
|
return bParameterUpdated;
|
|
}
|
|
|
|
UTexture*
|
|
FHoudiniEngineMaterialUtils::FindGeneratedTexture( const FString& TextureString, FHoudiniCookParams& CookParams )
|
|
{
|
|
if ( TextureString.IsEmpty() )
|
|
return nullptr;
|
|
|
|
// Try to find the corresponding texture in the cooked temporary package generated by an HDA
|
|
UTexture* FoundTexture = nullptr;
|
|
for ( TMap<FString, TWeakObjectPtr< UPackage > >::TIterator IterPackage( *(CookParams.CookedTemporaryPackages) ); IterPackage; ++IterPackage )
|
|
{
|
|
// Iterate through the cooked packages
|
|
UPackage * CurrentPackage = IterPackage.Value().Get();
|
|
if ( !CurrentPackage )
|
|
continue;
|
|
|
|
// First, check if the package contains a texture
|
|
FString CurrentPackageName = CurrentPackage->GetName();
|
|
UTexture* PackageTexture = LoadObject< UTexture >( CurrentPackage, *CurrentPackageName, nullptr, LOAD_None, nullptr );
|
|
if ( !PackageTexture )
|
|
continue;
|
|
|
|
// Then check if the package's metadata match what we're looking for
|
|
// Make sure this texture was generated by Houdini Engine
|
|
UMetaData * MetaData = CurrentPackage->GetMetaData();
|
|
if ( !MetaData || !MetaData->HasValue( PackageTexture, HAPI_UNREAL_PACKAGE_META_GENERATED_OBJECT ) )
|
|
continue;
|
|
|
|
// Get the texture type from the meta data
|
|
// Texture type store has meta data will be C_A, N, S, R etc..
|
|
const FString TextureTypeString = MetaData->GetValue( PackageTexture, HAPI_UNREAL_PACKAGE_META_GENERATED_TEXTURE_TYPE );
|
|
if ( TextureTypeString.Compare( TextureString, ESearchCase::IgnoreCase ) == 0 )
|
|
{
|
|
FoundTexture = PackageTexture;
|
|
break;
|
|
}
|
|
|
|
// Convert the texture type to a "friendly" version
|
|
// C_A to diffuse, N to Normal, S to Specular etc...
|
|
FString TextureTypeFriendlyString = TextureTypeString;
|
|
FString TextureTypeFriendlyAlternateString = TEXT("");
|
|
if (TextureTypeString.Compare(HAPI_UNREAL_PACKAGE_META_GENERATED_TEXTURE_DIFFUSE, ESearchCase::IgnoreCase) == 0)
|
|
{
|
|
TextureTypeFriendlyString = TEXT("diffuse");
|
|
TextureTypeFriendlyAlternateString = TEXT("basecolor");
|
|
}
|
|
else if ( TextureTypeString.Compare( HAPI_UNREAL_PACKAGE_META_GENERATED_TEXTURE_NORMAL, ESearchCase::IgnoreCase) == 0 )
|
|
TextureTypeFriendlyString = TEXT( "normal" );
|
|
else if ( TextureTypeString.Compare( HAPI_UNREAL_PACKAGE_META_GENERATED_TEXTURE_EMISSIVE, ESearchCase::IgnoreCase) == 0 )
|
|
TextureTypeFriendlyString = TEXT( "emissive" );
|
|
else if ( TextureTypeString.Compare( HAPI_UNREAL_PACKAGE_META_GENERATED_TEXTURE_SPECULAR, ESearchCase::IgnoreCase) == 0 )
|
|
TextureTypeFriendlyString = TEXT( "specular" );
|
|
else if ( TextureTypeString.Compare( HAPI_UNREAL_PACKAGE_META_GENERATED_TEXTURE_ROUGHNESS, ESearchCase::IgnoreCase) == 0 )
|
|
TextureTypeFriendlyString = TEXT( "roughness" );
|
|
else if ( TextureTypeString.Compare( HAPI_UNREAL_PACKAGE_META_GENERATED_TEXTURE_METALLIC, ESearchCase::IgnoreCase) == 0 )
|
|
TextureTypeFriendlyString = TEXT( "metallic" );
|
|
else if ( TextureTypeString.Compare( HAPI_UNREAL_PACKAGE_META_GENERATED_TEXTURE_OPACITY_MASK, ESearchCase::IgnoreCase) == 0 )
|
|
TextureTypeFriendlyString = TEXT( "opacity" );
|
|
|
|
// See if we have a match between the texture string and the friendly name
|
|
if ( ( TextureTypeFriendlyString.Compare( TextureString, ESearchCase::IgnoreCase ) == 0 )
|
|
|| ( !TextureTypeFriendlyAlternateString.IsEmpty() && TextureTypeFriendlyAlternateString.Compare( TextureString, ESearchCase::IgnoreCase ) == 0 ) )
|
|
{
|
|
FoundTexture = PackageTexture;
|
|
break;
|
|
}
|
|
|
|
// Get the node path from the meta data
|
|
const FString NodePath = MetaData->GetValue( PackageTexture, HAPI_UNREAL_PACKAGE_META_NODE_PATH );
|
|
if ( NodePath.IsEmpty() )
|
|
continue;
|
|
|
|
// See if we have a match with the path and texture type
|
|
FString PathAndType = NodePath + TEXT("/") + TextureTypeString;
|
|
if ( PathAndType.Compare( TextureString, ESearchCase::IgnoreCase ) == 0 )
|
|
{
|
|
FoundTexture = PackageTexture;
|
|
break;
|
|
}
|
|
|
|
// See if we have a match with the friendly path and texture type
|
|
FString PathAndFriendlyType = NodePath + TEXT("/") + TextureTypeFriendlyString;
|
|
if ( PathAndFriendlyType.Compare( TextureString, ESearchCase::IgnoreCase ) == 0 )
|
|
{
|
|
FoundTexture = PackageTexture;
|
|
break;
|
|
}
|
|
|
|
// Try the alternate friendly string
|
|
if ( !TextureTypeFriendlyAlternateString.IsEmpty() )
|
|
{
|
|
PathAndFriendlyType = NodePath + TEXT("/") + TextureTypeFriendlyAlternateString;
|
|
if ( PathAndFriendlyType.Compare( TextureString, ESearchCase::IgnoreCase ) == 0 )
|
|
{
|
|
FoundTexture = PackageTexture;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return FoundTexture;
|
|
}
|