Files
2025-05-18 13:04:45 +08:00

437 lines
13 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "DatasmithMaxLogger.h"
#include "DatasmithMaxDirectLink.h"
#include "DatasmithMaxExporterDefines.h"
#include "HAL/UnrealMemory.h"
#include "Windows/AllowWindowsPlatformTypes.h"
MAX_INCLUDES_START
#include "max.h"
MAX_INCLUDES_END
DatasmithMaxLogger& DatasmithMaxLogger::Get()
{
static TSharedRef< DatasmithMaxLogger > Instance = MakeShared< DatasmithMaxLogger >(); // Create a shared ref because we support TSharedFromThis
return Instance.Get();
}
void DatasmithMaxLogger::Purge()
{
ResetGeneralErrors();
ResetTextureErrors();
ResetMissingAssetErrors();
PartialSupportedMats.Empty();
UnsupportedMats.Empty();
PartialSupportedMaps.Empty();
UnsupportedMaps.Empty();
UnsupportedLight.Empty();
FailUVs.Empty();
FailObjs.Empty();
InvalidTransforms.Empty();
}
void DatasmithMaxLogger::AddPartialSupportedMat(Mtl* Mat)
{
for (int i = 0; i < PartialSupportedMats.Num(); i++)
{
if (Mat->ClassID() == PartialSupportedMats[i]->ClassID())
{
return;
}
}
PartialSupportedMats.Add(Mat);
}
void DatasmithMaxLogger::AddUnsupportedMat(Mtl* Mat)
{
for (int i = 0; i < UnsupportedMats.Num(); i++)
{
if (Mat->ClassID() == UnsupportedMats[i]->ClassID())
{
return;
}
}
UnsupportedMats.Add(Mat);
}
void DatasmithMaxLogger::AddPartialSupportedMap(Texmap* Map)
{
for (int i = 0; i < PartialSupportedMaps.Num(); i++)
{
if (Map->ClassID() == PartialSupportedMaps[i]->ClassID())
{
return;
}
}
PartialSupportedMaps.Add(Map);
}
void DatasmithMaxLogger::AddUnsupportedMap(Texmap* Map)
{
for (int i = 0; i < UnsupportedMaps.Num(); i++)
{
if (Map->ClassID() == UnsupportedMaps[i]->ClassID())
{
return;
}
}
MSTR Classname;
Map->GetClassName(Classname);
DatasmithMaxDirectLink::LogWarning(FString::Printf(TEXT("Unsupported texmap \"%s\" of type %s (0x%08x-0x%08x)"), Map->GetName().ToBSTR(), Classname.ToBSTR(), Map->ClassID().PartA(), Map->ClassID().PartB()));
UnsupportedMaps.Add(Map);
}
void DatasmithMaxLogger::AddUnsupportedLight(INode* Light)
{
for (int i = 0; i < UnsupportedLight.Num(); i++)
{
if (Light->ClassID() == UnsupportedLight[i]->ClassID())
{
return;
}
}
UnsupportedLight.Add(Light);
}
void DatasmithMaxLogger::AddUnsupportedUV(INode* Node)
{
FailUVs.AddUnique(Node);
}
void DatasmithMaxLogger::AddInvalidObj(INode* Node)
{
FailObjs.AddUnique(Node);
}
TArray<INode*> DatasmithMaxLogger::GetInvalidObjects() const
{
return FailObjs;
}
void DatasmithMaxLogger::AddInvalidTransform(INode* Node)
{
InvalidTransforms.AddUnique(Node);
}
bool DatasmithMaxLogger::HasWarnings()
{
if (GetGeneralErrorsCount() > 0 || GetTextureErrorsCount() > 0 || GetMissingAssetErrorsCount() > 0 ||
PartialSupportedMats.Num() > 0 || UnsupportedMats.Num() > 0 ||
PartialSupportedMaps.Num()>0 || UnsupportedMaps.Num()>0 || UnsupportedLight.Num() > 0 || FailUVs.Num() > 0)
{
return true;
}
else
{
return false;
}
}
bool DatasmithMaxLogger::CopyToClipBoard()
{
if ( ShowMessage.IsEmpty() )
{
return false;
}
if (OpenClipboard(NULL))
{
EmptyClipboard();
HGLOBAL HClipboardData;
size_t Num = (ShowMessage.Len() + 1) * sizeof(WCHAR);
HClipboardData = GlobalAlloc(NULL, Num);
WCHAR* PchData = (WCHAR*)GlobalLock(HClipboardData);
FMemory::Memcpy(PchData, *ShowMessage, Num);
SetClipboardData(CF_UNICODETEXT, HClipboardData);
GlobalUnlock(HClipboardData);
CloseClipboard();
return true;
}
return false;
}
void DatasmithMaxLogger::AddItem(const TCHAR* Msg, HWND Handle, FString& FullMsg)
{
SendDlgItemMessage(Handle, IDC_ERROR_MSG_LIST, LB_ADDSTRING, NULL, (LPARAM)Msg);
FullMsg += Msg;
FullMsg += LINE_TERMINATOR;
}
void DatasmithMaxLogger::AddObjectList(TArray< INode* > ObjectList, HWND Handle, const TCHAR* Header, const TCHAR* Description)
{
const int32 MAX_ITEMS_TO_SHOW = 20;
if ( ObjectList.Num() > 0 )
{
AddItem(TEXT("------------------------------------------------------------------------------------------"), Handle, ShowMessage);
AddItem(*(FString( TEXT("\t") ) + Header), Handle, ShowMessage);
AddItem(TEXT("------------------------------------------------------------------------------------------"), Handle, ShowMessage);
if ( Description && FCString::Strlen( Description ) )
{
AddItem(Description, Handle, ShowMessage);
}
FString ObjectListString;
int j = 0;
for (int i = 0; i < FMath::Min( ObjectList.Num(), MAX_ITEMS_TO_SHOW ); i++)
{
if (j > 0)
{
ObjectListString += TEXT(", [");
}
else
{
ObjectListString += TEXT("[");
}
ObjectListString += ObjectList[i]->GetName();
ObjectListString += TEXT("]");
j++;
if (j == 4)
{
j = 0;
AddItem(*ObjectListString, Handle, ShowMessage);
ObjectListString.Empty();
}
}
if (!ObjectListString.IsEmpty())
{
AddItem(*ObjectListString, Handle, ShowMessage);
}
if (ObjectList.Num() > MAX_ITEMS_TO_SHOW)
{
ObjectListString = TEXT("...and ") + FString::FromInt(ObjectList.Num() - MAX_ITEMS_TO_SHOW) + TEXT(" more");
AddItem(*ObjectListString, Handle, ShowMessage);
}
AddItem(TEXT("\n\n"), Handle, ShowMessage);
}
}
FString DatasmithMaxLogger::GetLightDescription(INode* LightNode)
{
ObjectState State = LightNode->EvalWorldState(0);
LightObject *Light = (LightObject*)State.obj;
MSTR Classname;
Light->GetClassName(Classname);
return FString::Printf(TEXT("\"%s\" of type %s (0x%08x-0x%08x)"), LightNode->GetName(), Classname.ToBSTR(), Light->ClassID().PartA(), Light->ClassID().PartB());
}
void DatasmithMaxLogger::Show(HWND Handle)
{
ShowMessage = TEXT("");
if (GetGeneralErrorsCount() > 0)
{
AddItem(TEXT("------------------------------------------------------------------------------------------"), Handle, ShowMessage);
AddItem(TEXT("\tGENERAL MESSAGES"), Handle, ShowMessage);
AddItem(TEXT("------------------------------------------------------------------------------------------"), Handle, ShowMessage);
for (int i = 0; i < GetGeneralErrorsCount(); i++)
{
AddItem(GetGeneralError(i), Handle, ShowMessage);
}
AddItem(TEXT("\n\n"), Handle, ShowMessage);
}
if (GetMissingAssetErrorsCount() > 0)
{
AddItem(TEXT("------------------------------------------------------------------------------------------"), Handle, ShowMessage);
AddItem(TEXT("\tMISSING EXTERNAL FILES"), Handle, ShowMessage);
AddItem(TEXT("------------------------------------------------------------------------------------------"), Handle, ShowMessage);
AddItem(TEXT("One or more file required to export correctly to Unreal is missing,"), Handle, ShowMessage);
AddItem(TEXT("which can produce incorrect results. We recommend that you solve the issue"), Handle, ShowMessage);
AddItem(TEXT("with the 3ds Max's Asset Tracking tool prior to export."), Handle, ShowMessage);
for (int i = 0; i < GetMissingAssetErrorsCount(); i++)
{
AddItem(GetMissingAssetError(i), Handle, ShowMessage);
}
AddItem(TEXT("\n\n"), Handle, ShowMessage);
}
// Invalid Objects
AddObjectList( FailObjs, Handle, TEXT("INVALID OBJECTS (unsupported, corrupted or too small geometry?)") );
if (PartialSupportedMats.Num() > 0 || PartialSupportedMaps.Num()>0)
{
AddItem(TEXT("------------------------------------------------------------------------------------------"), Handle, ShowMessage);
AddItem(TEXT("\tPARTIALLY SUPPORTED"), Handle, ShowMessage);
AddItem(TEXT("------------------------------------------------------------------------------------------"), Handle, ShowMessage);
for (int i = 0; i < PartialSupportedMats.Num(); i++)
{
MSTR Classname;
PartialSupportedMats[i]->GetClassName(Classname);
FString Msg = TEXT("Materials of type ") + FString(Classname.ToBSTR()) + TEXT(" are not supported and its first children will be used.");
AddItem(*Msg, Handle, ShowMessage);
}
for (int i = 0; i < PartialSupportedMaps.Num(); i++)
{
MSTR Classname;
PartialSupportedMaps[i]->GetClassName(Classname);;
FString Msg = TEXT("Texmaps of type ") + FString(Classname.ToBSTR()) + TEXT(" are not supported and its first children will be used.");
AddItem(*Msg, Handle, ShowMessage);
}
ShowMessage += TEXT("\n\n");
SendDlgItemMessage(Handle, IDC_ERROR_MSG_LIST, LB_ADDSTRING, NULL, (LPARAM)TEXT("\n"));
SendDlgItemMessage(Handle, IDC_ERROR_MSG_LIST, LB_ADDSTRING, NULL, (LPARAM)TEXT("\n"));
}
if (UnsupportedMats.Num() > 0)
{
AddItem(TEXT("------------------------------------------------------------------------------------------"), Handle, ShowMessage);
AddItem(TEXT("\tMATERIALS"), Handle, ShowMessage);
AddItem(TEXT("------------------------------------------------------------------------------------------"), Handle, ShowMessage);
AddItem(TEXT("Some materials used in the scene are not supported by the Unreal Datasmith format in the moment. They will not be imported inside Unreal Engine."), Handle, ShowMessage);
for (int i = 0; i < UnsupportedMats.Num(); i++)
{
MSTR Classname;
UnsupportedMats[i]->GetClassName(Classname);
Mtl* Mat = UnsupportedMats[i];
FString Msg = FString::Printf(TEXT("\"%s\" of type %s (0x%08x-0x%08x)"), Mat->GetName().ToBSTR(), Classname.ToBSTR(), Mat->ClassID().PartA(), Mat->ClassID().PartB());
AddItem(*Msg, Handle, ShowMessage);
}
AddItem(TEXT("\n\n"), Handle, ShowMessage);
}
if (UnsupportedMaps.Num() > 0 || GetTextureErrorsCount() > 0)
{
AddItem(TEXT("------------------------------------------------------------------------------------------"), Handle, ShowMessage);
AddItem(TEXT("\tTEXTUREMAPS"), Handle, ShowMessage);
AddItem(TEXT("------------------------------------------------------------------------------------------"), Handle, ShowMessage);
if (UnsupportedMaps.Num() > 0)
{
AddItem(TEXT("Some texture maps used in the scene are not supported by the Unreal Datasmith format in the moment. They will not be imported inside Unreal Engine."), Handle, ShowMessage);
for (int i = 0; i < UnsupportedMaps.Num(); i++)
{
MSTR Classname;
UnsupportedMaps[i]->GetClassName(Classname);
Texmap* Map = UnsupportedMaps[i];
FString Msg = FString::Printf(TEXT("\"%s\" of type %s (0x%08x-0x%08x)"), Map->GetName().ToBSTR(), Classname.ToBSTR(), Map->ClassID().PartA(), Map->ClassID().PartB());
AddItem(*Msg, Handle, ShowMessage);
}
}
if (GetTextureErrorsCount() > 0)
{
for (int i = 0; i < GetTextureErrorsCount(); i++)
{
AddItem(GetTextureError(i), Handle, ShowMessage);
}
}
AddItem(TEXT("\n\n"), Handle, ShowMessage);
}
if (UnsupportedLight.Num() > 0)
{
AddItem(TEXT("------------------------------------------------------------------------------------------"), Handle, ShowMessage);
AddItem(TEXT("\tLIGHTS"), Handle, ShowMessage);
AddItem(TEXT("------------------------------------------------------------------------------------------"), Handle, ShowMessage);
AddItem(TEXT("Some lights used in the scene are not supported by the Unreal Datasmith format in the moment. They will not be imported inside Unreal Engine."), Handle, ShowMessage);
for (int i = 0; i < UnsupportedLight.Num(); i++)
{
FString Msg = GetLightDescription(UnsupportedLight[i]);
AddItem(*Msg, Handle, ShowMessage);
}
AddItem(TEXT("\n\n"), Handle, ShowMessage);
}
// Invalid UVs
AddObjectList( FailUVs, Handle, TEXT("MESHES WITHOUT UV CHANNEL 2 (USED FOR LIGHT BAKING)") );
// Invalid Transforms
FString InvalidTransformsDescription = TEXT("Some objects have customized pivots and nonuniform scales.") + FString( LINE_TERMINATOR );
InvalidTransformsDescription += TEXT("Those combinations are not supported by Unreal and will produce incorrect results.") + FString( LINE_TERMINATOR );
InvalidTransformsDescription += TEXT("It is recommended to use the Reset XForm Utility on those objects:") + FString( LINE_TERMINATOR );
AddObjectList( InvalidTransforms, Handle, TEXT("INVALID TRANSFORMS"), *InvalidTransformsDescription );
}
INT_PTR CALLBACK MsgListDlgProc(HWND hDlg, UINT iMsg, WPARAM wParam, LPARAM lParam)
{
int Tabs[3] = { 24, 24, 24 };
switch (iMsg)
{
case WM_INITDIALOG:
CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
SendDlgItemMessage(hDlg, IDC_ERROR_MSG_LIST, LB_RESETCONTENT, 0, 0);
SendDlgItemMessage(hDlg, IDC_ERROR_MSG_LIST, LB_SETTABSTOPS, (WPARAM)3, (LPARAM)Tabs);
EnableWindow(GetDlgItem(hDlg, IDSELECTINVALID), DatasmithMaxLogger::Get().GetInvalidObjects().Num()>0);
DatasmithMaxLogger::Get().Show(hDlg);
return TRUE;
case WM_COMMAND:
switch (LOWORD(wParam))
{
case IDCOPY:
DatasmithMaxLogger::Get().CopyToClipBoard();
break;
case IDSELECTINVALID:
INodeTab SelectedNodes;
for (int i = 0; i < DatasmithMaxLogger::Get().GetInvalidObjects().Num(); i++)
{
SelectedNodes.AppendNode(DatasmithMaxLogger::Get().GetInvalidObjects()[i]);
}
GetCOREInterface()->ClearNodeSelection();
GetCOREInterface()->SelectNodeTab(SelectedNodes,TRUE,TRUE);
break;
}
break;
case WM_CLOSE:
EndDialog(hDlg, TRUE);
break;
case WM_SIZE:
{
int DialogWidth = (int)LOWORD(lParam);
int DialogHeight = (int)HIWORD(lParam);
HWND hList = GetDlgItem(hDlg, IDC_ERROR_MSG_LIST);
HWND hCopy = GetDlgItem(hDlg, IDCOPY);
HWND hInvalid = GetDlgItem(hDlg, IDSELECTINVALID);
MoveWindow(hList, 6, 7, (DialogWidth - 12), (DialogHeight - 42), true);
MoveWindow(hCopy, 24, DialogHeight - 31, (DialogWidth / 2 - 24), 26, true);
MoveWindow(hInvalid, (DialogWidth / 2) + 3 , DialogHeight - 31, (DialogWidth / 2 - 23), 26, true);
break;
}
}
return FALSE;
}
#include "Windows/HideWindowsPlatformTypes.h"