Commit 6cdeb206 by wester

testing fpr exposing API

parent 110727d8
// Fill out your copyright notice in the Description page of Project Settings. // Fill out your copyright notice in the Description page of Project Settings.
#include "GLTFLoaderPrivatePCH.h" #include "GLTFLoaderPrivatePCH.h"
#include "GLTFFactory.h" #include "GLTFFactory.h"
#include "Engine.h" #include "Engine.h"
#include "Editor/UnrealEd/Public/Editor.h" #include "Editor/UnrealEd/Public/Editor.h"
#include "GLTFMeshBuilder.h" #include "GLTFMeshBuilder.h"
#include "UnrealEd.h" #include "UnrealEd.h"
#include "Factories.h" #include "Factories.h"
#include "BusyCursor.h" #include "BusyCursor.h"
#include "SSkeletonWidget.h" #include "SSkeletonWidget.h"
#include "AssetRegistryModule.h" #include "AssetRegistryModule.h"
#include "Engine/StaticMesh.h" #include "Engine/StaticMesh.h"
#include "FbxErrors.h" #include "FbxErrors.h"
#define LOCTEXT_NAMESPACE "GLTFFactory" #define LOCTEXT_NAMESPACE "GLTFFactory"
UGLTFFactory::UGLTFFactory(const FObjectInitializer& ObjectInitializer) UGLTFFactory::UGLTFFactory(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer) : Super(ObjectInitializer)
{ {
SupportedClass = UStaticMeshComponent::StaticClass(); SupportedClass = UStaticMeshComponent::StaticClass();
Formats.Add(TEXT("gltf;GLTF meshes")); Formats.Add(TEXT("gltf;GLTF meshes"));
bCreateNew = false; bCreateNew = false;
bText = false; bText = false;
bEditorImport = true; bEditorImport = true;
bOperationCanceled = false; bOperationCanceled = false;
bDetectImportTypeOnImport = false; bDetectImportTypeOnImport = false;
} }
//This function is adapted from ::CreateBinary() //This function is adapted from ::CreateBinary()
UObject* UGLTFFactory::FactoryCreateBinary UObject* UGLTFFactory::FactoryCreateBinary
( (
UClass* Class, UClass* Class,
UObject* InParent, UObject* InParent,
FName Name, FName Name,
EObjectFlags Flags, EObjectFlags Flags,
UObject* Context, UObject* Context,
const TCHAR* Type, const TCHAR* Type,
const uint8*& Buffer, const uint8*& Buffer,
const uint8* BufferEnd, const uint8* BufferEnd,
FFeedbackContext* Warn, FFeedbackContext* Warn,
bool& bOutOperationCanceled bool& bOutOperationCanceled
) )
{ {
if (bOperationCanceled) if (bOperationCanceled)
{ {
bOutOperationCanceled = true; bOutOperationCanceled = true;
FEditorDelegates::OnAssetPostImport.Broadcast(this, NULL); FEditorDelegates::OnAssetPostImport.Broadcast(this, NULL);
return NULL; return NULL;
} }
FEditorDelegates::OnAssetPreImport.Broadcast(this, Class, InParent, Name, Type); FEditorDelegates::OnAssetPreImport.Broadcast(this, Class, InParent, Name, Type);
UObject* NewObject = NULL; UObject* NewObject = NULL;
GLTFMeshBuilder Builder(*UFactory::CurrentFilename); GLTFMeshBuilder Builder(*UFactory::CurrentFilename);
bool bShowImportDialog = bShowOption && !GIsAutomationTesting; bool bShowImportDialog = bShowOption && !GIsAutomationTesting;
bool bImportAll = false; bool bImportAll = false;
auto ImportOptions = FGLTFLoaderModule::ImportOptions; auto ImportOptions = FGLTFLoaderModule::ImportOptions;
bOutOperationCanceled = bOperationCanceled; bOutOperationCanceled = bOperationCanceled;
if (bImportAll) if (bImportAll)
{ {
// If the user chose to import all, we don't show the dialog again and use the same settings for each object until importing another set of files // If the user chose to import all, we don't show the dialog again and use the same settings for each object until importing another set of files
bShowOption = false; bShowOption = false;
} }
// For multiple files, use the same settings // For multiple files, use the same settings
bDetectImportTypeOnImport = false; bDetectImportTypeOnImport = false;
Warn->BeginSlowTask(NSLOCTEXT("GLTFFactory", "BeginImportingGLTFMeshTask", "Importing GLTF mesh"), true); Warn->BeginSlowTask(NSLOCTEXT("GLTFFactory", "BeginImportingGLTFMeshTask", "Importing GLTF mesh"), true);
if (!Builder.LoadedSuccessfully()) if (!Builder.LoadedSuccessfully())
{ {
// Log the error message and fail the import. // Log the error message and fail the import.
Warn->Log(ELogVerbosity::Error, Builder.GetError()); Warn->Log(ELogVerbosity::Error, Builder.GetError());
} }
else else
{ {
// Log the import message and import the mesh. // Log the import message and import the mesh.
const FString errorMessage = Builder.GetError(); const FString errorMessage = Builder.GetError();
if (errorMessage.Len() > 0) if (errorMessage.Len() > 0)
{ {
Warn->Log(errorMessage); Warn->Log(errorMessage);
} }
FString RootNodeToImport = ""; FString RootNodeToImport = "";
RootNodeToImport = Builder.GetRootNode(); RootNodeToImport = Builder.GetRootNode();
// For animation and static mesh we assume there is at lease one interesting node by default // For animation and static mesh we assume there is at lease one interesting node by default
int32 InterestingNodeCount = 1; int32 InterestingNodeCount = 1;
bool bImportStaticMeshLODs = /*ImportUI->StaticMeshImportData->bImportMeshLODs*/ false; bool bImportStaticMeshLODs = /*ImportUI->StaticMeshImportData->bImportMeshLODs*/ false;
bool bCombineMeshes = /*ImportUI->bCombineMeshes*/ true; bool bCombineMeshes = /*ImportUI->bCombineMeshes*/ true;
if (bCombineMeshes && !bImportStaticMeshLODs) if (bCombineMeshes && !bImportStaticMeshLODs)
{ {
// If Combine meshes and dont import mesh LODs, the interesting node count should be 1 so all the meshes are grouped together into one static mesh // If Combine meshes and dont import mesh LODs, the interesting node count should be 1 so all the meshes are grouped together into one static mesh
InterestingNodeCount = 1; InterestingNodeCount = 1;
} }
else else
{ {
// count meshes in lod groups if we dont care about importing LODs // count meshes in lod groups if we dont care about importing LODs
bool bCountLODGroupMeshes = !bImportStaticMeshLODs; bool bCountLODGroupMeshes = !bImportStaticMeshLODs;
int32 NumLODGroups = 0; int32 NumLODGroups = 0;
InterestingNodeCount = Builder.GetMeshCount(RootNodeToImport/*, bCountLODGroupMeshes, NumLODGroups*/); InterestingNodeCount = Builder.GetMeshCount(RootNodeToImport/*, bCountLODGroupMeshes, NumLODGroups*/);
// if there were LODs in the file, do not combine meshes even if requested // if there were LODs in the file, do not combine meshes even if requested
if (bImportStaticMeshLODs && bCombineMeshes) if (bImportStaticMeshLODs && bCombineMeshes)
{ {
bCombineMeshes = NumLODGroups == 0; bCombineMeshes = NumLODGroups == 0;
} }
} }
const FString Filename(UFactory::CurrentFilename); const FString Filename(UFactory::CurrentFilename);
if (RootNodeToImport.Len() != 0 && InterestingNodeCount > 0) if (RootNodeToImport.Len() != 0 && InterestingNodeCount > 0)
{ {
int32 NodeIndex = 0; int32 NodeIndex = 0;
int32 ImportedMeshCount = 0; int32 ImportedMeshCount = 0;
UStaticMeshComponent* NewStaticMesh = NULL; UStaticMeshComponent* NewStaticMesh = NULL;
if (bCombineMeshes) if (bCombineMeshes)
{ {
auto MeshNames = Builder.GetMeshNames(RootNodeToImport); auto MeshNames = Builder.GetMeshNames(RootNodeToImport);
if (MeshNames.Num() > 0) if (MeshNames.Num() > 0)
{ {
NewStaticMesh = Builder.ImportStaticMeshAsSingle(InParent, MeshNames, Name, Flags/*, ImportUI->StaticMeshImportData*/, NULL/*, 0*/); NewStaticMesh = Builder.ImportStaticMeshAsSingle(InParent, MeshNames, Name, Flags/*, ImportUI->StaticMeshImportData*/, NULL/*, 0*/);
for (auto Mesh : MeshNames) for (auto Mesh : MeshNames)
{ {
Warn->Log(FString("Found mesh: ") + Mesh); Warn->Log(FString("Found mesh: ") + Mesh);
} }
} }
ImportedMeshCount = NewStaticMesh ? 1 : 0; ImportedMeshCount = NewStaticMesh ? 1 : 0;
} }
NewObject = NewStaticMesh; NewObject = NewStaticMesh;
} }
else else
{ {
if (RootNodeToImport == "") if (RootNodeToImport == "")
{ {
Builder.AddTokenizedErrorMessage(FTokenizedMessage::Create(EMessageSeverity::Error, LOCTEXT("FailedToImport_InvalidRoot", "Could not find root node.")), FFbxErrors::SkeletalMesh_InvalidRoot); Builder.AddTokenizedErrorMessage(FTokenizedMessage::Create(EMessageSeverity::Error, LOCTEXT("FailedToImport_InvalidRoot", "Could not find root node.")), FFbxErrors::SkeletalMesh_InvalidRoot);
} }
else else
{ {
Builder.AddTokenizedErrorMessage(FTokenizedMessage::Create(EMessageSeverity::Error, LOCTEXT("FailedToImport_InvalidNode", "Could not find any node.")), FFbxErrors::SkeletalMesh_InvalidNode); Builder.AddTokenizedErrorMessage(FTokenizedMessage::Create(EMessageSeverity::Error, LOCTEXT("FailedToImport_InvalidNode", "Could not find any node.")), FFbxErrors::SkeletalMesh_InvalidNode);
} }
} }
} }
if (NewObject == NULL) if (NewObject == NULL)
{ {
// Import fail error message // Import fail error message
Builder.AddTokenizedErrorMessage(FTokenizedMessage::Create(EMessageSeverity::Error, LOCTEXT("FailedToImport_NoObject", "Import failed.")), FFbxErrors::Generic_ImportingNewObjectFailed); Builder.AddTokenizedErrorMessage(FTokenizedMessage::Create(EMessageSeverity::Error, LOCTEXT("FailedToImport_NoObject", "Import failed.")), FFbxErrors::Generic_ImportingNewObjectFailed);
Warn->Log(ELogVerbosity::Warning, "Failed to import GLTF mesh"); Warn->Log(ELogVerbosity::Warning, "Failed to import GLTF mesh");
} }
Warn->EndSlowTask(); Warn->EndSlowTask();
FEditorDelegates::OnAssetPostImport.Broadcast(this, NewObject); FEditorDelegates::OnAssetPostImport.Broadcast(this, NewObject);
return NewObject; return NewObject;
} }
bool UGLTFFactory::DoesSupportClass(UClass * Class) bool UGLTFFactory::DoesSupportClass(UClass * Class)
{ {
return (Class == UStaticMesh::StaticClass()); return (Class == UStaticMesh::StaticClass());
} }
bool UGLTFFactory::FactoryCanImport(const FString& Filename) bool UGLTFFactory::FactoryCanImport(const FString& Filename)
{ {
const FString Extension = FPaths::GetExtension(Filename); const FString Extension = FPaths::GetExtension(Filename);
if (Extension == TEXT("gltf")) if (Extension == TEXT("gltf"))
{ {
return true; return true;
} }
return false; return false;
} }
#undef LOCTEXT_NAMESPACE #undef LOCTEXT_NAMESPACE
\ No newline at end of file
/// @file GLTFFactory.h by Robert Poncelet /// @file GLTFFactory.h by Robert Poncelet
#pragma once #pragma once
#include "Factories/Factory.h" #include "Factories/Factory.h"
#include "GLTFFactory.generated.h" #include "GLTFFactory.generated.h"
UCLASS() UCLASS()
/// The class instantiated by the AssetToolsModule for importing the chosen UAsset into the content browser. Adapted from UFbxFactory. /// The class instantiated by the AssetToolsModule for importing the chosen UAsset into the content browser. Adapted from UFbxFactory.
class UGLTFFactory : public UFactory class UGLTFFactory : public UFactory
{ {
GENERATED_BODY() GENERATED_BODY()
UGLTFFactory(const FObjectInitializer& ObjectInitializer); UGLTFFactory(const FObjectInitializer& ObjectInitializer);
/// @name UFactory Implementation /// @name UFactory Implementation
///@{ ///@{
virtual bool DoesSupportClass(UClass * Class) override; virtual bool DoesSupportClass(UClass * Class) override;
virtual UObject* FactoryCreateBinary(UClass* InClass, UObject* InParent, FName InName, EObjectFlags Flags, UObject* Context, const TCHAR* Type, const uint8*& Buffer, const uint8* BufferEnd, FFeedbackContext* Warn, bool& bOutOperationCanceled) override; virtual UObject* FactoryCreateBinary(UClass* InClass, UObject* InParent, FName InName, EObjectFlags Flags, UObject* Context, const TCHAR* Type, const uint8*& Buffer, const uint8* BufferEnd, FFeedbackContext* Warn, bool& bOutOperationCanceled) override;
virtual bool FactoryCanImport(const FString& Filename) override; virtual bool FactoryCanImport(const FString& Filename) override;
///@} ///@}
bool bShowOption; bool bShowOption;
bool bDetectImportTypeOnImport; bool bDetectImportTypeOnImport;
/** true if the import operation was canceled. */ /** true if the import operation was canceled. */
bool bOperationCanceled; bool bOperationCanceled;
}; };
// Some copyright should be here... // Some copyright should be here...
#include "GLTFLoaderPrivatePCH.h" #include "GLTFLoaderPrivatePCH.h"
#include "SlateBasics.h" #include "SlateBasics.h"
#include "SlateExtras.h" #include "SlateExtras.h"
#include "Developer/DesktopPlatform/Public/DesktopPlatformModule.h" #include "Developer/DesktopPlatform/Public/DesktopPlatformModule.h"
#include "Developer/AssetTools/Public/AssetToolsModule.h" #include "Developer/AssetTools/Public/AssetToolsModule.h"
#include "GLTFLoaderStyle.h" #include "GLTFLoaderStyle.h"
#include "GLTFLoaderCommands.h" #include "GLTFLoaderCommands.h"
#include "GLTFFactory.h" #include "GLTFFactory.h"
#include "LevelEditor.h" #include "LevelEditor.h"
static const FName GLTFLoaderTabName("GLTFLoader"); static const FName GLTFLoaderTabName("GLTFLoader");
GLTFImportOptions FGLTFLoaderModule::ImportOptions = GLTFImportOptions::Default(); GLTFImportOptions FGLTFLoaderModule::ImportOptions = GLTFImportOptions::Default();
#define LOCTEXT_NAMESPACE "FGLTFLoaderModule" #define LOCTEXT_NAMESPACE "FGLTFLoaderModule"
void FGLTFLoaderModule::StartupModule() void FGLTFLoaderModule::StartupModule()
{ {
FGLTFLoaderStyle::Initialize(); FGLTFLoaderStyle::Initialize();
FGLTFLoaderStyle::ReloadTextures(); FGLTFLoaderStyle::ReloadTextures();
FGLTFLoaderCommands::Register(); FGLTFLoaderCommands::Register();
PluginCommands = MakeShareable(new FUICommandList); PluginCommands = MakeShareable(new FUICommandList);
PluginCommands->MapAction( PluginCommands->MapAction(
FGLTFLoaderCommands::Get().OpenPluginWindow, FGLTFLoaderCommands::Get().OpenPluginWindow,
FExecuteAction::CreateRaw(this, &FGLTFLoaderModule::PluginButtonClicked), FExecuteAction::CreateRaw(this, &FGLTFLoaderModule::PluginButtonClicked),
FCanExecuteAction()); FCanExecuteAction());
PluginCommands->MapAction( PluginCommands->MapAction(
FGLTFLoaderCommands::Get().OpenImportWindow, FGLTFLoaderCommands::Get().OpenImportWindow,
FExecuteAction::CreateRaw(this, &FGLTFLoaderModule::OpenImportWindow), FExecuteAction::CreateRaw(this, &FGLTFLoaderModule::OpenImportWindow),
FCanExecuteAction()); FCanExecuteAction());
FLevelEditorModule& LevelEditorModule = FModuleManager::LoadModuleChecked<FLevelEditorModule>("LevelEditor"); FLevelEditorModule& LevelEditorModule = FModuleManager::LoadModuleChecked<FLevelEditorModule>("LevelEditor");
{ {
TSharedPtr<FExtender> MenuExtender = MakeShareable(new FExtender()); TSharedPtr<FExtender> MenuExtender = MakeShareable(new FExtender());
MenuExtender->AddMenuExtension("WindowLayout", EExtensionHook::After, PluginCommands, FMenuExtensionDelegate::CreateRaw(this, &FGLTFLoaderModule::AddMenuExtension)); MenuExtender->AddMenuExtension("WindowLayout", EExtensionHook::After, PluginCommands, FMenuExtensionDelegate::CreateRaw(this, &FGLTFLoaderModule::AddMenuExtension));
LevelEditorModule.GetMenuExtensibilityManager()->AddExtender(MenuExtender); LevelEditorModule.GetMenuExtensibilityManager()->AddExtender(MenuExtender);
} }
{ {
TSharedPtr<FExtender> ToolbarExtender = MakeShareable(new FExtender); TSharedPtr<FExtender> ToolbarExtender = MakeShareable(new FExtender);
ToolbarExtender->AddToolBarExtension("Settings", EExtensionHook::After, PluginCommands, FToolBarExtensionDelegate::CreateRaw(this, &FGLTFLoaderModule::AddToolbarExtension)); ToolbarExtender->AddToolBarExtension("Settings", EExtensionHook::After, PluginCommands, FToolBarExtensionDelegate::CreateRaw(this, &FGLTFLoaderModule::AddToolbarExtension));
LevelEditorModule.GetToolBarExtensibilityManager()->AddExtender(ToolbarExtender); LevelEditorModule.GetToolBarExtensibilityManager()->AddExtender(ToolbarExtender);
} }
FGlobalTabmanager::Get()->RegisterNomadTabSpawner(GLTFLoaderTabName, FOnSpawnTab::CreateRaw(this, &FGLTFLoaderModule::OnSpawnPluginTab)) FGlobalTabmanager::Get()->RegisterNomadTabSpawner(GLTFLoaderTabName, FOnSpawnTab::CreateRaw(this, &FGLTFLoaderModule::OnSpawnPluginTab))
.SetDisplayName(LOCTEXT("FGLTFLoaderTabTitle", "GLTFLoader")) .SetDisplayName(LOCTEXT("FGLTFLoaderTabTitle", "GLTFLoader"))
.SetMenuType(ETabSpawnerMenuType::Hidden); .SetMenuType(ETabSpawnerMenuType::Hidden);
UE_LOG(LogInit, Warning, TEXT("GLTFLoader module started successfully.")); UE_LOG(LogInit, Warning, TEXT("GLTFLoader module started successfully."));
} }
void FGLTFLoaderModule::ShutdownModule() void FGLTFLoaderModule::ShutdownModule()
{ {
FGLTFLoaderStyle::Shutdown(); FGLTFLoaderStyle::Shutdown();
FGLTFLoaderCommands::Unregister(); FGLTFLoaderCommands::Unregister();
FGlobalTabmanager::Get()->UnregisterNomadTabSpawner(GLTFLoaderTabName); FGlobalTabmanager::Get()->UnregisterNomadTabSpawner(GLTFLoaderTabName);
} }
TSharedRef<SDockTab> FGLTFLoaderModule::OnSpawnPluginTab(const FSpawnTabArgs& SpawnTabArgs) TSharedRef<SDockTab> FGLTFLoaderModule::OnSpawnPluginTab(const FSpawnTabArgs& SpawnTabArgs)
{ {
return SNew(SDockTab) return SNew(SDockTab)
.TabRole(ETabRole::NomadTab) .TabRole(ETabRole::NomadTab)
[ [
SNew(SVerticalBox) SNew(SVerticalBox)
+ SVerticalBox::Slot() + SVerticalBox::Slot()
.Padding(1.0f) .Padding(1.0f)
.HAlign(HAlign_Center) .HAlign(HAlign_Center)
.VAlign(VAlign_Top) .VAlign(VAlign_Top)
[ [
SNew(STextBlock) SNew(STextBlock)
.Text(LOCTEXT("TopText", "Import a glTF file")) .Text(LOCTEXT("TopText", "Import a glTF file"))
] ]
+ SVerticalBox::Slot() + SVerticalBox::Slot()
.Padding(1.0f) .Padding(1.0f)
.HAlign(HAlign_Fill) .HAlign(HAlign_Fill)
.VAlign(VAlign_Center) .VAlign(VAlign_Center)
[ [
SNew(SHorizontalBox) SNew(SHorizontalBox)
+ SHorizontalBox::Slot() + SHorizontalBox::Slot()
.Padding(1.0f) .Padding(1.0f)
.HAlign(HAlign_Center) .HAlign(HAlign_Center)
.VAlign(VAlign_Center) .VAlign(VAlign_Center)
[ [
SNew(STextBlock) SNew(STextBlock)
.Text(LOCTEXT("ImportTranslation", "Import Translation")) .Text(LOCTEXT("ImportTranslation", "Import Translation"))
] ]
+ SHorizontalBox::Slot() + SHorizontalBox::Slot()
.Padding(1.0f) .Padding(1.0f)
.HAlign(HAlign_Center) .HAlign(HAlign_Center)
.VAlign(VAlign_Center) .VAlign(VAlign_Center)
[ [
SNew(SVectorInputBox) SNew(SVectorInputBox)
.X_Raw(this, &FGLTFLoaderModule::GetImportTX) .X_Raw(this, &FGLTFLoaderModule::GetImportTX)
.Y_Raw(this, &FGLTFLoaderModule::GetImportTY) .Y_Raw(this, &FGLTFLoaderModule::GetImportTY)
.Z_Raw(this, &FGLTFLoaderModule::GetImportTZ) .Z_Raw(this, &FGLTFLoaderModule::GetImportTZ)
.bColorAxisLabels(true) .bColorAxisLabels(true)
.AllowResponsiveLayout(true) .AllowResponsiveLayout(true)
.OnXChanged_Raw(this, &FGLTFLoaderModule::SetImportTX) .OnXChanged_Raw(this, &FGLTFLoaderModule::SetImportTX)
.OnYChanged_Raw(this, &FGLTFLoaderModule::SetImportTY) .OnYChanged_Raw(this, &FGLTFLoaderModule::SetImportTY)
.OnZChanged_Raw(this, &FGLTFLoaderModule::SetImportTZ) .OnZChanged_Raw(this, &FGLTFLoaderModule::SetImportTZ)
] ]
] ]
+ SVerticalBox::Slot() + SVerticalBox::Slot()
.Padding(1.0f) .Padding(1.0f)
.HAlign(HAlign_Fill) .HAlign(HAlign_Fill)
.VAlign(VAlign_Center) .VAlign(VAlign_Center)
[ [
SNew(SHorizontalBox) SNew(SHorizontalBox)
+ SHorizontalBox::Slot() + SHorizontalBox::Slot()
.Padding(1.0f) .Padding(1.0f)
.HAlign(HAlign_Center) .HAlign(HAlign_Center)
.VAlign(VAlign_Center) .VAlign(VAlign_Center)
[ [
SNew(STextBlock) SNew(STextBlock)
.Text(LOCTEXT("ImportRotation", "Import Rotation")) .Text(LOCTEXT("ImportRotation", "Import Rotation"))
] ]
+ SHorizontalBox::Slot() + SHorizontalBox::Slot()
.Padding(1.0f) .Padding(1.0f)
.HAlign(HAlign_Center) .HAlign(HAlign_Center)
.VAlign(VAlign_Center) .VAlign(VAlign_Center)
[ [
SNew(SRotatorInputBox) SNew(SRotatorInputBox)
.Pitch_Raw(this, &FGLTFLoaderModule::GetImportRPitch) .Pitch_Raw(this, &FGLTFLoaderModule::GetImportRPitch)
.Yaw_Raw(this, &FGLTFLoaderModule::GetImportRYaw) .Yaw_Raw(this, &FGLTFLoaderModule::GetImportRYaw)
.Roll_Raw(this, &FGLTFLoaderModule::GetImportRRoll) .Roll_Raw(this, &FGLTFLoaderModule::GetImportRRoll)
.bColorAxisLabels(true) .bColorAxisLabels(true)
.AllowResponsiveLayout(true) .AllowResponsiveLayout(true)
.OnPitchChanged_Raw(this, &FGLTFLoaderModule::SetImportRPitch) .OnPitchChanged_Raw(this, &FGLTFLoaderModule::SetImportRPitch)
.OnYawChanged_Raw(this, &FGLTFLoaderModule::SetImportRYaw) .OnYawChanged_Raw(this, &FGLTFLoaderModule::SetImportRYaw)
.OnRollChanged_Raw(this, &FGLTFLoaderModule::SetImportRRoll) .OnRollChanged_Raw(this, &FGLTFLoaderModule::SetImportRRoll)
] ]
] ]
+ SVerticalBox::Slot() + SVerticalBox::Slot()
.Padding(1.0f) .Padding(1.0f)
.HAlign(HAlign_Fill) .HAlign(HAlign_Fill)
.VAlign(VAlign_Center) .VAlign(VAlign_Center)
[ [
SNew(SHorizontalBox) SNew(SHorizontalBox)
+ SHorizontalBox::Slot() + SHorizontalBox::Slot()
.Padding(1.0f) .Padding(1.0f)
.HAlign(HAlign_Center) .HAlign(HAlign_Center)
.VAlign(VAlign_Center) .VAlign(VAlign_Center)
[ [
SNew(STextBlock) SNew(STextBlock)
.Text(LOCTEXT("ImportScale", "Import Scale")) .Text(LOCTEXT("ImportScale", "Import Scale"))
] ]
+ SHorizontalBox::Slot() + SHorizontalBox::Slot()
.Padding(1.0f) .Padding(1.0f)
.HAlign(HAlign_Center) .HAlign(HAlign_Center)
.VAlign(VAlign_Center) .VAlign(VAlign_Center)
[ [
SNew(SNumericEntryBox<float>) SNew(SNumericEntryBox<float>)
.Value_Raw(this, &FGLTFLoaderModule::GetImportScale) .Value_Raw(this, &FGLTFLoaderModule::GetImportScale)
.OnValueChanged_Raw(this, &FGLTFLoaderModule::SetImportScale) .OnValueChanged_Raw(this, &FGLTFLoaderModule::SetImportScale)
] ]
] ]
+ SVerticalBox::Slot() + SVerticalBox::Slot()
.Padding(1.0f) .Padding(1.0f)
.HAlign(HAlign_Fill) .HAlign(HAlign_Fill)
.VAlign(VAlign_Center) .VAlign(VAlign_Center)
[ [
SNew(SHorizontalBox) SNew(SHorizontalBox)
+ SHorizontalBox::Slot() + SHorizontalBox::Slot()
.Padding(1.0f) .Padding(1.0f)
.HAlign(HAlign_Center) .HAlign(HAlign_Center)
.VAlign(VAlign_Center) .VAlign(VAlign_Center)
[ [
SNew(STextBlock) SNew(STextBlock)
.Text(LOCTEXT("CorrectUp", "Correct Y up to Z up")) .Text(LOCTEXT("CorrectUp", "Correct Y up to Z up"))
] ]
+ SHorizontalBox::Slot() + SHorizontalBox::Slot()
.Padding(1.0f) .Padding(1.0f)
.HAlign(HAlign_Center) .HAlign(HAlign_Center)
.VAlign(VAlign_Center) .VAlign(VAlign_Center)
[ [
SNew(SCheckBox) SNew(SCheckBox)
.IsChecked_Raw(this, &FGLTFLoaderModule::GetCorrectUp) .IsChecked_Raw(this, &FGLTFLoaderModule::GetCorrectUp)
.OnCheckStateChanged_Raw(this, &FGLTFLoaderModule::SetCorrectUp) .OnCheckStateChanged_Raw(this, &FGLTFLoaderModule::SetCorrectUp)
] ]
] ]
+ SVerticalBox::Slot() + SVerticalBox::Slot()
.Padding(1.0f) .Padding(1.0f)
.HAlign(HAlign_Center) .HAlign(HAlign_Center)
[ [
SNew(SBox) SNew(SBox)
.HAlign(HAlign_Center) .HAlign(HAlign_Center)
.VAlign(VAlign_Center) .VAlign(VAlign_Center)
[ [
SNew(SButton) SNew(SButton)
.OnClicked_Raw(this, &FGLTFLoaderModule::OpenImportWindowDelegateFunc) .OnClicked_Raw(this, &FGLTFLoaderModule::OpenImportWindowDelegateFunc)
.Content() .Content()
[ [
SNew(STextBlock) SNew(STextBlock)
.Text(LOCTEXT("ImportWindow", "Import File")) .Text(LOCTEXT("ImportWindow", "Import File"))
] ]
] ]
] ]
]; ];
} }
void FGLTFLoaderModule::OpenImportWindow() void FGLTFLoaderModule::OpenImportWindow()
{ {
TArray<FString> Filenames; TArray<FString> Filenames;
if (FDesktopPlatformModule::Get()->OpenFileDialog(nullptr, if (FDesktopPlatformModule::Get()->OpenFileDialog(nullptr,
TEXT("Choose a GLTF file to import"), TEXT("Choose a GLTF file to import"),
TEXT(""), TEXT(""),
TEXT(""), TEXT(""),
TEXT("GL Transmission Format files (*.gltf)|*.gltf"), TEXT("GL Transmission Format files (*.gltf)|*.gltf"),
EFileDialogFlags::None, EFileDialogFlags::None,
Filenames)) Filenames))
{ {
for (FString File : Filenames) for (FString File : Filenames)
{ {
UE_LOG(LogTemp, Log, TEXT("File: %s"), *File); UE_LOG(LogTemp, Log, TEXT("File: %s"), *File);
} }
FAssetToolsModule& AssetToolsModule = FModuleManager::Get().LoadModuleChecked<FAssetToolsModule>("AssetTools"); FAssetToolsModule& AssetToolsModule = FModuleManager::Get().LoadModuleChecked<FAssetToolsModule>("AssetTools");
//TArray<FString> Filenames; //TArray<FString> Filenames;
//Filenames.Add("D:/Dropbox/Studium/MasterArbeit/glTF-Sample-Models/1.0/Box/glTF/Box.gltf"); //Filenames.Add("D:/Dropbox/Studium/MasterArbeit/glTF-Sample-Models/1.0/Box/glTF/Box.gltf");
AssetToolsModule.Get().ImportAssets(Filenames, FString("/Game/Content")); AssetToolsModule.Get().ImportAssets(Filenames, FString("/Game/Content"));
} }
} }
void FGLTFLoaderModule::PluginButtonClicked() void FGLTFLoaderModule::PluginButtonClicked()
{ {
FGlobalTabmanager::Get()->InvokeTab(GLTFLoaderTabName); FGlobalTabmanager::Get()->InvokeTab(GLTFLoaderTabName);
} }
void FGLTFLoaderModule::AddMenuExtension(FMenuBuilder& Builder) void FGLTFLoaderModule::AddMenuExtension(FMenuBuilder& Builder)
{ {
Builder.AddMenuEntry(FGLTFLoaderCommands::Get().OpenPluginWindow); Builder.AddMenuEntry(FGLTFLoaderCommands::Get().OpenPluginWindow);
} }
void FGLTFLoaderModule::AddToolbarExtension(FToolBarBuilder& Builder) void FGLTFLoaderModule::AddToolbarExtension(FToolBarBuilder& Builder)
{ {
Builder.AddToolBarButton(FGLTFLoaderCommands::Get().OpenPluginWindow); Builder.AddToolBarButton(FGLTFLoaderCommands::Get().OpenPluginWindow);
} }
// Delegate setters // Delegate setters
void FGLTFLoaderModule::SetImportTX(float Value) { ImportOptions.ImportTranslation.X = Value; } void FGLTFLoaderModule::SetImportTX(float Value) { ImportOptions.ImportTranslation.X = Value; }
void FGLTFLoaderModule::SetImportTY(float Value) { ImportOptions.ImportTranslation.Y = Value; } void FGLTFLoaderModule::SetImportTY(float Value) { ImportOptions.ImportTranslation.Y = Value; }
void FGLTFLoaderModule::SetImportTZ(float Value) { ImportOptions.ImportTranslation.Z = Value; } void FGLTFLoaderModule::SetImportTZ(float Value) { ImportOptions.ImportTranslation.Z = Value; }
void FGLTFLoaderModule::SetImportRPitch(float Value) { ImportOptions.ImportRotation.Pitch = Value; } void FGLTFLoaderModule::SetImportRPitch(float Value) { ImportOptions.ImportRotation.Pitch = Value; }
void FGLTFLoaderModule::SetImportRYaw(float Value) { ImportOptions.ImportRotation.Yaw = Value; } void FGLTFLoaderModule::SetImportRYaw(float Value) { ImportOptions.ImportRotation.Yaw = Value; }
void FGLTFLoaderModule::SetImportRRoll(float Value) { ImportOptions.ImportRotation.Roll = Value; } void FGLTFLoaderModule::SetImportRRoll(float Value) { ImportOptions.ImportRotation.Roll = Value; }
void FGLTFLoaderModule::SetImportScale(float Value) { ImportOptions.ImportUniformScale = Value; } void FGLTFLoaderModule::SetImportScale(float Value) { ImportOptions.ImportUniformScale = Value; }
void FGLTFLoaderModule::SetCorrectUp(ECheckBoxState Value) { ImportOptions.bCorrectUpDirection = (Value == ECheckBoxState::Checked); } void FGLTFLoaderModule::SetCorrectUp(ECheckBoxState Value) { ImportOptions.bCorrectUpDirection = (Value == ECheckBoxState::Checked); }
// Delegate getters // Delegate getters
TOptional<float> FGLTFLoaderModule::GetImportTX() const { return ImportOptions.ImportTranslation.X; } TOptional<float> FGLTFLoaderModule::GetImportTX() const { return ImportOptions.ImportTranslation.X; }
TOptional<float> FGLTFLoaderModule::GetImportTY() const { return ImportOptions.ImportTranslation.Y; } TOptional<float> FGLTFLoaderModule::GetImportTY() const { return ImportOptions.ImportTranslation.Y; }
TOptional<float> FGLTFLoaderModule::GetImportTZ() const { return ImportOptions.ImportTranslation.Z; } TOptional<float> FGLTFLoaderModule::GetImportTZ() const { return ImportOptions.ImportTranslation.Z; }
TOptional<float> FGLTFLoaderModule::GetImportRPitch() const { return ImportOptions.ImportRotation.Pitch; } TOptional<float> FGLTFLoaderModule::GetImportRPitch() const { return ImportOptions.ImportRotation.Pitch; }
TOptional<float> FGLTFLoaderModule::GetImportRYaw() const { return ImportOptions.ImportRotation.Yaw; } TOptional<float> FGLTFLoaderModule::GetImportRYaw() const { return ImportOptions.ImportRotation.Yaw; }
TOptional<float> FGLTFLoaderModule::GetImportRRoll() const { return ImportOptions.ImportRotation.Roll; } TOptional<float> FGLTFLoaderModule::GetImportRRoll() const { return ImportOptions.ImportRotation.Roll; }
TOptional<float> FGLTFLoaderModule::GetImportScale() const { return ImportOptions.ImportUniformScale; } TOptional<float> FGLTFLoaderModule::GetImportScale() const { return ImportOptions.ImportUniformScale; }
ECheckBoxState FGLTFLoaderModule::GetCorrectUp() const { return ImportOptions.bCorrectUpDirection ? ECheckBoxState::Checked : ECheckBoxState::Unchecked; } ECheckBoxState FGLTFLoaderModule::GetCorrectUp() const { return ImportOptions.bCorrectUpDirection ? ECheckBoxState::Checked : ECheckBoxState::Unchecked; }
#undef LOCTEXT_NAMESPACE #undef LOCTEXT_NAMESPACE
IMPLEMENT_MODULE(FGLTFLoaderModule, GLTFLoader) IMPLEMENT_MODULE(FGLTFLoaderModule, GLTFLoader)
\ No newline at end of file
// Some copyright should be here... // Some copyright should be here...
#include "SlateBasics.h" #include "SlateBasics.h"
#include "GLTFLoader.h" #include "GLTFLoader.h"
// You should place include statements to your module's private header files here. You only need to // You should place include statements to your module's private header files here. You only need to
// add includes for headers that are used in most of your module's source files though. // add includes for headers that are used in most of your module's source files though.
\ No newline at end of file
// Some copyright should be here... // Some copyright should be here...
#include "GLTFLoaderPrivatePCH.h" #include "GLTFLoaderPrivatePCH.h"
#include "GLTFLoaderStyle.h" #include "GLTFLoaderStyle.h"
#include "SlateGameResources.h" #include "SlateGameResources.h"
#include "IPluginManager.h" #include "IPluginManager.h"
TSharedPtr< FSlateStyleSet > FGLTFLoaderStyle::StyleInstance = NULL; TSharedPtr< FSlateStyleSet > FGLTFLoaderStyle::StyleInstance = NULL;
void FGLTFLoaderStyle::Initialize() void FGLTFLoaderStyle::Initialize()
{ {
if (!StyleInstance.IsValid()) if (!StyleInstance.IsValid())
{ {
StyleInstance = Create(); StyleInstance = Create();
FSlateStyleRegistry::RegisterSlateStyle(*StyleInstance); FSlateStyleRegistry::RegisterSlateStyle(*StyleInstance);
} }
} }
void FGLTFLoaderStyle::Shutdown() void FGLTFLoaderStyle::Shutdown()
{ {
FSlateStyleRegistry::UnRegisterSlateStyle(*StyleInstance); FSlateStyleRegistry::UnRegisterSlateStyle(*StyleInstance);
ensure(StyleInstance.IsUnique()); ensure(StyleInstance.IsUnique());
StyleInstance.Reset(); StyleInstance.Reset();
} }
FName FGLTFLoaderStyle::GetStyleSetName() FName FGLTFLoaderStyle::GetStyleSetName()
{ {
static FName StyleSetName(TEXT("GLTFLoaderStyle")); static FName StyleSetName(TEXT("GLTFLoaderStyle"));
return StyleSetName; return StyleSetName;
} }
#define IMAGE_BRUSH( RelativePath, ... ) FSlateImageBrush( Style->RootToContentDir( RelativePath, TEXT(".png") ), __VA_ARGS__ ) #define IMAGE_BRUSH( RelativePath, ... ) FSlateImageBrush( Style->RootToContentDir( RelativePath, TEXT(".png") ), __VA_ARGS__ )
#define BOX_BRUSH( RelativePath, ... ) FSlateBoxBrush( Style->RootToContentDir( RelativePath, TEXT(".png") ), __VA_ARGS__ ) #define BOX_BRUSH( RelativePath, ... ) FSlateBoxBrush( Style->RootToContentDir( RelativePath, TEXT(".png") ), __VA_ARGS__ )
#define BORDER_BRUSH( RelativePath, ... ) FSlateBorderBrush( Style->RootToContentDir( RelativePath, TEXT(".png") ), __VA_ARGS__ ) #define BORDER_BRUSH( RelativePath, ... ) FSlateBorderBrush( Style->RootToContentDir( RelativePath, TEXT(".png") ), __VA_ARGS__ )
#define TTF_FONT( RelativePath, ... ) FSlateFontInfo( Style->RootToContentDir( RelativePath, TEXT(".ttf") ), __VA_ARGS__ ) #define TTF_FONT( RelativePath, ... ) FSlateFontInfo( Style->RootToContentDir( RelativePath, TEXT(".ttf") ), __VA_ARGS__ )
#define OTF_FONT( RelativePath, ... ) FSlateFontInfo( Style->RootToContentDir( RelativePath, TEXT(".otf") ), __VA_ARGS__ ) #define OTF_FONT( RelativePath, ... ) FSlateFontInfo( Style->RootToContentDir( RelativePath, TEXT(".otf") ), __VA_ARGS__ )
const FVector2D Icon16x16(16.0f, 16.0f); const FVector2D Icon16x16(16.0f, 16.0f);
const FVector2D Icon20x20(20.0f, 20.0f); const FVector2D Icon20x20(20.0f, 20.0f);
const FVector2D Icon40x40(40.0f, 40.0f); const FVector2D Icon40x40(40.0f, 40.0f);
TSharedRef< FSlateStyleSet > FGLTFLoaderStyle::Create() TSharedRef< FSlateStyleSet > FGLTFLoaderStyle::Create()
{ {
TSharedRef< FSlateStyleSet > Style = MakeShareable(new FSlateStyleSet("GLTFLoaderStyle")); TSharedRef< FSlateStyleSet > Style = MakeShareable(new FSlateStyleSet("GLTFLoaderStyle"));
Style->SetContentRoot(IPluginManager::Get().FindPlugin("GLTFLoader")->GetBaseDir() / TEXT("Resources")); Style->SetContentRoot(IPluginManager::Get().FindPlugin("GLTFLoader")->GetBaseDir() / TEXT("Resources"));
Style->Set("GLTFLoader.OpenPluginWindow", new IMAGE_BRUSH(TEXT("ButtonIcon_40x"), Icon40x40)); Style->Set("GLTFLoader.OpenPluginWindow", new IMAGE_BRUSH(TEXT("ButtonIcon_40x"), Icon40x40));
return Style; return Style;
} }
#undef IMAGE_BRUSH #undef IMAGE_BRUSH
#undef BOX_BRUSH #undef BOX_BRUSH
#undef BORDER_BRUSH #undef BORDER_BRUSH
#undef TTF_FONT #undef TTF_FONT
#undef OTF_FONT #undef OTF_FONT
void FGLTFLoaderStyle::ReloadTextures() void FGLTFLoaderStyle::ReloadTextures()
{ {
FSlateApplication::Get().GetRenderer()->ReloadTextureResources(); FSlateApplication::Get().GetRenderer()->ReloadTextureResources();
} }
const ISlateStyle& FGLTFLoaderStyle::Get() const ISlateStyle& FGLTFLoaderStyle::Get()
{ {
return *StyleInstance; return *StyleInstance;
} }
#include "GLTFLoaderPrivatePCH.h" #include "GLTFLoaderPrivatePCH.h"
#include <locale> #include <locale>
#include <codecvt> #include <codecvt>
#define TINYGLTF_LOADER_IMPLEMENTATION #define TINYGLTF_LOADER_IMPLEMENTATION
#define STB_IMAGE_IMPLEMENTATION #define STB_IMAGE_IMPLEMENTATION
#include "tiny_gltf_loader.h" #include "tiny_gltf_loader.h"
#include "GLTFMeshBuilder.h" #include "GLTFMeshBuilder.h"
#include "GLTFLoaderCommands.h" #include "GLTFLoaderCommands.h"
//#include "Editor/UnrealEd/Classes/Factories/Factory.h" //#include "Editor/UnrealEd/Classes/Factories/Factory.h"
// These replace TargetPlatform.h since it can't seem to find the right paths from here // These replace TargetPlatform.h since it can't seem to find the right paths from here
#include "ModuleManager.h" #include "ModuleManager.h"
#include "Developer/TargetPlatform/Public/Interfaces/TargetDeviceId.h" #include "Developer/TargetPlatform/Public/Interfaces/TargetDeviceId.h"
#include "Developer/TargetPlatform/Public/Interfaces/ITargetDevice.h" #include "Developer/TargetPlatform/Public/Interfaces/ITargetDevice.h"
#include "Developer/TargetPlatform/Public/Interfaces/ITargetPlatform.h" #include "Developer/TargetPlatform/Public/Interfaces/ITargetPlatform.h"
#include "Developer/TargetPlatform/Public/Interfaces/ITargetPlatformModule.h" #include "Developer/TargetPlatform/Public/Interfaces/ITargetPlatformModule.h"
#include "Developer/TargetPlatform/Public/Interfaces/ITargetPlatformManagerModule.h" #include "Developer/TargetPlatform/Public/Interfaces/ITargetPlatformManagerModule.h"
#include "UnrealEd.h" #include "UnrealEd.h"
#include "Developer/RawMesh/Public/RawMesh.h" #include "Developer/RawMesh/Public/RawMesh.h"
#include "Developer/MeshUtilities/Public/MeshUtilities.h" #include "Developer/MeshUtilities/Public/MeshUtilities.h"
#include "Engine.h" #include "Engine.h"
#include "StaticMeshResources.h" #include "StaticMeshResources.h"
#include "TextureLayout.h" #include "TextureLayout.h"
#include "ObjectTools.h" #include "ObjectTools.h"
#include "PackageTools.h" #include "PackageTools.h"
#include "Editor/UnrealEd/Classes/Factories/FbxStaticMeshImportData.h" #include "Editor/UnrealEd/Classes/Factories/FbxStaticMeshImportData.h"
#include "../Private/GeomFitUtils.h" #include "../Private/GeomFitUtils.h"
#include "FbxErrors.h" #include "FbxErrors.h"
#include "Engine/StaticMeshSocket.h" #include "Engine/StaticMeshSocket.h"
#include "Engine/Polys.h" #include "Engine/Polys.h"
#include "PhysicsEngine/BodySetup.h" #include "PhysicsEngine/BodySetup.h"
/// @cond /// @cond
// Syntactic sugar to neatly map the TinyGLTF enum to the corresponding data type // Syntactic sugar to neatly map the TinyGLTF enum to the corresponding data type
// Adapted from http://stackoverflow.com/questions/1735796/is-it-possible-to-choose-a-c-generic-type-parameter-at-runtime // Adapted from http://stackoverflow.com/questions/1735796/is-it-possible-to-choose-a-c-generic-type-parameter-at-runtime
template<int Type> struct GLTFType; template<int Type> struct GLTFType;
template<> struct GLTFType <TINYGLTF_COMPONENT_TYPE_BYTE> { typedef int8 Type; }; template<> struct GLTFType <TINYGLTF_COMPONENT_TYPE_BYTE> { typedef int8 Type; };
template<> struct GLTFType <TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE> { typedef uint8 Type; }; template<> struct GLTFType <TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE> { typedef uint8 Type; };
template<> struct GLTFType <TINYGLTF_COMPONENT_TYPE_SHORT> { typedef int16 Type; }; template<> struct GLTFType <TINYGLTF_COMPONENT_TYPE_SHORT> { typedef int16 Type; };
template<> struct GLTFType <TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT> { typedef uint16 Type; }; template<> struct GLTFType <TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT> { typedef uint16 Type; };
template<> struct GLTFType <TINYGLTF_COMPONENT_TYPE_INT> { typedef int32 Type; }; template<> struct GLTFType <TINYGLTF_COMPONENT_TYPE_INT> { typedef int32 Type; };
template<> struct GLTFType <TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT> { typedef uint32 Type; }; template<> struct GLTFType <TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT> { typedef uint32 Type; };
template<> struct GLTFType <TINYGLTF_COMPONENT_TYPE_FLOAT> { typedef float Type; }; template<> struct GLTFType <TINYGLTF_COMPONENT_TYPE_FLOAT> { typedef float Type; };
template<> struct GLTFType <TINYGLTF_COMPONENT_TYPE_DOUBLE> { typedef double Type; }; template<> struct GLTFType <TINYGLTF_COMPONENT_TYPE_DOUBLE> { typedef double Type; };
template<> struct GLTFType <TINYGLTF_TYPE_VEC2> { typedef FVector2D Type; }; template<> struct GLTFType <TINYGLTF_TYPE_VEC2> { typedef FVector2D Type; };
template<> struct GLTFType <TINYGLTF_TYPE_VEC3> { typedef FVector Type; }; template<> struct GLTFType <TINYGLTF_TYPE_VEC3> { typedef FVector Type; };
template<> struct GLTFType <TINYGLTF_TYPE_VEC4> { typedef FVector4 Type; }; template<> struct GLTFType <TINYGLTF_TYPE_VEC4> { typedef FVector4 Type; };
/// @endcond /// @endcond
template <typename T> template <typename T>
bool GLTFMeshBuilder::ConvertAttrib(TArray<T> &OutArray, tinygltf::Mesh* Mesh, std::string AttribName, bool UseWedgeIndices, bool AutoSetArraySize) bool UUGLTFMeshBuilder::ConvertAttrib(TArray<T> &OutArray, tinygltf::Mesh* Mesh, std::string AttribName, bool UseWedgeIndices, bool AutoSetArraySize)
{ {
if (AttribName != "__WedgeIndices" && !HasAttribute(Mesh, AttribName)) if (AttribName != "__WedgeIndices" && !HasAttribute(Mesh, AttribName))
{ {
return false; return false;
} }
UE_LOG(LogTemp, Log, TEXT("%s"), *(FText::Format(FText::FromString("Importing data for attribute \"{0}\""), FText::FromString(ToFString(AttribName)))).ToString()); UE_LOG(LogTemp, Log, TEXT("%s"), *(FText::Format(FText::FromString("Importing data for attribute \"{0}\""), FText::FromString(ToFString(AttribName)))).ToString());
if (AutoSetArraySize)// This should always be false since for now we are just extending the array each time we want to add an element if (AutoSetArraySize)// This should always be false since for now we are just extending the array each time we want to add an element
{ {
int32 Size = 0; int32 Size = 0;
if (UseWedgeIndices) if (UseWedgeIndices)
{ {
for (auto Prim : Mesh->primitives) for (auto Prim : Mesh->primitives)
{ {
Size += GetNumWedges(&Prim); // Number of wedges Size += GetNumWedges(&Prim); // Number of wedges
} }
} }
else else
{ {
for (auto Prim : Mesh->primitives) for (auto Prim : Mesh->primitives)
{ {
Size += Scene->accessors[Prim.attributes.begin()->second].count; // Number of vertices Size += Scene->accessors[Prim.attributes.begin()->second].count; // Number of vertices
} }
} }
OutArray.SetNumUninitialized(Size); OutArray.SetNumUninitialized(Size);
} }
// Getting an attribute for individual triangle corners ("wedges") // Getting an attribute for individual triangle corners ("wedges")
else if (UseWedgeIndices && AttribName != "__WedgeIndices")// Make sure we don't try to access indices for the index array itself! else if (UseWedgeIndices && AttribName != "__WedgeIndices")// Make sure we don't try to access indices for the index array itself!
{ {
for (auto Prim : Mesh->primitives) for (auto Prim : Mesh->primitives)
{ {
std::string IndexAccessorName = Prim.indices; std::string IndexAccessorName = Prim.indices;
std::string AttribAccessorName = Prim.attributes[AttribName]; std::string AttribAccessorName = Prim.attributes[AttribName];
tinygltf::Accessor* IndexAccessor = &Scene->accessors[IndexAccessorName]; tinygltf::Accessor* IndexAccessor = &Scene->accessors[IndexAccessorName];
tinygltf::Accessor* AttribAccessor = &Scene->accessors[AttribAccessorName]; tinygltf::Accessor* AttribAccessor = &Scene->accessors[AttribAccessorName];
if (!IndexAccessor || !AttribAccessor) if (!IndexAccessor || !AttribAccessor)
{ {
AddTokenizedErrorMessage( AddTokenizedErrorMessage(
FTokenizedMessage::Create( FTokenizedMessage::Create(
EMessageSeverity::Warning, EMessageSeverity::Warning,
FText::FromString(FString("Invalid accessor"))), FText::FromString(FString("Invalid accessor"))),
FFbxErrors::Generic_Mesh_NoGeometry); FFbxErrors::Generic_Mesh_NoGeometry);
return false; return false;
} }
TArray<int32> IndexArray; TArray<int32> IndexArray;
TArray<T> VertArray; TArray<T> VertArray;
if (!GetBufferData(IndexArray, IndexAccessor) || !GetBufferData(VertArray, AttribAccessor)) if (!GetBufferData(IndexArray, IndexAccessor) || !GetBufferData(VertArray, AttribAccessor))
{ {
return false; return false;
} }
switch (Prim.mode) switch (Prim.mode)
{ {
case TINYGLTF_MODE_TRIANGLES: case TINYGLTF_MODE_TRIANGLES:
for (auto Index : IndexArray) for (auto Index : IndexArray)
{ {
OutArray.Add(VertArray[Index]); OutArray.Add(VertArray[Index]);
} }
break; break;
case TINYGLTF_MODE_TRIANGLE_STRIP: case TINYGLTF_MODE_TRIANGLE_STRIP:
OutArray.Add(VertArray[IndexArray[0]]); OutArray.Add(VertArray[IndexArray[0]]);
OutArray.Add(VertArray[IndexArray[1]]); OutArray.Add(VertArray[IndexArray[1]]);
OutArray.Add(VertArray[IndexArray[2]]); OutArray.Add(VertArray[IndexArray[2]]);
for (int i = 2; i < IndexAccessor->count - 2; i += 2) for (int i = 2; i < IndexAccessor->count - 2; i += 2)
{ {
// First triangle // First triangle
OutArray.Add(VertArray[IndexArray[ i ]]); OutArray.Add(VertArray[IndexArray[ i ]]);
OutArray.Add(VertArray[IndexArray[i-1]]); OutArray.Add(VertArray[IndexArray[i-1]]);
OutArray.Add(VertArray[IndexArray[i+1]]); OutArray.Add(VertArray[IndexArray[i+1]]);
// Second triangle // Second triangle
OutArray.Add(VertArray[IndexArray[ i ]]); OutArray.Add(VertArray[IndexArray[ i ]]);
OutArray.Add(VertArray[IndexArray[i+1]]); OutArray.Add(VertArray[IndexArray[i+1]]);
OutArray.Add(VertArray[IndexArray[i+2]]); OutArray.Add(VertArray[IndexArray[i+2]]);
} }
break; break;
case TINYGLTF_MODE_TRIANGLE_FAN: case TINYGLTF_MODE_TRIANGLE_FAN:
for (int i = 1; i < IndexAccessor->count - 1; ++i) for (int i = 1; i < IndexAccessor->count - 1; ++i)
{ {
// Triangle // Triangle
OutArray.Add(VertArray[IndexArray[ 0 ]]); OutArray.Add(VertArray[IndexArray[ 0 ]]);
OutArray.Add(VertArray[IndexArray[ i ]]); OutArray.Add(VertArray[IndexArray[ i ]]);
OutArray.Add(VertArray[IndexArray[i+1]]); OutArray.Add(VertArray[IndexArray[i+1]]);
} }
break; break;
default: default:
return false; return false;
} }
} }
} }
// Getting a vertex attribute // Getting a vertex attribute
else else
{ {
for (auto Prim : Mesh->primitives) for (auto Prim : Mesh->primitives)
{ {
std::string AccessorName; std::string AccessorName;
if (AttribName == "__WedgeIndices") if (AttribName == "__WedgeIndices")
{ {
AccessorName = Prim.indices; AccessorName = Prim.indices;
} }
else else
{ {
AccessorName = Prim.attributes[AttribName]; AccessorName = Prim.attributes[AttribName];
} }
if (!GetBufferData(OutArray, &Scene->accessors[AccessorName], true)) if (!GetBufferData(OutArray, &Scene->accessors[AccessorName], true))
{ {
return false; return false;
} }
} }
} }
return true; return true;
} }
// Retrieve a value from the buffer, implicitly accounting for endianness // Retrieve a value from the buffer, implicitly accounting for endianness
// Adapted from http://stackoverflow.com/questions/13001183/how-to-read-little-endian-integers-from-file-in-c // Adapted from http://stackoverflow.com/questions/13001183/how-to-read-little-endian-integers-from-file-in-c
template <typename T> T GLTFMeshBuilder::BufferValue(void* Data/*, uint8 Size*/) template <typename T> T UGLTFMeshBuilder::BufferValue(void* Data/*, uint8 Size*/)
{ {
T Ret = T(0); T Ret = T(0);
auto NewData = reinterpret_cast<unsigned char*>(Data); auto NewData = reinterpret_cast<unsigned char*>(Data);
for (int i = 0; i < sizeof(T); ++i) for (int i = 0; i < sizeof(T); ++i)
{ {
Ret |= (T)(NewData[i]) << (8 * i); Ret |= (T)(NewData[i]) << (8 * i);
} }
return Ret; return Ret;
} }
// Use unions for floats and doubles since they don't have a bitwise OR operator // Use unions for floats and doubles since they don't have a bitwise OR operator
template <> float GLTFMeshBuilder::BufferValue(void* Data) template <> float UGLTFMeshBuilder::BufferValue(void* Data)
{ {
assert(sizeof(float) == sizeof(int32)); assert(sizeof(float) == sizeof(int32));
union union
{ {
float Ret; float Ret;
int32 IntRet; int32 IntRet;
}; };
Ret = 0.0f; Ret = 0.0f;
auto NewData = reinterpret_cast<unsigned char*>(Data); auto NewData = reinterpret_cast<unsigned char*>(Data);
for (int i = 0; i < sizeof(int32); ++i) for (int i = 0; i < sizeof(int32); ++i)
{ {
IntRet |= (int32)(NewData[i]) << (8 * i); IntRet |= (int32)(NewData[i]) << (8 * i);
} }
return Ret; return Ret;
} }
template <> double GLTFMeshBuilder::BufferValue(void* Data) template <> double UGLTFMeshBuilder::BufferValue(void* Data)
{ {
assert(sizeof(float) == sizeof(int64)); assert(sizeof(float) == sizeof(int64));
union union
{ {
double Ret; double Ret;
int64 IntRet; int64 IntRet;
}; };
Ret = 0.0; Ret = 0.0;
auto NewData = reinterpret_cast<unsigned char*>(Data); auto NewData = reinterpret_cast<unsigned char*>(Data);
for (int i = 0; i < sizeof(int64); ++i) for (int i = 0; i < sizeof(int64); ++i)
{ {
IntRet |= (int64)(NewData[i]) << (8 * i); IntRet |= (int64)(NewData[i]) << (8 * i);
} }
return Ret; return Ret;
} }
/// @cond /// @cond
struct MaterialPair struct MaterialPair
{ {
tinygltf::Material* GLTFMaterial; tinygltf::Material* GLTFMaterial;
UMaterialInterface* Material; UMaterialInterface* Material;
}; };
/// @endcond /// @endcond
GLTFMeshBuilder::GLTFMeshBuilder(FString FilePath) UGLTFMeshBuilder::UGLTFMeshBuilder()
{ : UGLTFMeshBuilder(TEXT("H:/Repositories/MasterArbeit/glTF-Sample-Models/1.0/Duck/glTF/Duck.gltf"))
Loader = new tinygltf::TinyGLTFLoader; {
Scene = new tinygltf::Scene;
} //DO NOT USE
std::string TempError;
LoadSuccess = Loader->LoadFromFile((*Scene), TempError, ToStdString(FilePath)); UGLTFMeshBuilder::UGLTFMeshBuilder(FString FilePath)
Error = ToFString(TempError); {
} Loader = new tinygltf::TinyGLTFLoader;
Scene = new tinygltf::Scene;
GLTFMeshBuilder::~GLTFMeshBuilder()
{ std::string TempError;
delete Loader; LoadSuccess = Loader->LoadFromFile((*Scene), TempError, ToStdString(FilePath));
delete Scene; Error = ToFString(TempError);
} }
int32 GLTFMeshBuilder::GetMeshCount(FString NodeName) UGLTFMeshBuilder::~UGLTFMeshBuilder()
{ {
return (int32)Scene->nodes[ToStdString(NodeName)].meshes.size(); delete Loader;
} delete Scene;
}
FString GLTFMeshBuilder::GetRootNode()
{ int32 UGLTFMeshBuilder::GetMeshCount(FString NodeName)
for (auto ThisNode : Scene->nodes) {
{ return (int32)Scene->nodes[ToStdString(NodeName)].meshes.size();
bool ShouldReturn = false; }
for (auto ThatNode : Scene->nodes)
{ FString UGLTFMeshBuilder::GetRootNode()
if (FindInStdVector<std::string>(ThatNode.second.children, ThisNode.first) == -1) // If this node's name doesn't appear in any node's list of children {
{ for (auto ThisNode : Scene->nodes)
ShouldReturn = true; {
} bool ShouldReturn = false;
} for (auto ThatNode : Scene->nodes)
{
if (ShouldReturn) if (FindInStdVector<std::string>(ThatNode.second.children, ThisNode.first) == -1) // If this node's name doesn't appear in any node's list of children
{ {
return ToFString(ThisNode.first); ShouldReturn = true;
} }
} }
return FString("");
} if (ShouldReturn)
{
tinygltf::Node* GLTFMeshBuilder::GetMeshParentNode(tinygltf::Mesh* InMesh) return ToFString(ThisNode.first);
{ }
for (auto &Node : Scene->nodes) }
{ return FString("");
for (auto &MeshName : Node.second.meshes) }
{
if (&Scene->meshes[MeshName] == InMesh) tinygltf::Node* UGLTFMeshBuilder::GetMeshParentNode(tinygltf::Mesh* InMesh)
{ {
return &Node.second; for (auto &Node : Scene->nodes)
} {
} for (auto &MeshName : Node.second.meshes)
} {
return NULL; if (&Scene->meshes[MeshName] == InMesh)
} {
return &Node.second;
TArray<FString> GLTFMeshBuilder::GetMeshNames(FString NodeName, bool GetChildren) }
{ }
TArray<FString> MeshNameArray; }
return NULL;
for (auto Mesh : Scene->nodes[ToStdString(NodeName)].meshes) }
{
MeshNameArray.Add(ToFString(Mesh)); TArray<FString> UGLTFMeshBuilder::GetMeshNames(FString NodeName, bool GetChildren)
} {
TArray<FString> MeshNameArray;
if (GetChildren)
{ for (auto Mesh : Scene->nodes[ToStdString(NodeName)].meshes)
for (auto ChildName : Scene->nodes[ToStdString(NodeName)].children) {
{ MeshNameArray.Add(ToFString(Mesh));
MeshNameArray.Append(GetMeshNames(ToFString(ChildName))); }
}
} if (GetChildren)
{
return MeshNameArray; for (auto ChildName : Scene->nodes[ToStdString(NodeName)].children)
} {
MeshNameArray.Append(GetMeshNames(ToFString(ChildName)));
UStaticMeshComponent* GLTFMeshBuilder::ImportStaticMeshAsSingle(UObject* InParent, TArray<FString>& MeshNameArray, const FName InName, EObjectFlags Flags, UStaticMeshComponent* InStaticMesh) }
{ }
auto ImportOptions = FGLTFLoaderModule::ImportOptions;
return MeshNameArray;
int LODIndex = 0; }
bool bBuildStatus = true; UStaticMeshComponent* UGLTFMeshBuilder::ImportStaticMeshAsSingle(UObject* InParent, TArray<FString>& MeshNameArray, const FName InName, EObjectFlags Flags, UStaticMeshComponent* InStaticMesh)
{
// Make sure rendering is done - so we are not changing data being used by collision drawing. auto ImportOptions = FGLTFLoaderModule::ImportOptions;
FlushRenderingCommands();
int LODIndex = 0;
if (MeshNameArray.Num() == 0)
{ bool bBuildStatus = true;
return NULL;
} // Make sure rendering is done - so we are not changing data being used by collision drawing.
FlushRenderingCommands();
Parent = InParent;
if (MeshNameArray.Num() == 0)
FString MeshName = ObjectTools::SanitizeObjectName(InName.ToString()); {
return NULL;
// Parent package to place new meshes }
UPackage* Package = NULL;
Parent = InParent;
// create empty mesh
UStaticMeshComponent* StaticMeshComponent = NULL; FString MeshName = ObjectTools::SanitizeObjectName(InName.ToString());
UStaticMeshComponent* ExistingMesh = NULL; // Parent package to place new meshes
UObject* ExistingObject = NULL; UPackage* Package = NULL;
// A mapping of vertex positions to their color in the existing static mesh // create empty mesh
TMap<FVector, FColor> ExistingVertexColorData; UStaticMeshComponent* StaticMeshComponent = NULL;
FString NewPackageName; UStaticMeshComponent* ExistingMesh = NULL;
UObject* ExistingObject = NULL;
if( InStaticMesh == NULL || LODIndex == 0 )
{ // A mapping of vertex positions to their color in the existing static mesh
// Create a package for each mesh TMap<FVector, FColor> ExistingVertexColorData;
NewPackageName = FPackageName::GetLongPackagePath(Parent->GetOutermost()->GetName()) + TEXT("/") + MeshName;
NewPackageName = PackageTools::SanitizePackageName(NewPackageName); FString NewPackageName;
Package = CreatePackage(NULL, *NewPackageName);
if( InStaticMesh == NULL || LODIndex == 0 )
ExistingMesh = FindObject<UStaticMeshComponent>( Package, *MeshName ); {
ExistingObject = FindObject<UObject>( Package, *MeshName ); // Create a package for each mesh
} NewPackageName = FPackageName::GetLongPackagePath(Parent->GetOutermost()->GetName()) + TEXT("/") + MeshName;
NewPackageName = PackageTools::SanitizePackageName(NewPackageName);
if (ExistingMesh) Package = CreatePackage(NULL, *NewPackageName);
{
// Free any RHI resources for existing mesh before we re-create in place. ExistingMesh = FindObject<UStaticMeshComponent>( Package, *MeshName );
ExistingMesh->PreEditChange(NULL); ExistingObject = FindObject<UObject>( Package, *MeshName );
} }
else if (ExistingObject)
{ if (ExistingMesh)
// Replacing an object. Here we go! {
// Delete the existing object // Free any RHI resources for existing mesh before we re-create in place.
bool bDeleteSucceeded = ObjectTools::DeleteSingleObject( ExistingObject ); ExistingMesh->PreEditChange(NULL);
}
if (bDeleteSucceeded) else if (ExistingObject)
{ {
// Force GC so we can cleanly create a new asset (and not do an 'in place' replacement) // Replacing an object. Here we go!
CollectGarbage( GARBAGE_COLLECTION_KEEPFLAGS ); // Delete the existing object
bool bDeleteSucceeded = ObjectTools::DeleteSingleObject( ExistingObject );
// Create a package for each mesh
Package = CreatePackage(NULL, *NewPackageName); if (bDeleteSucceeded)
{
// Require the parent because it will have been invalidated from the garbage collection // Force GC so we can cleanly create a new asset (and not do an 'in place' replacement)
Parent = Package; CollectGarbage( GARBAGE_COLLECTION_KEEPFLAGS );
}
else // Create a package for each mesh
{ Package = CreatePackage(NULL, *NewPackageName);
// failed to delete
AddTokenizedErrorMessage(FTokenizedMessage::Create(EMessageSeverity::Error, FText::Format(FText::FromString(FString("{0} wasn't created.\n\nThe asset is referenced by other content.")), FText::FromString(MeshName))), FFbxErrors::Generic_CannotDeleteReferenced); // Require the parent because it will have been invalidated from the garbage collection
return NULL; Parent = Package;
} }
else
} {
// failed to delete
if( InStaticMesh != NULL && LODIndex > 0 ) AddTokenizedErrorMessage(FTokenizedMessage::Create(EMessageSeverity::Error, FText::Format(FText::FromString(FString("{0} wasn't created.\n\nThe asset is referenced by other content.")), FText::FromString(MeshName))), FFbxErrors::Generic_CannotDeleteReferenced);
{ return NULL;
StaticMeshComponent = InStaticMesh; }
}
else }
{
StaticMeshComponent = NewObject<UStaticMeshComponent>(Package, FName(*MeshName), Flags | RF_Public); if( InStaticMesh != NULL && LODIndex > 0 )
} {
StaticMeshComponent = InStaticMesh;
verify(StaticMeshComponent != NULL); }
else
if (StaticMeshComponent->GetStaticMesh() == NULL ) { {
UStaticMesh *mesh = NewObject<UStaticMesh>(Package); StaticMeshComponent = NewObject<UStaticMeshComponent>(Package, FName(*MeshName), Flags | RF_Public);
StaticMeshComponent->SetStaticMesh(mesh); }
}
verify(StaticMeshComponent->GetStaticMesh() != NULL); verify(StaticMeshComponent != NULL);
if (StaticMeshComponent->GetStaticMesh()->SourceModels.Num() < LODIndex+1) if (StaticMeshComponent->GetStaticMesh() == NULL ) {
{ UStaticMesh *mesh = NewObject<UStaticMesh>(Package);
// Add one LOD StaticMeshComponent->SetStaticMesh(mesh);
//StaticMeshComponent->GetStaticMesh()->SourceModels.AddUninitialized(); }
//StaticMeshComponent->GetStaticMesh()->SourceModels = new FStaticMeshSourceModel(); verify(StaticMeshComponent->GetStaticMesh() != NULL);
new(StaticMeshComponent->GetStaticMesh()->SourceModels) FStaticMeshSourceModel();
if (StaticMeshComponent->GetStaticMesh()->SourceModels.Num() < LODIndex+1)
if (StaticMeshComponent->GetStaticMesh()->SourceModels.Num() < LODIndex+1) {
{ // Add one LOD
LODIndex = StaticMeshComponent->GetStaticMesh()->SourceModels.Num() - 1; //StaticMeshComponent->GetStaticMesh()->SourceModels.AddUninitialized();
} //StaticMeshComponent->GetStaticMesh()->SourceModels = new FStaticMeshSourceModel();
} new(StaticMeshComponent->GetStaticMesh()->SourceModels) FStaticMeshSourceModel();
FStaticMeshSourceModel& SrcModel = StaticMeshComponent->GetStaticMesh()->SourceModels[LODIndex];
if (StaticMeshComponent->GetStaticMesh()->SourceModels.Num() < LODIndex+1)
if( InStaticMesh != NULL && LODIndex > 0 && !SrcModel.RawMeshBulkData->IsEmpty() ) {
{ LODIndex = StaticMeshComponent->GetStaticMesh()->SourceModels.Num() - 1;
// clear out the old mesh data }
FRawMesh EmptyRawMesh; }
SrcModel.RawMeshBulkData->SaveRawMesh( EmptyRawMesh ); FStaticMeshSourceModel& SrcModel = StaticMeshComponent->GetStaticMesh()->SourceModels[LODIndex];
}
if( InStaticMesh != NULL && LODIndex > 0 && !SrcModel.RawMeshBulkData->IsEmpty() )
// make sure it has a new lighting guid {
StaticMeshComponent->GetStaticMesh()->LightingGuid = FGuid::NewGuid(); // clear out the old mesh data
FRawMesh EmptyRawMesh;
// Set it to use textured lightmaps. Note that Build Lighting will do the error-checking (texcoordindex exists for all LODs, etc). SrcModel.RawMeshBulkData->SaveRawMesh( EmptyRawMesh );
StaticMeshComponent->GetStaticMesh()->LightMapResolution = 64; }
StaticMeshComponent->GetStaticMesh()->LightMapCoordinateIndex = 1;
// make sure it has a new lighting guid
FRawMesh NewRawMesh; StaticMeshComponent->GetStaticMesh()->LightingGuid = FGuid::NewGuid();
SrcModel.RawMeshBulkData->LoadRawMesh(NewRawMesh);
// Set it to use textured lightmaps. Note that Build Lighting will do the error-checking (texcoordindex exists for all LODs, etc).
//for (FString Name : MeshNameArray) StaticMeshComponent->GetStaticMesh()->LightMapResolution = 64;
for(int i = 0; i < MeshNameArray.Num(); i++) StaticMeshComponent->GetStaticMesh()->LightMapCoordinateIndex = 1;
{
FString Name = MeshNameArray[i]; FRawMesh NewRawMesh;
SrcModel.RawMeshBulkData->LoadRawMesh(NewRawMesh);
tinygltf::Mesh* Mesh = &Scene->meshes[ToStdString(Name)];
//for (FString Name : MeshNameArray)
if (Mesh) for(int i = 0; i < MeshNameArray.Num(); i++)
{ {
for (auto Prim : Mesh->primitives) FString Name = MeshNameArray[i];
{
MeshMaterials.AddUnique(ToFString(Prim.material)); tinygltf::Mesh* Mesh = &Scene->meshes[ToStdString(Name)];
}
} if (Mesh)
} {
for (auto Prim : Mesh->primitives)
for (FString Name : MeshNameArray) {
{ MeshMaterials.AddUnique(ToFString(Prim.material));
tinygltf::Mesh* Mesh = &Scene->meshes[ToStdString(Name)]; }
}
if (Mesh) }
{
if (!BuildStaticMeshFromGeometry(Mesh, StaticMeshComponent, LODIndex, NewRawMesh)) for (FString Name : MeshNameArray)
{ {
bBuildStatus = false; tinygltf::Mesh* Mesh = &Scene->meshes[ToStdString(Name)];
break;
} if (Mesh)
} {
} if (!BuildStaticMeshFromGeometry(Mesh, StaticMeshComponent, LODIndex, NewRawMesh))
{
// Store the new raw mesh. bBuildStatus = false;
SrcModel.RawMeshBulkData->SaveRawMesh(NewRawMesh); break;
}
}
if (bBuildStatus) }
{
// Compress the materials array by removing any duplicates. // Store the new raw mesh.
bool bDoRemap = false; SrcModel.RawMeshBulkData->SaveRawMesh(NewRawMesh);
TArray<int32> MaterialMap;
TArray<tinygltf::Material*> UniqueMaterials;
for (int32 MaterialIndex = 0; MaterialIndex < MeshMaterials.Num(); ++MaterialIndex) if (bBuildStatus)
{ {
bool bUnique = true; // Compress the materials array by removing any duplicates.
for (int32 OtherMaterialIndex = MaterialIndex - 1; OtherMaterialIndex >= 0; --OtherMaterialIndex) bool bDoRemap = false;
{ TArray<int32> MaterialMap;
if (MeshMaterials[MaterialIndex] == MeshMaterials[OtherMaterialIndex]) TArray<tinygltf::Material*> UniqueMaterials;
{ for (int32 MaterialIndex = 0; MaterialIndex < MeshMaterials.Num(); ++MaterialIndex)
int32 UniqueIndex = MaterialMap[OtherMaterialIndex]; {
bool bUnique = true;
MaterialMap.Add(UniqueIndex); for (int32 OtherMaterialIndex = MaterialIndex - 1; OtherMaterialIndex >= 0; --OtherMaterialIndex)
bDoRemap = true; {
bUnique = false; if (MeshMaterials[MaterialIndex] == MeshMaterials[OtherMaterialIndex])
break; {
} int32 UniqueIndex = MaterialMap[OtherMaterialIndex];
}
if (bUnique) MaterialMap.Add(UniqueIndex);
{ bDoRemap = true;
int32 UniqueIndex = UniqueMaterials.Add(&Scene->materials[ToStdString(MeshMaterials[MaterialIndex])]); bUnique = false;
break;
MaterialMap.Add( UniqueIndex ); }
} }
} if (bUnique)
{
if (UniqueMaterials.Num() > 8) int32 UniqueIndex = UniqueMaterials.Add(&Scene->materials[ToStdString(MeshMaterials[MaterialIndex])]);
{
AddTokenizedErrorMessage( MaterialMap.Add( UniqueIndex );
FTokenizedMessage::Create( }
EMessageSeverity::Warning, }
FText::Format(FText::FromString(FString("StaticMesh has a large number({1}) of materials and may render inefficently. Consider breaking up the mesh into multiple Static Mesh Assets.")),
FText::AsNumber(UniqueMaterials.Num()) if (UniqueMaterials.Num() > 8)
)), {
FFbxErrors::StaticMesh_TooManyMaterials); AddTokenizedErrorMessage(
} FTokenizedMessage::Create(
EMessageSeverity::Warning,
// Sort materials based on _SkinXX in the name. FText::Format(FText::FromString(FString("StaticMesh has a large number({1}) of materials and may render inefficently. Consider breaking up the mesh into multiple Static Mesh Assets.")),
TArray<uint32> SortedMaterialIndex; FText::AsNumber(UniqueMaterials.Num())
for (int32 MaterialIndex = 0; MaterialIndex < MeshMaterials.Num(); ++MaterialIndex) )),
{ FFbxErrors::StaticMesh_TooManyMaterials);
int32 SkinIndex = 0xffff; }
int32 RemappedIndex = MaterialMap[MaterialIndex];
if (!SortedMaterialIndex.IsValidIndex(RemappedIndex)) // Sort materials based on _SkinXX in the name.
{ TArray<uint32> SortedMaterialIndex;
FString GLTFMatName = MeshMaterials[RemappedIndex]; for (int32 MaterialIndex = 0; MaterialIndex < MeshMaterials.Num(); ++MaterialIndex)
{
int32 Offset = GLTFMatName.Find(TEXT("_SKIN"), ESearchCase::IgnoreCase, ESearchDir::FromEnd); int32 SkinIndex = 0xffff;
if (Offset != INDEX_NONE) int32 RemappedIndex = MaterialMap[MaterialIndex];
{ if (!SortedMaterialIndex.IsValidIndex(RemappedIndex))
// Chop off the material name so we are left with the number in _SKINXX {
FString SkinXXNumber = GLTFMatName.Right(GLTFMatName.Len() - (Offset + 1)).RightChop(4); FString GLTFMatName = MeshMaterials[RemappedIndex];
if (SkinXXNumber.IsNumeric()) int32 Offset = GLTFMatName.Find(TEXT("_SKIN"), ESearchCase::IgnoreCase, ESearchDir::FromEnd);
{ if (Offset != INDEX_NONE)
SkinIndex = FPlatformString::Atoi( *SkinXXNumber ); {
bDoRemap = true; // Chop off the material name so we are left with the number in _SKINXX
} FString SkinXXNumber = GLTFMatName.Right(GLTFMatName.Len() - (Offset + 1)).RightChop(4);
}
if (SkinXXNumber.IsNumeric())
SortedMaterialIndex.Add(((uint32)SkinIndex << 16) | ((uint32)RemappedIndex & 0xffff)); {
} SkinIndex = FPlatformString::Atoi( *SkinXXNumber );
} bDoRemap = true;
SortedMaterialIndex.Sort(); }
}
TArray<tinygltf::Material*> SortedMaterials;
for (int32 SortedIndex = 0; SortedIndex < SortedMaterialIndex.Num(); ++SortedIndex) SortedMaterialIndex.Add(((uint32)SkinIndex << 16) | ((uint32)RemappedIndex & 0xffff));
{ }
int32 RemappedIndex = SortedMaterialIndex[SortedIndex] & 0xffff; }
SortedMaterials.Add(UniqueMaterials[RemappedIndex]); SortedMaterialIndex.Sort();
}
for (int32 MaterialIndex = 0; MaterialIndex < MaterialMap.Num(); ++MaterialIndex) TArray<tinygltf::Material*> SortedMaterials;
{ for (int32 SortedIndex = 0; SortedIndex < SortedMaterialIndex.Num(); ++SortedIndex)
for (int32 SortedIndex = 0; SortedIndex < SortedMaterialIndex.Num(); ++SortedIndex) {
{ int32 RemappedIndex = SortedMaterialIndex[SortedIndex] & 0xffff;
int32 RemappedIndex = SortedMaterialIndex[SortedIndex] & 0xffff; SortedMaterials.Add(UniqueMaterials[RemappedIndex]);
if (MaterialMap[MaterialIndex] == RemappedIndex) }
{ for (int32 MaterialIndex = 0; MaterialIndex < MaterialMap.Num(); ++MaterialIndex)
MaterialMap[MaterialIndex] = SortedIndex; {
break; for (int32 SortedIndex = 0; SortedIndex < SortedMaterialIndex.Num(); ++SortedIndex)
} {
} int32 RemappedIndex = SortedMaterialIndex[SortedIndex] & 0xffff;
} if (MaterialMap[MaterialIndex] == RemappedIndex)
{
// Remap material indices. MaterialMap[MaterialIndex] = SortedIndex;
int32 MaxMaterialIndex = 0; break;
int32 FirstOpenUVChannel = 1; }
{ }
FRawMesh LocalRawMesh; }
SrcModel.RawMeshBulkData->LoadRawMesh(LocalRawMesh);
// Remap material indices.
if (bDoRemap) int32 MaxMaterialIndex = 0;
{ int32 FirstOpenUVChannel = 1;
for (int32 TriIndex = 0; TriIndex < LocalRawMesh.FaceMaterialIndices.Num(); ++TriIndex) {
{ FRawMesh LocalRawMesh;
LocalRawMesh.FaceMaterialIndices[TriIndex] = MaterialMap[LocalRawMesh.FaceMaterialIndices[TriIndex]]; SrcModel.RawMeshBulkData->LoadRawMesh(LocalRawMesh);
}
} if (bDoRemap)
{
// Compact material indices so that we won't have any sections with zero triangles. for (int32 TriIndex = 0; TriIndex < LocalRawMesh.FaceMaterialIndices.Num(); ++TriIndex)
LocalRawMesh.CompactMaterialIndices(); {
LocalRawMesh.FaceMaterialIndices[TriIndex] = MaterialMap[LocalRawMesh.FaceMaterialIndices[TriIndex]];
// Also compact the sorted materials array. }
if (LocalRawMesh.MaterialIndexToImportIndex.Num() > 0) }
{
TArray<tinygltf::Material*> OldSortedMaterials; // Compact material indices so that we won't have any sections with zero triangles.
LocalRawMesh.CompactMaterialIndices();
Exchange(OldSortedMaterials,SortedMaterials);
SortedMaterials.Empty(LocalRawMesh.MaterialIndexToImportIndex.Num()); // Also compact the sorted materials array.
for (int32 MaterialIndex = 0; MaterialIndex < LocalRawMesh.MaterialIndexToImportIndex.Num(); ++MaterialIndex) if (LocalRawMesh.MaterialIndexToImportIndex.Num() > 0)
{ {
tinygltf::Material* Material; TArray<tinygltf::Material*> OldSortedMaterials;
int32 ImportIndex = LocalRawMesh.MaterialIndexToImportIndex[MaterialIndex];
if (OldSortedMaterials.IsValidIndex(ImportIndex)) Exchange(OldSortedMaterials,SortedMaterials);
{ SortedMaterials.Empty(LocalRawMesh.MaterialIndexToImportIndex.Num());
Material = OldSortedMaterials[ImportIndex]; for (int32 MaterialIndex = 0; MaterialIndex < LocalRawMesh.MaterialIndexToImportIndex.Num(); ++MaterialIndex)
} {
SortedMaterials.Add(Material); tinygltf::Material* Material;
} int32 ImportIndex = LocalRawMesh.MaterialIndexToImportIndex[MaterialIndex];
} if (OldSortedMaterials.IsValidIndex(ImportIndex))
{
for (int32 TriIndex = 0; TriIndex < LocalRawMesh.FaceMaterialIndices.Num(); ++TriIndex) Material = OldSortedMaterials[ImportIndex];
{ }
MaxMaterialIndex = FMath::Max<int32>(MaxMaterialIndex,LocalRawMesh.FaceMaterialIndices[TriIndex]); SortedMaterials.Add(Material);
} }
}
for( int32 i = 0; i < MAX_MESH_TEXTURE_COORDS; i++ )
{ for (int32 TriIndex = 0; TriIndex < LocalRawMesh.FaceMaterialIndices.Num(); ++TriIndex)
if( LocalRawMesh.WedgeTexCoords[i].Num() == 0 ) {
{ MaxMaterialIndex = FMath::Max<int32>(MaxMaterialIndex,LocalRawMesh.FaceMaterialIndices[TriIndex]);
FirstOpenUVChannel = i; }
break;
} for( int32 i = 0; i < MAX_MESH_TEXTURE_COORDS; i++ )
} {
if( LocalRawMesh.WedgeTexCoords[i].Num() == 0 )
SrcModel.RawMeshBulkData->SaveRawMesh(LocalRawMesh); {
} FirstOpenUVChannel = i;
break;
// Setup per-section info and the materials array. }
if (LODIndex == 0) }
{
StaticMeshComponent->GetMaterial(0); SrcModel.RawMeshBulkData->SaveRawMesh(LocalRawMesh);
//TODO check if right }
//StaticMeshComponent->Materials.Empty();
StaticMeshComponent->GetNumMaterials(); // Setup per-section info and the materials array.
} if (LODIndex == 0)
{
// Build a new map of sections with the unique material set StaticMeshComponent->GetMaterial(0);
FMeshSectionInfoMap NewMap; //TODO check if right
int32 NumMaterials = FMath::Min(SortedMaterials.Num(),MaxMaterialIndex+1); //StaticMeshComponent->Materials.Empty();
for (int32 MaterialIndex = 0; MaterialIndex < NumMaterials; ++MaterialIndex) StaticMeshComponent->GetNumMaterials();
{ }
FMeshSectionInfo Info = StaticMeshComponent->GetStaticMesh()->SectionInfoMap.Get(LODIndex, MaterialIndex);
//FIXME // Build a new map of sections with the unique material set
//int32 Index = StaticMeshComponent->Materials.Add(ToUMaterial(SortedMaterials[MaterialIndex])); FMeshSectionInfoMap NewMap;
int32 NumMaterials = FMath::Min(SortedMaterials.Num(),MaxMaterialIndex+1);
//Info.MaterialIndex = Index; for (int32 MaterialIndex = 0; MaterialIndex < NumMaterials; ++MaterialIndex)
//NewMap.Set( LODIndex, MaterialIndex, Info); {
} FMeshSectionInfo Info = StaticMeshComponent->GetStaticMesh()->SectionInfoMap.Get(LODIndex, MaterialIndex);
//FIXME
// Copy the final section map into the static mesh //int32 Index = StaticMeshComponent->Materials.Add(ToUMaterial(SortedMaterials[MaterialIndex]));
StaticMeshComponent->GetStaticMesh()->SectionInfoMap.Clear();
StaticMeshComponent->GetStaticMesh()->SectionInfoMap.CopyFrom(NewMap); //Info.MaterialIndex = Index;
//NewMap.Set( LODIndex, MaterialIndex, Info);
FRawMesh LocalRawMesh; }
SrcModel.RawMeshBulkData->LoadRawMesh(LocalRawMesh);
// Copy the final section map into the static mesh
// Setup default LOD settings based on the selected LOD group. StaticMeshComponent->GetStaticMesh()->SectionInfoMap.Clear();
if (ExistingMesh == NULL && LODIndex == 0) StaticMeshComponent->GetStaticMesh()->SectionInfoMap.CopyFrom(NewMap);
{
ITargetPlatform* CurrentPlatform = GetTargetPlatformManagerRef().GetRunningTargetPlatform(); FRawMesh LocalRawMesh;
check(CurrentPlatform); SrcModel.RawMeshBulkData->LoadRawMesh(LocalRawMesh);
const FStaticMeshLODGroup& LODGroup = CurrentPlatform->GetStaticMeshLODSettings().GetLODGroup(ImportOptions.StaticMeshLODGroup);
int32 NumLODs = LODGroup.GetDefaultNumLODs(); // Setup default LOD settings based on the selected LOD group.
while (StaticMeshComponent->GetStaticMesh()->SourceModels.Num() < NumLODs) if (ExistingMesh == NULL && LODIndex == 0)
{ {
new (StaticMeshComponent->GetStaticMesh()->SourceModels) FStaticMeshSourceModel(); ITargetPlatform* CurrentPlatform = GetTargetPlatformManagerRef().GetRunningTargetPlatform();
} check(CurrentPlatform);
for (int32 ModelLODIndex = 0; ModelLODIndex < NumLODs; ++ModelLODIndex) const FStaticMeshLODGroup& LODGroup = CurrentPlatform->GetStaticMeshLODSettings().GetLODGroup(ImportOptions.StaticMeshLODGroup);
{ int32 NumLODs = LODGroup.GetDefaultNumLODs();
StaticMeshComponent->GetStaticMesh()->SourceModels[ModelLODIndex].ReductionSettings = LODGroup.GetDefaultSettings(ModelLODIndex); while (StaticMeshComponent->GetStaticMesh()->SourceModels.Num() < NumLODs)
} {
StaticMeshComponent->GetStaticMesh()->LightMapResolution = LODGroup.GetDefaultLightMapResolution(); new (StaticMeshComponent->GetStaticMesh()->SourceModels) FStaticMeshSourceModel();
} }
for (int32 ModelLODIndex = 0; ModelLODIndex < NumLODs; ++ModelLODIndex)
// @todo This overrides restored values currently but we need to be able to import over the existing settings if the user chose to do so. {
SrcModel.BuildSettings.bRemoveDegenerates = ImportOptions.bRemoveDegenerates; StaticMeshComponent->GetStaticMesh()->SourceModels[ModelLODIndex].ReductionSettings = LODGroup.GetDefaultSettings(ModelLODIndex);
SrcModel.BuildSettings.bBuildAdjacencyBuffer = ImportOptions.bBuildAdjacencyBuffer; }
SrcModel.BuildSettings.bRecomputeNormals = LocalRawMesh.WedgeTangentZ.Num() == 0; StaticMeshComponent->GetStaticMesh()->LightMapResolution = LODGroup.GetDefaultLightMapResolution();
SrcModel.BuildSettings.bRecomputeTangents = LocalRawMesh.WedgeTangentX.Num() == 0 || LocalRawMesh.WedgeTangentY.Num() == 0; }
SrcModel.BuildSettings.bUseMikkTSpace = false;
if( ImportOptions.bGenerateLightmapUVs ) // @todo This overrides restored values currently but we need to be able to import over the existing settings if the user chose to do so.
{ SrcModel.BuildSettings.bRemoveDegenerates = ImportOptions.bRemoveDegenerates;
SrcModel.BuildSettings.bGenerateLightmapUVs = true; SrcModel.BuildSettings.bBuildAdjacencyBuffer = ImportOptions.bBuildAdjacencyBuffer;
SrcModel.BuildSettings.DstLightmapIndex = FirstOpenUVChannel; SrcModel.BuildSettings.bRecomputeNormals = LocalRawMesh.WedgeTangentZ.Num() == 0;
StaticMeshComponent->GetStaticMesh()->LightMapCoordinateIndex = FirstOpenUVChannel; SrcModel.BuildSettings.bRecomputeTangents = LocalRawMesh.WedgeTangentX.Num() == 0 || LocalRawMesh.WedgeTangentY.Num() == 0;
} SrcModel.BuildSettings.bUseMikkTSpace = false;
else if( ImportOptions.bGenerateLightmapUVs )
{ {
SrcModel.BuildSettings.bGenerateLightmapUVs = false; SrcModel.BuildSettings.bGenerateLightmapUVs = true;
} SrcModel.BuildSettings.DstLightmapIndex = FirstOpenUVChannel;
StaticMeshComponent->GetStaticMesh()->LightMapCoordinateIndex = FirstOpenUVChannel;
TArray<FText> BuildErrors; }
StaticMeshComponent->GetStaticMesh()->LODGroup = ImportOptions.StaticMeshLODGroup; else
StaticMeshComponent->GetStaticMesh()->Build(false, &BuildErrors); {
SrcModel.BuildSettings.bGenerateLightmapUVs = false;
for( FText& Error : BuildErrors ) }
{
AddTokenizedErrorMessage(FTokenizedMessage::Create(EMessageSeverity::Warning, Error), FFbxErrors::StaticMesh_BuildError ); TArray<FText> BuildErrors;
} StaticMeshComponent->GetStaticMesh()->LODGroup = ImportOptions.StaticMeshLODGroup;
StaticMeshComponent->GetStaticMesh()->Build(false, &BuildErrors);
// this is damage control. After build, we'd like to absolutely sure that
// all index is pointing correctly and they're all used. Otherwise we remove them for( FText& Error : BuildErrors )
FMeshSectionInfoMap OldSectionInfoMap = StaticMeshComponent->GetStaticMesh()->SectionInfoMap; {
StaticMeshComponent->GetStaticMesh()->SectionInfoMap.Clear(); AddTokenizedErrorMessage(FTokenizedMessage::Create(EMessageSeverity::Warning, Error), FFbxErrors::StaticMesh_BuildError );
// fix up section data }
for (int32 LODResourceIndex = 0; LODResourceIndex < StaticMeshComponent->GetStaticMesh()->RenderData->LODResources.Num(); ++LODResourceIndex)
{ // this is damage control. After build, we'd like to absolutely sure that
FStaticMeshLODResources& LOD = StaticMeshComponent->GetStaticMesh()->RenderData->LODResources[LODResourceIndex]; // all index is pointing correctly and they're all used. Otherwise we remove them
int32 NumSections = LOD.Sections.Num(); FMeshSectionInfoMap OldSectionInfoMap = StaticMeshComponent->GetStaticMesh()->SectionInfoMap;
for(int32 SectionIndex = 0; SectionIndex < NumSections; ++SectionIndex) StaticMeshComponent->GetStaticMesh()->SectionInfoMap.Clear();
{ // fix up section data
FMeshSectionInfo Info = OldSectionInfoMap.Get(LODResourceIndex, SectionIndex); for (int32 LODResourceIndex = 0; LODResourceIndex < StaticMeshComponent->GetStaticMesh()->RenderData->LODResources.Num(); ++LODResourceIndex)
if (StaticMeshComponent->GetMaterials().IsValidIndex(Info.MaterialIndex)) {
{ FStaticMeshLODResources& LOD = StaticMeshComponent->GetStaticMesh()->RenderData->LODResources[LODResourceIndex];
StaticMeshComponent->GetStaticMesh()->SectionInfoMap.Set(LODResourceIndex, SectionIndex, Info); int32 NumSections = LOD.Sections.Num();
} for(int32 SectionIndex = 0; SectionIndex < NumSections; ++SectionIndex)
} {
} FMeshSectionInfo Info = OldSectionInfoMap.Get(LODResourceIndex, SectionIndex);
} if (StaticMeshComponent->GetMaterials().IsValidIndex(Info.MaterialIndex))
else {
{ StaticMeshComponent->GetStaticMesh()->SectionInfoMap.Set(LODResourceIndex, SectionIndex, Info);
StaticMeshComponent = NULL; }
} }
}
return StaticMeshComponent; }
} else
{
// Reverse the winding order for triangle indices StaticMeshComponent = NULL;
template <typename T> }
void GLTFMeshBuilder::ReverseTriDirection(TArray<T>& OutArray)
{ return StaticMeshComponent;
for (int i = 0; i < OutArray.Num() - 2; i += 3) }
{
T Temp = OutArray[i]; // Reverse the winding order for triangle indices
OutArray[i] = OutArray[i + 2]; template <typename T>
OutArray[i + 2] = Temp; void UGLTFMeshBuilder::ReverseTriDirection(TArray<T>& OutArray)
} {
} for (int i = 0; i < OutArray.Num() - 2; i += 3)
{
bool GLTFMeshBuilder::BuildStaticMeshFromGeometry(tinygltf::Mesh* Mesh, UStaticMeshComponent* StaticMeshComponent, int LODIndex, FRawMesh& RawMesh) T Temp = OutArray[i];
{ OutArray[i] = OutArray[i + 2];
check(StaticMeshComponent->GetStaticMesh()->SourceModels.IsValidIndex(LODIndex)); OutArray[i + 2] = Temp;
}
auto ImportOptions = FGLTFLoaderModule::ImportOptions; }
tinygltf::Node* Node = GetMeshParentNode(Mesh); bool UGLTFMeshBuilder::BuildStaticMeshFromGeometry(tinygltf::Mesh* Mesh, UStaticMeshComponent* StaticMeshComponent, int LODIndex, FRawMesh& RawMesh)
FStaticMeshSourceModel& SrcModel = StaticMeshComponent->GetStaticMesh()->SourceModels[LODIndex]; {
check(StaticMeshComponent->GetStaticMesh()->SourceModels.IsValidIndex(LODIndex));
tinygltf::Primitive* BaseLayer = &Mesh->primitives[0];
if (BaseLayer == NULL) auto ImportOptions = FGLTFLoaderModule::ImportOptions;
{
AddTokenizedErrorMessage(FTokenizedMessage::Create(EMessageSeverity::Error, FText::Format(FText::FromString(FString("Error_NoGeometryInMesh", "There is no geometry information in mesh '{0}'")), FText::FromString(ToFString(Mesh->name)))), FFbxErrors::Generic_Mesh_NoGeometry); tinygltf::Node* Node = GetMeshParentNode(Mesh);
return false; FStaticMeshSourceModel& SrcModel = StaticMeshComponent->GetStaticMesh()->SourceModels[LODIndex];
}
tinygltf::Primitive* BaseLayer = &Mesh->primitives[0];
// if (BaseLayer == NULL)
// create materials {
// AddTokenizedErrorMessage(FTokenizedMessage::Create(EMessageSeverity::Error, FText::Format(FText::FromString(FString("Error_NoGeometryInMesh", "There is no geometry information in mesh '{0}'")), FText::FromString(ToFString(Mesh->name)))), FFbxErrors::Generic_Mesh_NoGeometry);
return false;
TArray<tinygltf::Material*> FoundMaterials; }
for (auto Prim : Mesh->primitives)
{ //
tinygltf::Material* CurrentMaterial = &Scene->materials[Prim.material]; // create materials
FoundMaterials.AddUnique(CurrentMaterial); //
}
TArray<tinygltf::Material*> FoundMaterials;
TArray<UMaterialInterface*> Materials; for (auto Prim : Mesh->primitives)
if (ImportOptions.bImportMaterials) {
{ tinygltf::Material* CurrentMaterial = &Scene->materials[Prim.material];
Materials.Add(UMaterial::GetDefaultMaterial(EMaterialDomain::MD_Surface)); FoundMaterials.AddUnique(CurrentMaterial);
} }
else if (ImportOptions.bImportTextures)
{ TArray<UMaterialInterface*> Materials;
ImportTexturesFromNode(Node); if (ImportOptions.bImportMaterials)
} {
Materials.Add(UMaterial::GetDefaultMaterial(EMaterialDomain::MD_Surface));
// Used later to offset the material indices on the raw triangle data }
int32 MaterialIndexOffset = MeshMaterials.Num(); else if (ImportOptions.bImportTextures)
{
for (int32 MaterialIndex = 0; MaterialIndex < FoundMaterials.Num(); MaterialIndex++) ImportTexturesFromNode(Node);
{ }
MaterialPair NewMaterialPair;// = new(MeshMaterials)FFbxMaterial;
tinygltf::Material* GLTFMaterial = FoundMaterials[MaterialIndex]; // Used later to offset the material indices on the raw triangle data
NewMaterialPair.GLTFMaterial = GLTFMaterial; int32 MaterialIndexOffset = MeshMaterials.Num();
if (ImportOptions.bImportMaterials)
{ for (int32 MaterialIndex = 0; MaterialIndex < FoundMaterials.Num(); MaterialIndex++)
NewMaterialPair.Material = Materials[MaterialIndex]; {
} MaterialPair NewMaterialPair;// = new(MeshMaterials)FFbxMaterial;
else tinygltf::Material* GLTFMaterial = FoundMaterials[MaterialIndex];
{ NewMaterialPair.GLTFMaterial = GLTFMaterial;
FString MaterialFullName = ObjectTools::SanitizeObjectName(ToFString(GLTFMaterial->name)); if (ImportOptions.bImportMaterials)
FString BasePackageName = PackageTools::SanitizePackageName(FPackageName::GetLongPackagePath(StaticMeshComponent->GetOutermost()->GetName()) / MaterialFullName); {
UMaterialInterface* UnrealMaterialInterface = FindObject<UMaterialInterface>(NULL, *(BasePackageName + TEXT(".") + MaterialFullName)); NewMaterialPair.Material = Materials[MaterialIndex];
if (UnrealMaterialInterface == NULL) }
{ else
UnrealMaterialInterface = UMaterial::GetDefaultMaterial(MD_Surface); {
} FString MaterialFullName = ObjectTools::SanitizeObjectName(ToFString(GLTFMaterial->name));
NewMaterialPair.Material = UnrealMaterialInterface; FString BasePackageName = PackageTools::SanitizePackageName(FPackageName::GetLongPackagePath(StaticMeshComponent->GetOutermost()->GetName()) / MaterialFullName);
} UMaterialInterface* UnrealMaterialInterface = FindObject<UMaterialInterface>(NULL, *(BasePackageName + TEXT(".") + MaterialFullName));
} if (UnrealMaterialInterface == NULL)
{
if (FoundMaterials.Num() == 0) UnrealMaterialInterface = UMaterial::GetDefaultMaterial(MD_Surface);
{ }
UMaterial* DefaultMaterial = UMaterial::GetDefaultMaterial(MD_Surface); NewMaterialPair.Material = UnrealMaterialInterface;
check(DefaultMaterial); }
MaterialPair NewMaterial; }
NewMaterial.Material = DefaultMaterial;
NewMaterial.GLTFMaterial = NULL; if (FoundMaterials.Num() == 0)
FoundMaterials.AddDefaulted(1); {
} UMaterial* DefaultMaterial = UMaterial::GetDefaultMaterial(MD_Surface);
check(DefaultMaterial);
// Smoothing is already included in the format MaterialPair NewMaterial;
bool bSmoothingAvailable = true; NewMaterial.Material = DefaultMaterial;
NewMaterial.GLTFMaterial = NULL;
// TODO: Collisions FoundMaterials.AddDefaulted(1);
// }
// build collision
// // Smoothing is already included in the format
bool bImportedCollision = false; bool bSmoothingAvailable = true;
bool bEnableCollision = bImportedCollision || (/*GBuildStaticMeshCollision*/true && LODIndex == 0 && ImportOptions.bRemoveDegenerates); // TODO: Collisions
for (int32 SectionIndex = MaterialIndexOffset; SectionIndex<MaterialIndexOffset + FoundMaterials.Num(); SectionIndex++) //
{ // build collision
FMeshSectionInfo Info = StaticMeshComponent->GetStaticMesh()->SectionInfoMap.Get(LODIndex, SectionIndex); //
Info.bEnableCollision = bEnableCollision; bool bImportedCollision = false;
StaticMeshComponent->GetStaticMesh()->SectionInfoMap.Set(LODIndex, SectionIndex, Info);
} bool bEnableCollision = bImportedCollision || (/*GBuildStaticMeshCollision*/true && LODIndex == 0 && ImportOptions.bRemoveDegenerates);
for (int32 SectionIndex = MaterialIndexOffset; SectionIndex<MaterialIndexOffset + FoundMaterials.Num(); SectionIndex++)
// {
// build un-mesh triangles FMeshSectionInfo Info = StaticMeshComponent->GetStaticMesh()->SectionInfoMap.Get(LODIndex, SectionIndex);
// Info.bEnableCollision = bEnableCollision;
StaticMeshComponent->GetStaticMesh()->SectionInfoMap.Set(LODIndex, SectionIndex, Info);
// Construct the matrices for the conversion from right handed to left handed system }
FMatrix TotalMatrix;
FMatrix TotalMatrixForNormal; //
TotalMatrix = GetNodeTransform(GetMeshParentNode(Mesh)); // build un-mesh triangles
FTransform ImportTransform(ImportOptions.ImportRotation.Quaternion(), ImportOptions.ImportTranslation, FVector(ImportOptions.ImportUniformScale)); //
FMatrix ImportMatrix = ImportTransform.ToMatrixWithScale();
TotalMatrix = TotalMatrix * ImportMatrix; // Construct the matrices for the conversion from right handed to left handed system
FMatrix TotalMatrix;
if (ImportOptions.bCorrectUpDirection) FMatrix TotalMatrixForNormal;
{ TotalMatrix = GetNodeTransform(GetMeshParentNode(Mesh));
FTransform Temp(FRotator(0.0f, 0.0f, -90.0f)); FTransform ImportTransform(ImportOptions.ImportRotation.Quaternion(), ImportOptions.ImportTranslation, FVector(ImportOptions.ImportUniformScale));
TotalMatrix = TotalMatrix * Temp.ToMatrixWithScale(); FMatrix ImportMatrix = ImportTransform.ToMatrixWithScale();
} TotalMatrix = TotalMatrix * ImportMatrix;
TotalMatrixForNormal = TotalMatrix.Inverse(); if (ImportOptions.bCorrectUpDirection)
TotalMatrixForNormal = TotalMatrixForNormal.GetTransposed(); {
FTransform Temp(FRotator(0.0f, 0.0f, -90.0f));
// Whether an odd number of axes have negative scale TotalMatrix = TotalMatrix * Temp.ToMatrixWithScale();
bool OddNegativeScale = (TotalMatrix.M[0][0] * TotalMatrix.M[1][1] * TotalMatrix.M[2][2]) < 0; }
// Copy the actual data! TotalMatrixForNormal = TotalMatrix.Inverse();
// Vertex Positions TotalMatrixForNormal = TotalMatrixForNormal.GetTransposed();
TArray<FVector> NewVertexPositions;
if (!ConvertAttrib(NewVertexPositions, Mesh, std::string("POSITION"), false)) // Whether an odd number of axes have negative scale
{ bool OddNegativeScale = (TotalMatrix.M[0][0] * TotalMatrix.M[1][1] * TotalMatrix.M[2][2]) < 0;
AddTokenizedErrorMessage(
FTokenizedMessage::Create( // Copy the actual data!
EMessageSeverity::Error, // Vertex Positions
FText::FromString(FString("Could not obtain position data."))), TArray<FVector> NewVertexPositions;
FFbxErrors::Generic_Mesh_LOD_InvalidIndex); if (!ConvertAttrib(NewVertexPositions, Mesh, std::string("POSITION"), false))
return false; {
} AddTokenizedErrorMessage(
for (auto& Vertex : NewVertexPositions) FTokenizedMessage::Create(
{ EMessageSeverity::Error,
FVector4 Vert4(Vertex); FText::FromString(FString("Could not obtain position data."))),
Vertex = TotalMatrix.TransformFVector4(Vert4); FFbxErrors::Generic_Mesh_LOD_InvalidIndex);
} return false;
int32 VertexCount = NewVertexPositions.Num(); }
for (auto& Vertex : NewVertexPositions)
// Triangle indices {
TArray<int32> NewWedgeIndices; FVector4 Vert4(Vertex);
ConvertAttrib(NewWedgeIndices, Mesh, std::string("__WedgeIndices")); Vertex = TotalMatrix.TransformFVector4(Vert4);
int32 WedgeCount = NewWedgeIndices.Num(); }
int32 TriangleCount = WedgeCount / 3; int32 VertexCount = NewVertexPositions.Num();
if (TriangleCount == 0)
{ // Triangle indices
AddTokenizedErrorMessage(FTokenizedMessage::Create(EMessageSeverity::Error, FText::Format(FText::FromString(FString("Error_NoTrianglesFoundInMesh", "No triangles were found on mesh '{0}'")), FText::FromString(ToFString(Mesh->name)))), FFbxErrors::StaticMesh_NoTriangles); TArray<int32> NewWedgeIndices;
return false; ConvertAttrib(NewWedgeIndices, Mesh, std::string("__WedgeIndices"));
} int32 WedgeCount = NewWedgeIndices.Num();
int32 TriangleCount = WedgeCount / 3;
// Normals if (TriangleCount == 0)
TArray<FVector> NewWedgeTangentX, NewWedgeTangentY, NewWedgeTangentZ; {
bool HasNormals = ConvertAttrib(NewWedgeTangentZ, Mesh, "NORMAL"); AddTokenizedErrorMessage(FTokenizedMessage::Create(EMessageSeverity::Error, FText::Format(FText::FromString(FString("Error_NoTrianglesFoundInMesh", "No triangles were found on mesh '{0}'")), FText::FromString(ToFString(Mesh->name)))), FFbxErrors::StaticMesh_NoTriangles);
ConvertAttrib(NewWedgeTangentY, Mesh, "TANGENT"); return false;
ConvertAttrib(NewWedgeTangentX, Mesh, "BINORMAL"); }
if (!HasNormals)
{ // Normals
AddTokenizedErrorMessage( TArray<FVector> NewWedgeTangentX, NewWedgeTangentY, NewWedgeTangentZ;
FTokenizedMessage::Create( bool HasNormals = ConvertAttrib(NewWedgeTangentZ, Mesh, "NORMAL");
EMessageSeverity::Warning, ConvertAttrib(NewWedgeTangentY, Mesh, "TANGENT");
FText::FromString(FString("Could not obtain data for normals; they will be recalculated but the model will lack smoothing data."))), ConvertAttrib(NewWedgeTangentX, Mesh, "BINORMAL");
FFbxErrors::Generic_Mesh_LOD_InvalidIndex); if (!HasNormals)
} {
for (auto& Normal : NewWedgeTangentZ) AddTokenizedErrorMessage(
{ FTokenizedMessage::Create(
Normal = TotalMatrixForNormal.TransformVector(Normal); EMessageSeverity::Warning,
} FText::FromString(FString("Could not obtain data for normals; they will be recalculated but the model will lack smoothing data."))),
FFbxErrors::Generic_Mesh_LOD_InvalidIndex);
// UVs }
TArray<FVector2D> NewWedgeTexCoords[MAX_MESH_TEXTURE_COORDS]; for (auto& Normal : NewWedgeTangentZ)
bool bHasUVs = false; {
for (int i = 0; i < MAX_MESH_TEXTURE_COORDS; ++i) Normal = TotalMatrixForNormal.TransformVector(Normal);
{ }
bHasUVs |= ConvertAttrib(NewWedgeTexCoords[i], Mesh, std::string("TEXCOORD_") + std::to_string(i));
} // UVs
if (!bHasUVs) TArray<FVector2D> NewWedgeTexCoords[MAX_MESH_TEXTURE_COORDS];
{ bool bHasUVs = false;
AddTokenizedErrorMessage( for (int i = 0; i < MAX_MESH_TEXTURE_COORDS; ++i)
FTokenizedMessage::Create( {
EMessageSeverity::Warning, bHasUVs |= ConvertAttrib(NewWedgeTexCoords[i], Mesh, std::string("TEXCOORD_") + std::to_string(i));
FText::FromString(FString("Could not obtain UV data."))), }
FFbxErrors::Generic_Mesh_LOD_InvalidIndex); if (!bHasUVs)
} {
AddTokenizedErrorMessage(
TArray<FColor> NewWedgeColors; FTokenizedMessage::Create(
ConvertAttrib(NewWedgeColors, Mesh, std::string("COLOR")); // No need to check for errors EMessageSeverity::Warning,
FText::FromString(FString("Could not obtain UV data."))),
// Reverse the triangle winding order since glTF uses the opposite to Unreal FFbxErrors::Generic_Mesh_LOD_InvalidIndex);
// Except if the model has negative scale on an odd number of axes, which will effectively do it for us }
if (!OddNegativeScale)
{ TArray<FColor> NewWedgeColors;
ReverseTriDirection(NewWedgeIndices); ConvertAttrib(NewWedgeColors, Mesh, std::string("COLOR")); // No need to check for errors
ReverseTriDirection(NewWedgeColors);
ReverseTriDirection(NewWedgeTangentX); // Reverse the triangle winding order since glTF uses the opposite to Unreal
ReverseTriDirection(NewWedgeTangentY); // Except if the model has negative scale on an odd number of axes, which will effectively do it for us
ReverseTriDirection(NewWedgeTangentZ); if (!OddNegativeScale)
for (int i = 0; i < MAX_MESH_TEXTURE_COORDS; ++i) {
{ ReverseTriDirection(NewWedgeIndices);
ReverseTriDirection(NewWedgeTexCoords[i]); ReverseTriDirection(NewWedgeColors);
} ReverseTriDirection(NewWedgeTangentX);
} ReverseTriDirection(NewWedgeTangentY);
ReverseTriDirection(NewWedgeTangentZ);
TArray<int32> NewFaceMaterialIndices; for (int i = 0; i < MAX_MESH_TEXTURE_COORDS; ++i)
GetMaterialIndices(NewFaceMaterialIndices, (*Mesh)); {
ReverseTriDirection(NewWedgeTexCoords[i]);
TArray<int32> NewFaceSmoothingMasks; // Don't need to do anything with this since smoothing information is included implicitly in glTF }
}
// Force attribute arrays to the correct size, otherwise it complains
NewFaceMaterialIndices.SetNumZeroed(TriangleCount); TArray<int32> NewFaceMaterialIndices;
NewFaceSmoothingMasks.SetNumZeroed(TriangleCount); GetMaterialIndices(NewFaceMaterialIndices, (*Mesh));
NewWedgeColors.SetNumZeroed(WedgeCount);
NewWedgeTangentX.SetNumZeroed(WedgeCount); TArray<int32> NewFaceSmoothingMasks; // Don't need to do anything with this since smoothing information is included implicitly in glTF
NewWedgeTangentY.SetNumZeroed(WedgeCount);
NewWedgeTangentZ.SetNumZeroed(WedgeCount); // Force attribute arrays to the correct size, otherwise it complains
for (int32 i = 0; i < MAX_MESH_TEXTURE_COORDS; ++i) NewFaceMaterialIndices.SetNumZeroed(TriangleCount);
{ NewFaceSmoothingMasks.SetNumZeroed(TriangleCount);
NewWedgeTexCoords[i].SetNumZeroed(WedgeCount); NewWedgeColors.SetNumZeroed(WedgeCount);
} NewWedgeTangentX.SetNumZeroed(WedgeCount);
NewWedgeTangentY.SetNumZeroed(WedgeCount);
// Add the new data to the raw mesh NewWedgeTangentZ.SetNumZeroed(WedgeCount);
RawMesh.VertexPositions.Append(NewVertexPositions); for (int32 i = 0; i < MAX_MESH_TEXTURE_COORDS; ++i)
RawMesh.WedgeIndices.Append(NewWedgeIndices); {
RawMesh.FaceMaterialIndices.Append(NewFaceMaterialIndices); NewWedgeTexCoords[i].SetNumZeroed(WedgeCount);
RawMesh.FaceSmoothingMasks.Append(NewFaceSmoothingMasks); }
RawMesh.WedgeColors.Append(NewWedgeColors);
RawMesh.WedgeTangentX.Append(NewWedgeTangentX); // Add the new data to the raw mesh
RawMesh.WedgeTangentY.Append(NewWedgeTangentY); RawMesh.VertexPositions.Append(NewVertexPositions);
RawMesh.WedgeTangentZ.Append(NewWedgeTangentZ); RawMesh.WedgeIndices.Append(NewWedgeIndices);
for (int32 i = 0; i < MAX_MESH_TEXTURE_COORDS; ++i) RawMesh.FaceMaterialIndices.Append(NewFaceMaterialIndices);
{ RawMesh.FaceSmoothingMasks.Append(NewFaceSmoothingMasks);
RawMesh.WedgeTexCoords[i].Append(NewWedgeTexCoords[i]); RawMesh.WedgeColors.Append(NewWedgeColors);
} RawMesh.WedgeTangentX.Append(NewWedgeTangentX);
RawMesh.WedgeTangentY.Append(NewWedgeTangentY);
return true; RawMesh.WedgeTangentZ.Append(NewWedgeTangentZ);
} for (int32 i = 0; i < MAX_MESH_TEXTURE_COORDS; ++i)
{
UMaterialInterface* GLTFMeshBuilder::ToUMaterial(tinygltf::Material* Material) RawMesh.WedgeTexCoords[i].Append(NewWedgeTexCoords[i]);
{ }
return UMaterial::GetDefaultMaterial(MD_Surface);
} return true;
}
FString GLTFMeshBuilder::GetError()
{ UMaterialInterface* UGLTFMeshBuilder::ToUMaterial(tinygltf::Material* Material)
return Error; {
} return UMaterial::GetDefaultMaterial(MD_Surface);
}
FString GLTFMeshBuilder::ToFString(std::string InString)
{ FString UGLTFMeshBuilder::GetError()
return FString(InString.c_str()); {
} return Error;
}
std::string GLTFMeshBuilder::ToStdString(FString InString)
{ FString UGLTFMeshBuilder::ToFString(std::string InString)
auto CharArray = InString.GetCharArray(); {
return FString(InString.c_str());
std::wstring WideString(&CharArray[0]); }
std::wstring_convert< std::codecvt_utf8<wchar_t> > Convert;
return Convert.to_bytes(WideString); std::string UGLTFMeshBuilder::ToStdString(FString InString)
} {
auto CharArray = InString.GetCharArray();
template <typename T>
bool GLTFMeshBuilder::GetBufferData(TArray<T> &OutArray, tinygltf::Accessor* Accessor, bool Append) std::wstring WideString(&CharArray[0]);
{ std::wstring_convert< std::codecvt_utf8<wchar_t> > Convert;
if (Accessor->type != TINYGLTF_TYPE_SCALAR) return Convert.to_bytes(WideString);
{ }
return false;
} template <typename T>
bool UGLTFMeshBuilder::GetBufferData(TArray<T> &OutArray, tinygltf::Accessor* Accessor, bool Append)
if (!Accessor) {
{ if (Accessor->type != TINYGLTF_TYPE_SCALAR)
return false; {
} return false;
}
tinygltf::BufferView* BufferView = &Scene->bufferViews[Accessor->bufferView];
if (!Accessor)
if (!BufferView) {
{ return false;
return false; }
}
tinygltf::BufferView* BufferView = &Scene->bufferViews[Accessor->bufferView];
tinygltf::Buffer* Buffer = &Scene->buffers[BufferView->buffer];
if (!BufferView)
if (!Buffer) {
{ return false;
return false; }
}
tinygltf::Buffer* Buffer = &Scene->buffers[BufferView->buffer];
if (!Append)
{ if (!Buffer)
OutArray.Empty(); {
} return false;
}
size_t Stride;
if (Accessor->byteStride != 0) if (!Append)
{ {
Stride = Accessor->byteStride; OutArray.Empty();
} }
else
{ size_t Stride;
Stride = TypeSize(Accessor->componentType); if (Accessor->byteStride != 0)
} {
Stride = Accessor->byteStride;
unsigned char* Start = &Buffer->data[Accessor->byteOffset + BufferView->byteOffset]; }
else
switch (Accessor->componentType) {
{ Stride = TypeSize(Accessor->componentType);
case TINYGLTF_COMPONENT_TYPE_BYTE: BufferCopy<GLTFType<TINYGLTF_COMPONENT_TYPE_BYTE> ::Type, T>(OutArray, Start, Accessor->count, Stride); break; }
case TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE: BufferCopy<GLTFType<TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE> ::Type, T>(OutArray, Start, Accessor->count, Stride); break;
case TINYGLTF_COMPONENT_TYPE_SHORT: BufferCopy<GLTFType<TINYGLTF_COMPONENT_TYPE_SHORT> ::Type, T>(OutArray, Start, Accessor->count, Stride); break; unsigned char* Start = &Buffer->data[Accessor->byteOffset + BufferView->byteOffset];
case TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT: BufferCopy<GLTFType<TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT> ::Type, T>(OutArray, Start, Accessor->count, Stride); break;
case TINYGLTF_COMPONENT_TYPE_INT: BufferCopy<GLTFType<TINYGLTF_COMPONENT_TYPE_INT> ::Type, T>(OutArray, Start, Accessor->count, Stride); break; switch (Accessor->componentType)
case TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT: BufferCopy<GLTFType<TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT> ::Type, T>(OutArray, Start, Accessor->count, Stride); break; {
case TINYGLTF_COMPONENT_TYPE_FLOAT: BufferCopy<GLTFType<TINYGLTF_COMPONENT_TYPE_FLOAT> ::Type, T>(OutArray, Start, Accessor->count, Stride); break; case TINYGLTF_COMPONENT_TYPE_BYTE: BufferCopy<GLTFType<TINYGLTF_COMPONENT_TYPE_BYTE> ::Type, T>(OutArray, Start, Accessor->count, Stride); break;
case TINYGLTF_COMPONENT_TYPE_DOUBLE: BufferCopy<GLTFType<TINYGLTF_COMPONENT_TYPE_DOUBLE> ::Type, T>(OutArray, Start, Accessor->count, Stride); break; case TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE: BufferCopy<GLTFType<TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE> ::Type, T>(OutArray, Start, Accessor->count, Stride); break;
default: BufferCopy<T, T>(OutArray, Start, Accessor->count, Stride); break; case TINYGLTF_COMPONENT_TYPE_SHORT: BufferCopy<GLTFType<TINYGLTF_COMPONENT_TYPE_SHORT> ::Type, T>(OutArray, Start, Accessor->count, Stride); break;
} case TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT: BufferCopy<GLTFType<TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT> ::Type, T>(OutArray, Start, Accessor->count, Stride); break;
case TINYGLTF_COMPONENT_TYPE_INT: BufferCopy<GLTFType<TINYGLTF_COMPONENT_TYPE_INT> ::Type, T>(OutArray, Start, Accessor->count, Stride); break;
return true; case TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT: BufferCopy<GLTFType<TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT> ::Type, T>(OutArray, Start, Accessor->count, Stride); break;
} case TINYGLTF_COMPONENT_TYPE_FLOAT: BufferCopy<GLTFType<TINYGLTF_COMPONENT_TYPE_FLOAT> ::Type, T>(OutArray, Start, Accessor->count, Stride); break;
case TINYGLTF_COMPONENT_TYPE_DOUBLE: BufferCopy<GLTFType<TINYGLTF_COMPONENT_TYPE_DOUBLE> ::Type, T>(OutArray, Start, Accessor->count, Stride); break;
// This specialization is almost the same, but checks for the VEC2 GLTF type and uses the overloaded BufferCopy function for FVector2D default: BufferCopy<T, T>(OutArray, Start, Accessor->count, Stride); break;
template <> }
bool GLTFMeshBuilder::GetBufferData(TArray<FVector2D> &OutArray, tinygltf::Accessor* Accessor, bool Append)
{ return true;
if (Accessor->type != TINYGLTF_TYPE_VEC2) }
{
return false; // This specialization is almost the same, but checks for the VEC2 GLTF type and uses the overloaded BufferCopy function for FVector2D
} template <>
bool UGLTFMeshBuilder::GetBufferData(TArray<FVector2D> &OutArray, tinygltf::Accessor* Accessor, bool Append)
if (!Accessor) {
{ if (Accessor->type != TINYGLTF_TYPE_VEC2)
return false; {
} return false;
}
tinygltf::BufferView* BufferView = &Scene->bufferViews[Accessor->bufferView];
if (!Accessor)
if (!BufferView) {
{ return false;
return false; }
}
tinygltf::BufferView* BufferView = &Scene->bufferViews[Accessor->bufferView];
tinygltf::Buffer* Buffer = &Scene->buffers[BufferView->buffer];
if (!BufferView)
if (!Buffer) {
{ return false;
return false; }
}
tinygltf::Buffer* Buffer = &Scene->buffers[BufferView->buffer];
if (!Append)
{ if (!Buffer)
OutArray.Empty(); {
} return false;
}
size_t Stride;
if (Accessor->byteStride != 0) if (!Append)
{ {
Stride = Accessor->byteStride; OutArray.Empty();
} }
else
{ size_t Stride;
Stride = TypeSize(Accessor->componentType); if (Accessor->byteStride != 0)
} {
Stride = Accessor->byteStride;
unsigned char* Start = &Buffer->data[Accessor->byteOffset + BufferView->byteOffset]; }
else
switch (Accessor->componentType) {
{ Stride = TypeSize(Accessor->componentType);
case TINYGLTF_COMPONENT_TYPE_BYTE: BufferCopy<GLTFType<TINYGLTF_COMPONENT_TYPE_BYTE> ::Type>(OutArray, Start, Accessor->count, Stride); break; }
case TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE: BufferCopy<GLTFType<TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE> ::Type>(OutArray, Start, Accessor->count, Stride); break;
case TINYGLTF_COMPONENT_TYPE_SHORT: BufferCopy<GLTFType<TINYGLTF_COMPONENT_TYPE_SHORT> ::Type>(OutArray, Start, Accessor->count, Stride); break; unsigned char* Start = &Buffer->data[Accessor->byteOffset + BufferView->byteOffset];
case TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT: BufferCopy<GLTFType<TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT> ::Type>(OutArray, Start, Accessor->count, Stride); break;
case TINYGLTF_COMPONENT_TYPE_INT: BufferCopy<GLTFType<TINYGLTF_COMPONENT_TYPE_INT> ::Type>(OutArray, Start, Accessor->count, Stride); break; switch (Accessor->componentType)
case TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT: BufferCopy<GLTFType<TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT> ::Type>(OutArray, Start, Accessor->count, Stride); break; {
case TINYGLTF_COMPONENT_TYPE_FLOAT: BufferCopy<GLTFType<TINYGLTF_COMPONENT_TYPE_FLOAT> ::Type>(OutArray, Start, Accessor->count, Stride); break; case TINYGLTF_COMPONENT_TYPE_BYTE: BufferCopy<GLTFType<TINYGLTF_COMPONENT_TYPE_BYTE> ::Type>(OutArray, Start, Accessor->count, Stride); break;
case TINYGLTF_COMPONENT_TYPE_DOUBLE: BufferCopy<GLTFType<TINYGLTF_COMPONENT_TYPE_DOUBLE> ::Type>(OutArray, Start, Accessor->count, Stride); break; case TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE: BufferCopy<GLTFType<TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE> ::Type>(OutArray, Start, Accessor->count, Stride); break;
default: return false; case TINYGLTF_COMPONENT_TYPE_SHORT: BufferCopy<GLTFType<TINYGLTF_COMPONENT_TYPE_SHORT> ::Type>(OutArray, Start, Accessor->count, Stride); break;
} case TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT: BufferCopy<GLTFType<TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT> ::Type>(OutArray, Start, Accessor->count, Stride); break;
case TINYGLTF_COMPONENT_TYPE_INT: BufferCopy<GLTFType<TINYGLTF_COMPONENT_TYPE_INT> ::Type>(OutArray, Start, Accessor->count, Stride); break;
return true; case TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT: BufferCopy<GLTFType<TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT> ::Type>(OutArray, Start, Accessor->count, Stride); break;
} case TINYGLTF_COMPONENT_TYPE_FLOAT: BufferCopy<GLTFType<TINYGLTF_COMPONENT_TYPE_FLOAT> ::Type>(OutArray, Start, Accessor->count, Stride); break;
case TINYGLTF_COMPONENT_TYPE_DOUBLE: BufferCopy<GLTFType<TINYGLTF_COMPONENT_TYPE_DOUBLE> ::Type>(OutArray, Start, Accessor->count, Stride); break;
// This specialization is almost the same, but checks for the VEC3 GLTF type and uses the overloaded BufferCopy function for FVector default: return false;
template <> }
bool GLTFMeshBuilder::GetBufferData(TArray<FVector> &OutArray, tinygltf::Accessor* Accessor, bool Append)
{ return true;
if (Accessor->type != TINYGLTF_TYPE_VEC3) }
{
return false; // This specialization is almost the same, but checks for the VEC3 GLTF type and uses the overloaded BufferCopy function for FVector
} template <>
bool UGLTFMeshBuilder::GetBufferData(TArray<FVector> &OutArray, tinygltf::Accessor* Accessor, bool Append)
if (!Accessor) {
{ if (Accessor->type != TINYGLTF_TYPE_VEC3)
return false; {
} return false;
}
tinygltf::BufferView* BufferView = &Scene->bufferViews[Accessor->bufferView];
if (!Accessor)
if (!BufferView) {
{ return false;
return false; }
}
tinygltf::BufferView* BufferView = &Scene->bufferViews[Accessor->bufferView];
tinygltf::Buffer* Buffer = &Scene->buffers[BufferView->buffer];
if (!BufferView)
if (!Buffer) {
{ return false;
return false; }
}
tinygltf::Buffer* Buffer = &Scene->buffers[BufferView->buffer];
if (!Append)
{ if (!Buffer)
OutArray.Empty(); {
} return false;
}
size_t Stride;
if (Accessor->byteStride != 0) if (!Append)
{ {
Stride = Accessor->byteStride; OutArray.Empty();
} }
else
{ size_t Stride;
Stride = TypeSize(Accessor->componentType); if (Accessor->byteStride != 0)
} {
Stride = Accessor->byteStride;
unsigned char* Start = &Buffer->data[Accessor->byteOffset + BufferView->byteOffset]; }
else
switch (Accessor->componentType) {
{ Stride = TypeSize(Accessor->componentType);
case TINYGLTF_COMPONENT_TYPE_BYTE: BufferCopy<GLTFType<TINYGLTF_COMPONENT_TYPE_BYTE> ::Type>(OutArray, Start, Accessor->count, Stride); break; }
case TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE: BufferCopy<GLTFType<TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE> ::Type>(OutArray, Start, Accessor->count, Stride); break;
case TINYGLTF_COMPONENT_TYPE_SHORT: BufferCopy<GLTFType<TINYGLTF_COMPONENT_TYPE_SHORT> ::Type>(OutArray, Start, Accessor->count, Stride); break; unsigned char* Start = &Buffer->data[Accessor->byteOffset + BufferView->byteOffset];
case TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT: BufferCopy<GLTFType<TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT> ::Type>(OutArray, Start, Accessor->count, Stride); break;
case TINYGLTF_COMPONENT_TYPE_INT: BufferCopy<GLTFType<TINYGLTF_COMPONENT_TYPE_INT> ::Type>(OutArray, Start, Accessor->count, Stride); break; switch (Accessor->componentType)
case TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT: BufferCopy<GLTFType<TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT> ::Type>(OutArray, Start, Accessor->count, Stride); break; {
case TINYGLTF_COMPONENT_TYPE_FLOAT: BufferCopy<GLTFType<TINYGLTF_COMPONENT_TYPE_FLOAT> ::Type>(OutArray, Start, Accessor->count, Stride); break; case TINYGLTF_COMPONENT_TYPE_BYTE: BufferCopy<GLTFType<TINYGLTF_COMPONENT_TYPE_BYTE> ::Type>(OutArray, Start, Accessor->count, Stride); break;
case TINYGLTF_COMPONENT_TYPE_DOUBLE: BufferCopy<GLTFType<TINYGLTF_COMPONENT_TYPE_DOUBLE> ::Type>(OutArray, Start, Accessor->count, Stride); break; case TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE: BufferCopy<GLTFType<TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE> ::Type>(OutArray, Start, Accessor->count, Stride); break;
default: return false; case TINYGLTF_COMPONENT_TYPE_SHORT: BufferCopy<GLTFType<TINYGLTF_COMPONENT_TYPE_SHORT> ::Type>(OutArray, Start, Accessor->count, Stride); break;
} case TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT: BufferCopy<GLTFType<TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT> ::Type>(OutArray, Start, Accessor->count, Stride); break;
case TINYGLTF_COMPONENT_TYPE_INT: BufferCopy<GLTFType<TINYGLTF_COMPONENT_TYPE_INT> ::Type>(OutArray, Start, Accessor->count, Stride); break;
return true; case TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT: BufferCopy<GLTFType<TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT> ::Type>(OutArray, Start, Accessor->count, Stride); break;
} case TINYGLTF_COMPONENT_TYPE_FLOAT: BufferCopy<GLTFType<TINYGLTF_COMPONENT_TYPE_FLOAT> ::Type>(OutArray, Start, Accessor->count, Stride); break;
case TINYGLTF_COMPONENT_TYPE_DOUBLE: BufferCopy<GLTFType<TINYGLTF_COMPONENT_TYPE_DOUBLE> ::Type>(OutArray, Start, Accessor->count, Stride); break;
// This specialization is almost the same, but checks for the VEC4 GLTF type and uses the overloaded BufferCopy function for FVector4 default: return false;
template <> }
bool GLTFMeshBuilder::GetBufferData(TArray<FVector4> &OutArray, tinygltf::Accessor* Accessor, bool Append)
{ return true;
if (Accessor->type != TINYGLTF_TYPE_VEC4) }
{
return false; // This specialization is almost the same, but checks for the VEC4 GLTF type and uses the overloaded BufferCopy function for FVector4
} template <>
bool UGLTFMeshBuilder::GetBufferData(TArray<FVector4> &OutArray, tinygltf::Accessor* Accessor, bool Append)
if (!Accessor) {
{ if (Accessor->type != TINYGLTF_TYPE_VEC4)
return false; {
} return false;
}
tinygltf::BufferView* BufferView = &Scene->bufferViews[Accessor->bufferView];
if (!Accessor)
if (!BufferView) {
{ return false;
return false; }
}
tinygltf::BufferView* BufferView = &Scene->bufferViews[Accessor->bufferView];
tinygltf::Buffer* Buffer = &Scene->buffers[BufferView->buffer];
if (!BufferView)
if (!Buffer) {
{ return false;
return false; }
}
tinygltf::Buffer* Buffer = &Scene->buffers[BufferView->buffer];
if (!Append)
{ if (!Buffer)
OutArray.Empty(); {
} return false;
}
size_t Stride;
if (Accessor->byteStride != 0) if (!Append)
{ {
Stride = Accessor->byteStride; OutArray.Empty();
} }
else
{ size_t Stride;
Stride = TypeSize(Accessor->componentType); if (Accessor->byteStride != 0)
} {
Stride = Accessor->byteStride;
unsigned char* Start = &Buffer->data[Accessor->byteOffset + BufferView->byteOffset]; }
else
switch (Accessor->componentType) {
{ Stride = TypeSize(Accessor->componentType);
case TINYGLTF_COMPONENT_TYPE_BYTE: BufferCopy<GLTFType<TINYGLTF_COMPONENT_TYPE_BYTE> ::Type>(OutArray, Start, Accessor->count, Stride); break; }
case TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE: BufferCopy<GLTFType<TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE> ::Type>(OutArray, Start, Accessor->count, Stride); break;
case TINYGLTF_COMPONENT_TYPE_SHORT: BufferCopy<GLTFType<TINYGLTF_COMPONENT_TYPE_SHORT> ::Type>(OutArray, Start, Accessor->count, Stride); break; unsigned char* Start = &Buffer->data[Accessor->byteOffset + BufferView->byteOffset];
case TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT: BufferCopy<GLTFType<TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT> ::Type>(OutArray, Start, Accessor->count, Stride); break;
case TINYGLTF_COMPONENT_TYPE_INT: BufferCopy<GLTFType<TINYGLTF_COMPONENT_TYPE_INT> ::Type>(OutArray, Start, Accessor->count, Stride); break; switch (Accessor->componentType)
case TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT: BufferCopy<GLTFType<TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT> ::Type>(OutArray, Start, Accessor->count, Stride); break; {
case TINYGLTF_COMPONENT_TYPE_FLOAT: BufferCopy<GLTFType<TINYGLTF_COMPONENT_TYPE_FLOAT> ::Type>(OutArray, Start, Accessor->count, Stride); break; case TINYGLTF_COMPONENT_TYPE_BYTE: BufferCopy<GLTFType<TINYGLTF_COMPONENT_TYPE_BYTE> ::Type>(OutArray, Start, Accessor->count, Stride); break;
case TINYGLTF_COMPONENT_TYPE_DOUBLE: BufferCopy<GLTFType<TINYGLTF_COMPONENT_TYPE_DOUBLE> ::Type>(OutArray, Start, Accessor->count, Stride); break; case TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE: BufferCopy<GLTFType<TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE> ::Type>(OutArray, Start, Accessor->count, Stride); break;
default: return false; case TINYGLTF_COMPONENT_TYPE_SHORT: BufferCopy<GLTFType<TINYGLTF_COMPONENT_TYPE_SHORT> ::Type>(OutArray, Start, Accessor->count, Stride); break;
} case TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT: BufferCopy<GLTFType<TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT> ::Type>(OutArray, Start, Accessor->count, Stride); break;
case TINYGLTF_COMPONENT_TYPE_INT: BufferCopy<GLTFType<TINYGLTF_COMPONENT_TYPE_INT> ::Type>(OutArray, Start, Accessor->count, Stride); break;
return true; case TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT: BufferCopy<GLTFType<TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT> ::Type>(OutArray, Start, Accessor->count, Stride); break;
} case TINYGLTF_COMPONENT_TYPE_FLOAT: BufferCopy<GLTFType<TINYGLTF_COMPONENT_TYPE_FLOAT> ::Type>(OutArray, Start, Accessor->count, Stride); break;
case TINYGLTF_COMPONENT_TYPE_DOUBLE: BufferCopy<GLTFType<TINYGLTF_COMPONENT_TYPE_DOUBLE> ::Type>(OutArray, Start, Accessor->count, Stride); break;
// This specialization is almost the same, but checks for a VEC3 or VEC4 GLTF type and uses the overloaded BufferCopy function for FColor default: return false;
template <> }
bool GLTFMeshBuilder::GetBufferData(TArray<FColor> &OutArray, tinygltf::Accessor* Accessor, bool Append)
{ return true;
if (Accessor->type != TINYGLTF_TYPE_VEC3 && Accessor->type != TINYGLTF_TYPE_VEC4) }
{
return false; // This specialization is almost the same, but checks for a VEC3 or VEC4 GLTF type and uses the overloaded BufferCopy function for FColor
} template <>
bool UGLTFMeshBuilder::GetBufferData(TArray<FColor> &OutArray, tinygltf::Accessor* Accessor, bool Append)
if (!Accessor) {
{ if (Accessor->type != TINYGLTF_TYPE_VEC3 && Accessor->type != TINYGLTF_TYPE_VEC4)
return false; {
} return false;
}
tinygltf::BufferView* BufferView = &Scene->bufferViews[Accessor->bufferView];
if (!Accessor)
if (!BufferView) {
{ return false;
return false; }
}
tinygltf::BufferView* BufferView = &Scene->bufferViews[Accessor->bufferView];
tinygltf::Buffer* Buffer = &Scene->buffers[BufferView->buffer];
if (!BufferView)
if (!Buffer) {
{ return false;
return false; }
}
tinygltf::Buffer* Buffer = &Scene->buffers[BufferView->buffer];
if (!Append)
{ if (!Buffer)
OutArray.Empty(); {
} return false;
}
size_t Stride;
if (Accessor->byteStride != 0) if (!Append)
{ {
Stride = Accessor->byteStride; OutArray.Empty();
} }
else
{ size_t Stride;
Stride = TypeSize(Accessor->componentType); if (Accessor->byteStride != 0)
} {
Stride = Accessor->byteStride;
unsigned char* Start = &Buffer->data[Accessor->byteOffset + BufferView->byteOffset]; }
else
switch (Accessor->componentType) {
{ Stride = TypeSize(Accessor->componentType);
case TINYGLTF_COMPONENT_TYPE_BYTE: BufferCopy<GLTFType<TINYGLTF_COMPONENT_TYPE_BYTE> ::Type>(OutArray, Accessor->type, Start, Accessor->count, Stride); break; }
case TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE: BufferCopy<GLTFType<TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE> ::Type>(OutArray, Accessor->type, Start, Accessor->count, Stride); break;
case TINYGLTF_COMPONENT_TYPE_SHORT: BufferCopy<GLTFType<TINYGLTF_COMPONENT_TYPE_SHORT> ::Type>(OutArray, Accessor->type, Start, Accessor->count, Stride); break; unsigned char* Start = &Buffer->data[Accessor->byteOffset + BufferView->byteOffset];
case TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT: BufferCopy<GLTFType<TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT> ::Type>(OutArray, Accessor->type, Start, Accessor->count, Stride); break;
case TINYGLTF_COMPONENT_TYPE_INT: BufferCopy<GLTFType<TINYGLTF_COMPONENT_TYPE_INT> ::Type>(OutArray, Accessor->type, Start, Accessor->count, Stride); break; switch (Accessor->componentType)
case TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT: BufferCopy<GLTFType<TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT> ::Type>(OutArray, Accessor->type, Start, Accessor->count, Stride); break; {
case TINYGLTF_COMPONENT_TYPE_FLOAT: BufferCopy<GLTFType<TINYGLTF_COMPONENT_TYPE_FLOAT> ::Type>(OutArray, Accessor->type, Start, Accessor->count, Stride); break; case TINYGLTF_COMPONENT_TYPE_BYTE: BufferCopy<GLTFType<TINYGLTF_COMPONENT_TYPE_BYTE> ::Type>(OutArray, Accessor->type, Start, Accessor->count, Stride); break;
case TINYGLTF_COMPONENT_TYPE_DOUBLE: BufferCopy<GLTFType<TINYGLTF_COMPONENT_TYPE_DOUBLE> ::Type>(OutArray, Accessor->type, Start, Accessor->count, Stride); break; case TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE: BufferCopy<GLTFType<TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE> ::Type>(OutArray, Accessor->type, Start, Accessor->count, Stride); break;
default: return false; case TINYGLTF_COMPONENT_TYPE_SHORT: BufferCopy<GLTFType<TINYGLTF_COMPONENT_TYPE_SHORT> ::Type>(OutArray, Accessor->type, Start, Accessor->count, Stride); break;
} case TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT: BufferCopy<GLTFType<TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT> ::Type>(OutArray, Accessor->type, Start, Accessor->count, Stride); break;
case TINYGLTF_COMPONENT_TYPE_INT: BufferCopy<GLTFType<TINYGLTF_COMPONENT_TYPE_INT> ::Type>(OutArray, Accessor->type, Start, Accessor->count, Stride); break;
return true; case TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT: BufferCopy<GLTFType<TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT> ::Type>(OutArray, Accessor->type, Start, Accessor->count, Stride); break;
} case TINYGLTF_COMPONENT_TYPE_FLOAT: BufferCopy<GLTFType<TINYGLTF_COMPONENT_TYPE_FLOAT> ::Type>(OutArray, Accessor->type, Start, Accessor->count, Stride); break;
case TINYGLTF_COMPONENT_TYPE_DOUBLE: BufferCopy<GLTFType<TINYGLTF_COMPONENT_TYPE_DOUBLE> ::Type>(OutArray, Accessor->type, Start, Accessor->count, Stride); break;
bool GLTFMeshBuilder::HasAttribute(tinygltf::Mesh* Mesh, std::string AttribName) const default: return false;
{ }
for (auto Prim : Mesh->primitives)
{ return true;
for (auto Entry : Prim.attributes) }
{
if (Entry.first == AttribName) bool UGLTFMeshBuilder::HasAttribute(tinygltf::Mesh* Mesh, std::string AttribName) const
{ {
return true; for (auto Prim : Mesh->primitives)
} {
} for (auto Entry : Prim.attributes)
} {
return false; if (Entry.first == AttribName)
} {
return true;
template <typename T> int32 GLTFMeshBuilder::FindInStdVector(const std::vector<T> &InVector, const T &InElement) const }
{ }
for (int32 i = 0; i < InVector.size(); ++i) }
{ return false;
if (InVector[i] == InElement) }
{
return i; template <typename T> int32 UGLTFMeshBuilder::FindInStdVector(const std::vector<T> &InVector, const T &InElement) const
} {
} for (int32 i = 0; i < InVector.size(); ++i)
{
return -1; if (InVector[i] == InElement)
} {
return i;
FMatrix GLTFMeshBuilder::GetNodeTransform(tinygltf::Node* Node) }
{ }
if (Node->matrix.size() == 16)
{ return -1;
FMatrix Ret; }
for (int32 i = 0; i < 4; ++i)
{ FMatrix UGLTFMeshBuilder::GetNodeTransform(tinygltf::Node* Node)
for (int32 j = 0; j < 4; ++j) {
{ if (Node->matrix.size() == 16)
// Reverse order since glTF is column major and FMatrix is row major {
Ret.M[j][i] = Node->matrix[(4 * i) + j]; FMatrix Ret;
} for (int32 i = 0; i < 4; ++i)
} {
return Ret; for (int32 j = 0; j < 4; ++j)
} {
else if (Node->rotation.size() == 4 && Node->scale.size() == 3 && Node->translation.size() == 3) // Reverse order since glTF is column major and FMatrix is row major
{ Ret.M[j][i] = Node->matrix[(4 * i) + j];
FQuat Rotation((float)Node->rotation[0], (float)Node->rotation[1], (float)Node->rotation[2], (float)Node->rotation[3]); }
FVector Scale((float)Node->scale[0], (float)Node->scale[1], (float)Node->scale[2]); }
FVector Translation((float)Node->translation[0], (float)Node->translation[1], (float)Node->translation[2]); return Ret;
return FTransform(Rotation, Translation, Scale).ToMatrixWithScale(); }
} else if (Node->rotation.size() == 4 && Node->scale.size() == 3 && Node->translation.size() == 3)
else {
{ FQuat Rotation((float)Node->rotation[0], (float)Node->rotation[1], (float)Node->rotation[2], (float)Node->rotation[3]);
return FMatrix::Identity; FVector Scale((float)Node->scale[0], (float)Node->scale[1], (float)Node->scale[2]);
} FVector Translation((float)Node->translation[0], (float)Node->translation[1], (float)Node->translation[2]);
} return FTransform(Rotation, Translation, Scale).ToMatrixWithScale();
}
size_t GLTFMeshBuilder::TypeSize(int Type) const else
{ {
switch (Type) return FMatrix::Identity;
{ }
case TINYGLTF_COMPONENT_TYPE_BYTE: }
case TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE:
return sizeof(GLTFType<TINYGLTF_COMPONENT_TYPE_BYTE>::Type); size_t UGLTFMeshBuilder::TypeSize(int Type) const
{
case TINYGLTF_COMPONENT_TYPE_SHORT: switch (Type)
case TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT: {
return sizeof(GLTFType<TINYGLTF_COMPONENT_TYPE_SHORT>::Type); case TINYGLTF_COMPONENT_TYPE_BYTE:
case TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE:
case TINYGLTF_COMPONENT_TYPE_INT: return sizeof(GLTFType<TINYGLTF_COMPONENT_TYPE_BYTE>::Type);
case TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT:
return sizeof(GLTFType<TINYGLTF_COMPONENT_TYPE_INT>::Type); case TINYGLTF_COMPONENT_TYPE_SHORT:
case TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT:
case TINYGLTF_COMPONENT_TYPE_FLOAT: return sizeof(GLTFType<TINYGLTF_COMPONENT_TYPE_SHORT>::Type);
return sizeof(GLTFType<TINYGLTF_COMPONENT_TYPE_FLOAT>::Type);
case TINYGLTF_COMPONENT_TYPE_INT:
case TINYGLTF_COMPONENT_TYPE_DOUBLE: case TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT:
return sizeof(GLTFType<TINYGLTF_COMPONENT_TYPE_DOUBLE>::Type); return sizeof(GLTFType<TINYGLTF_COMPONENT_TYPE_INT>::Type);
default: case TINYGLTF_COMPONENT_TYPE_FLOAT:
return -1; return sizeof(GLTFType<TINYGLTF_COMPONENT_TYPE_FLOAT>::Type);
}
} case TINYGLTF_COMPONENT_TYPE_DOUBLE:
return sizeof(GLTFType<TINYGLTF_COMPONENT_TYPE_DOUBLE>::Type);
int32 GLTFMeshBuilder::GetNumWedges(tinygltf::Primitive* Prim) const
{ default:
switch (Prim->mode) return -1;
{ }
case TINYGLTF_MODE_TRIANGLES: }
return Prim->indices.size();
int32 UGLTFMeshBuilder::GetNumWedges(tinygltf::Primitive* Prim) const
case TINYGLTF_MODE_TRIANGLE_STRIP: {
case TINYGLTF_MODE_TRIANGLE_FAN: switch (Prim->mode)
return (Prim->indices.size() - 2) * 3; {
case TINYGLTF_MODE_TRIANGLES:
default: return Prim->indices.size();
return 0;
} case TINYGLTF_MODE_TRIANGLE_STRIP:
} case TINYGLTF_MODE_TRIANGLE_FAN:
return (Prim->indices.size() - 2) * 3;
void GLTFMeshBuilder::GetMaterialIndices(TArray<int32>& OutArray, tinygltf::Mesh& Mesh)
{ default:
for (auto Prim : Mesh.primitives) return 0;
{ }
int32 Index = MeshMaterials.Find(ToFString(Prim.material)); }
for (int i = 0; i < GetNumWedges(&Prim) / 3; ++i)
{ void UGLTFMeshBuilder::GetMaterialIndices(TArray<int32>& OutArray, tinygltf::Mesh& Mesh)
OutArray.Add(Index); {
} for (auto Prim : Mesh.primitives)
} {
} int32 Index = MeshMaterials.Find(ToFString(Prim.material));
for (int i = 0; i < GetNumWedges(&Prim) / 3; ++i)
template <typename SrcType, typename DstType> void GLTFMeshBuilder::BufferCopy(TArray<DstType>& OutArray, unsigned char* Data, int32 Count, size_t Stride) {
{ OutArray.Add(Index);
for (int32 i = 0; i < Count; ++i) }
{ }
// At this point, assume we can cast directly to the destination type }
OutArray.Add((DstType)BufferValue<SrcType>(Data));
Data += Stride; template <typename SrcType, typename DstType> void UGLTFMeshBuilder::BufferCopy(TArray<DstType>& OutArray, unsigned char* Data, int32 Count, size_t Stride)
} {
} for (int32 i = 0; i < Count; ++i)
{
template <typename SrcType> void GLTFMeshBuilder::BufferCopy(TArray<FVector2D> &OutArray, unsigned char* Data, int32 Count, size_t Stride) // At this point, assume we can cast directly to the destination type
{ OutArray.Add((DstType)BufferValue<SrcType>(Data));
for (int32 i = 0; i < Count; ++i) Data += Stride;
{ }
// At this point, assume we can cast directly to the destination type }
OutArray.Add(FVector2D(BufferValue<SrcType>(Data), BufferValue<SrcType>(Data + sizeof(SrcType))));
Data += Stride; template <typename SrcType> void UGLTFMeshBuilder::BufferCopy(TArray<FVector2D> &OutArray, unsigned char* Data, int32 Count, size_t Stride)
} {
} for (int32 i = 0; i < Count; ++i)
{
template <typename SrcType> void GLTFMeshBuilder::BufferCopy(TArray<FVector> &OutArray, unsigned char* Data, int32 Count, size_t Stride) // At this point, assume we can cast directly to the destination type
{ OutArray.Add(FVector2D(BufferValue<SrcType>(Data), BufferValue<SrcType>(Data + sizeof(SrcType))));
for (int32 i = 0; i < Count; ++i) Data += Stride;
{ }
// At this point, assume we can cast directly to the destination type }
OutArray.Add(FVector(BufferValue<SrcType>(Data), BufferValue<SrcType>(Data + sizeof(SrcType)), BufferValue<SrcType>(Data + 2 * sizeof(SrcType))));
Data += Stride; template <typename SrcType> void UGLTFMeshBuilder::BufferCopy(TArray<FVector> &OutArray, unsigned char* Data, int32 Count, size_t Stride)
} {
} for (int32 i = 0; i < Count; ++i)
{
template <typename SrcType> void GLTFMeshBuilder::BufferCopy(TArray<FVector4> &OutArray, unsigned char* Data, int32 Count, size_t Stride) // At this point, assume we can cast directly to the destination type
{ OutArray.Add(FVector(BufferValue<SrcType>(Data), BufferValue<SrcType>(Data + sizeof(SrcType)), BufferValue<SrcType>(Data + 2 * sizeof(SrcType))));
for (int32 i = 0; i < Count; ++i) Data += Stride;
{ }
// At this point, assume we can cast directly to the destination type }
OutArray.Add(FVector4(BufferValue<SrcType>(Data), BufferValue<SrcType>(Data + sizeof(SrcType)), BufferValue<SrcType>(Data + 2 * sizeof(SrcType)), BufferValue<SrcType>(Data + 3 * sizeof(SrcType))));
Data += Stride; template <typename SrcType> void UGLTFMeshBuilder::BufferCopy(TArray<FVector4> &OutArray, unsigned char* Data, int32 Count, size_t Stride)
} {
} for (int32 i = 0; i < Count; ++i)
{
template <typename SrcType> void GLTFMeshBuilder::BufferCopy(TArray<FColor> &OutArray, int InType, unsigned char* Data, int32 Count, size_t Stride) // At this point, assume we can cast directly to the destination type
{ OutArray.Add(FVector4(BufferValue<SrcType>(Data), BufferValue<SrcType>(Data + sizeof(SrcType)), BufferValue<SrcType>(Data + 2 * sizeof(SrcType)), BufferValue<SrcType>(Data + 3 * sizeof(SrcType))));
switch (InType) Data += Stride;
{ }
case TINYGLTF_TYPE_VEC3: }
for (int32 i = 0; i < Count; ++i)
{ template <typename SrcType> void UGLTFMeshBuilder::BufferCopy(TArray<FColor> &OutArray, int InType, unsigned char* Data, int32 Count, size_t Stride)
// At this point, assume we can cast directly to the destination type {
OutArray.Add(FColor(BufferValue<SrcType>(Data), BufferValue<SrcType>(Data + sizeof(SrcType)), BufferValue<SrcType>(Data + 2 * sizeof(SrcType)))); switch (InType)
Data += Stride; {
} case TINYGLTF_TYPE_VEC3:
break; for (int32 i = 0; i < Count; ++i)
case TINYGLTF_TYPE_VEC4: {
for (int32 i = 0; i < Count; ++i) // At this point, assume we can cast directly to the destination type
{ OutArray.Add(FColor(BufferValue<SrcType>(Data), BufferValue<SrcType>(Data + sizeof(SrcType)), BufferValue<SrcType>(Data + 2 * sizeof(SrcType))));
// At this point, assume we can cast directly to the destination type Data += Stride;
OutArray.Add(FColor(BufferValue<SrcType>(Data), BufferValue<SrcType>(Data + sizeof(SrcType)), BufferValue<SrcType>(Data + 2 * sizeof(SrcType)), BufferValue<SrcType>(Data + 3 * sizeof(SrcType)))); }
Data += Stride; break;
} case TINYGLTF_TYPE_VEC4:
break; for (int32 i = 0; i < Count; ++i)
default: return; {
} // At this point, assume we can cast directly to the destination type
} OutArray.Add(FColor(BufferValue<SrcType>(Data), BufferValue<SrcType>(Data + sizeof(SrcType)), BufferValue<SrcType>(Data + 2 * sizeof(SrcType)), BufferValue<SrcType>(Data + 3 * sizeof(SrcType))));
Data += Stride;
void GLTFMeshBuilder::AddTokenizedErrorMessage(TSharedRef<FTokenizedMessage> Error, FName ErrorName) }
{ break;
UE_LOG(LogTemp, Warning, TEXT("%s"), *(Error->ToText().ToString())); default: return;
}
}
void UGLTFMeshBuilder::AddTokenizedErrorMessage(TSharedRef<FTokenizedMessage> Error, FName ErrorName)
{
UE_LOG(LogTemp, Warning, TEXT("%s"), *(Error->ToText().ToString()));
} }
\ No newline at end of file
/// @file GLTFMeshBuilder.h by Robert Poncelet /// @file GLTFMeshBuilder.h by Robert Poncelet
#pragma once #pragma once
#include "UnrealString.h" #include "UnrealString.h"
#include "TokenizedMessage.h" #include "TokenizedMessage.h"
#include "GLTFImportOptions.h" #include "GLTFImportOptions.h"
#include <string> #include <string>
#include <vector> #include <vector>
class UStaticMesh; #include "GLTFMeshBuilder.generated.h"
class UMaterialInterface;
struct FRawMesh; class UStaticMesh;
class UMaterialInterface;
/// Forward-declared TinyGLTF types since its header can only be #included in one source file. struct FRawMesh;
/// This also means that we must use pointers to these types outside of GLTFMeshBuilder.cpp.
namespace tinygltf /// Forward-declared TinyGLTF types since its header can only be #included in one source file.
{ /// This also means that we must use pointers to these types outside of GLTFMeshBuilder.cpp.
class TinyGLTFLoader; namespace tinygltf
class Scene; {
class Node; class TinyGLTFLoader;
struct ACCESSOR; class Scene;
typedef struct ACCESSOR Accessor; class Node;
struct PRIMITIVE; struct ACCESSOR;
typedef struct PRIMITIVE Primitive; typedef struct ACCESSOR Accessor;
struct MESH; struct PRIMITIVE;
typedef struct MESH Mesh; typedef struct PRIMITIVE Primitive;
struct MATERIAL; struct MESH;
typedef struct MATERIAL Material; typedef struct MESH Mesh;
} struct MATERIAL;
typedef struct MATERIAL Material;
/// Works in conjunction with TinyGLTF and Unreal's Static Mesh build system to return a UStaticMesh to the factory. This class is adapted from FbxImporter. }
class GLTFMeshBuilder
{ /// Works in conjunction with TinyGLTF and Unreal's Static Mesh build system to return a UStaticMesh to the factory. This class is adapted from FbxImporter.
public: UCLASS()
GLTFMeshBuilder(FString FilePath); class GLTFLOADER_API UGLTFMeshBuilder : public UObject
~GLTFMeshBuilder(); {
GENERATED_BODY()
/// Returns whether we have a valid glTF scene loaded up. For a new MeshBuilder, this should always be queried before calling other functions. public:
bool LoadedSuccessfully() { return LoadSuccess; } UGLTFMeshBuilder();
UGLTFMeshBuilder(FString FilePath);
/// Returns the number of meshes owned by a given node. ~UGLTFMeshBuilder();
int32 GetMeshCount(FString NodeName);
/// Returns whether we have a valid glTF scene loaded up. For a new MeshBuilder, this should always be queried before calling other functions.
/// Returns the name of the scene's root node. bool LoadedSuccessfully() { return LoadSuccess; }
FString GetRootNode();
/// Returns the number of meshes owned by a given node.
/// Obtains the mesh names of a node (and optionally its children); useful as an argument to <B>ImportStaticMeshAsSingle()</B>. int32 GetMeshCount(FString NodeName);
TArray<FString> GetMeshNames(FString NodeName, bool GetChildren = true);
/// Returns the name of the scene's root node.
/// Organises materials and builds the StaticMesh using a RawMesh filled with data using <B>BuildStaticMeshFromGeometry()</B>. FString GetRootNode();
/// This function mirrors that in FFbxImporter of the same name.
/// @param InParent A pointer provided by the system importing the file (i.e. probably AssetImportModule) so we know where the saved package goes. /// Obtains the mesh names of a node (and optionally its children); useful as an argument to <B>ImportStaticMeshAsSingle()</B>.
/// @param MeshNameArray An array of strings used as keys to obtain the actual meshes from the glTF scene. TArray<FString> GetMeshNames(FString NodeName, bool GetChildren = true);
/// @param InName The name of the package to be saved.
/// @param Flags Metadata used for the creation of the new package. /// Organises materials and builds the StaticMesh using a RawMesh filled with data using <B>BuildStaticMeshFromGeometry()</B>.
/// @param InStaticMesh A pointer to the StaticMesh to be built and have this new geometry added to it. /// This function mirrors that in FFbxImporter of the same name.
UStaticMeshComponent* ImportStaticMeshAsSingle(UObject* InParent, TArray<FString>& MeshNameArray, const FName InName, EObjectFlags Flags, UStaticMeshComponent* InStaticMesh); /// @param InParent A pointer provided by the system importing the file (i.e. probably AssetImportModule) so we know where the saved package goes.
/// @param MeshNameArray An array of strings used as keys to obtain the actual meshes from the glTF scene.
/// Obtains the geometry data from the file and adds it to the RawMesh ready to be built for the StaticMesh. /// @param InName The name of the package to be saved.
/// This function mirrors that in FFbxImporter of the same name. /// @param Flags Metadata used for the creation of the new package.
/// @param Mesh The glTF mesh to grab the data from. /// @param InStaticMesh A pointer to the StaticMesh to be built and have this new geometry added to it.
/// @param StaticMesh The asset the mesh will be built for. This will eventually be the object serialised and saved in the import process. UStaticMeshComponent* ImportStaticMeshAsSingle(UObject* InParent, TArray<FString>& MeshNameArray, const FName InName, EObjectFlags Flags, UStaticMeshComponent* InStaticMesh);
/// @param MeshMaterials An array of materials to convert to those used by the engine and send to the build process.
/// @param LODIndex Level of detail for this mesh - currently unused i.e. always 0. /// Obtains the geometry data from the file and adds it to the RawMesh ready to be built for the StaticMesh.
/// @param RawMesh The intermediate container for the data between the external format (glTF in our case) and the built StaticMesh. /// This function mirrors that in FFbxImporter of the same name.
bool BuildStaticMeshFromGeometry(tinygltf::Mesh* Mesh, UStaticMeshComponent* StaticMesh, int LODIndex, FRawMesh& RawMesh); /// @param Mesh The glTF mesh to grab the data from.
/// @param StaticMesh The asset the mesh will be built for. This will eventually be the object serialised and saved in the import process.
/// Material/texture system does nothing currently. /// @param MeshMaterials An array of materials to convert to those used by the engine and send to the build process.
UMaterialInterface* ToUMaterial(tinygltf::Material* Material); /// @param LODIndex Level of detail for this mesh - currently unused i.e. always 0.
void CreateUnrealMaterial(tinygltf::Material* Material, TArray<UMaterialInterface*>& OutMaterials) { return; }; /// @param RawMesh The intermediate container for the data between the external format (glTF in our case) and the built StaticMesh.
int32 CreateNodeMaterials(tinygltf::Node* Node, TArray<UMaterialInterface*>& OutMaterials) { return 0; }; bool BuildStaticMeshFromGeometry(tinygltf::Mesh* Mesh, UStaticMeshComponent* StaticMesh, int LODIndex, FRawMesh& RawMesh);
void ImportTexturesFromNode(tinygltf::Node* Node) { return; }
/// Material/texture system does nothing currently.
/// Logs an error message. UMaterialInterface* ToUMaterial(tinygltf::Material* Material);
void AddTokenizedErrorMessage(TSharedRef<FTokenizedMessage> Error, FName ErrorName); void CreateUnrealMaterial(tinygltf::Material* Material, TArray<UMaterialInterface*>& OutMaterials) { return; };
/// Returns the error message left by TinyGLTFLoader, if any. int32 CreateNodeMaterials(tinygltf::Node* Node, TArray<UMaterialInterface*>& OutMaterials) { return 0; };
FString GetError(); void ImportTexturesFromNode(tinygltf::Node* Node) { return; }
private: /// Logs an error message.
// Templated data copy functions, from highest to lowest level: void AddTokenizedErrorMessage(TSharedRef<FTokenizedMessage> Error, FName ErrorName);
/// Returns the error message left by TinyGLTFLoader, if any.
/// @name Level 4: ConvertAttrib FString GetError();
///@{
/// Fills a TArray with a particular vertex attribute. Set InAttribName to "__WedgeIndex" to use the "indices" accessor for each primitive, or "__MaterialIndices" to obtain the material index. private:
/// @param OutArray The array to fill with data from the imported file. // Templated data copy functions, from highest to lowest level:
/// @param Mesh The glTF from which to convert the specified attribute.
/// @param AttribName The name of the attribute to convert. /// @name Level 4: ConvertAttrib
/// @param UseWedgeIndices Whether to copy data for each triangle corner ("wedge") or each vertex. ///@{
/// @param AutoSetArraySize Whether to resize the array to the number of elements in this attribute (usually false since we may be adding to data from another mesh). /// Fills a TArray with a particular vertex attribute. Set InAttribName to "__WedgeIndex" to use the "indices" accessor for each primitive, or "__MaterialIndices" to obtain the material index.
template <typename T> bool ConvertAttrib(TArray<T> &OutArray, tinygltf::Mesh* Mesh, std::string AttribName, bool UseWedgeIndices = true, bool AutoSetArraySize = false); /// @param OutArray The array to fill with data from the imported file.
///@} /// @param Mesh The glTF from which to convert the specified attribute.
/// @param AttribName The name of the attribute to convert.
/// @name Level 3: GetBufferData /// @param UseWedgeIndices Whether to copy data for each triangle corner ("wedge") or each vertex.
///@{ /// @param AutoSetArraySize Whether to resize the array to the number of elements in this attribute (usually false since we may be adding to data from another mesh).
/// Fills a TArray with typed data; works at the glTF Accessor level and figures out which arguments to send to <B>BufferCopy()</B>. template <typename T> bool ConvertAttrib(TArray<T> &OutArray, tinygltf::Mesh* Mesh, std::string AttribName, bool UseWedgeIndices = true, bool AutoSetArraySize = false);
/// @param OutArray The array to fill with data from the imported file. ///@}
/// @param Accessor A pointer to a glTF Accessor containing the type data for the geometry attribute.
/// @param Append Whether to add to the array or overwrite the elements currently in it. /// @name Level 3: GetBufferData
template <typename T> bool GetBufferData (TArray<T> &OutArray, tinygltf::Accessor* Accessor, bool Append = true); ///@{
template <> bool GetBufferData<FVector2D> (TArray<FVector2D> &OutArray, tinygltf::Accessor* Accessor, bool Append ); /// Fills a TArray with typed data; works at the glTF Accessor level and figures out which arguments to send to <B>BufferCopy()</B>.
template <> bool GetBufferData<FVector> (TArray<FVector> &OutArray, tinygltf::Accessor* Accessor, bool Append ); /// @param OutArray The array to fill with data from the imported file.
template <> bool GetBufferData<FVector4> (TArray<FVector4> &OutArray, tinygltf::Accessor* Accessor, bool Append ); /// @param Accessor A pointer to a glTF Accessor containing the type data for the geometry attribute.
///@} /// @param Append Whether to add to the array or overwrite the elements currently in it.
template <typename T> bool GetBufferData (TArray<T> &OutArray, tinygltf::Accessor* Accessor, bool Append = true);
/// @name Level 2: BufferCopy template <> bool GetBufferData<FVector2D> (TArray<FVector2D> &OutArray, tinygltf::Accessor* Accessor, bool Append );
///@{ template <> bool GetBufferData<FVector> (TArray<FVector> &OutArray, tinygltf::Accessor* Accessor, bool Append );
/// Handles filling the TArray at the data type level. template <> bool GetBufferData<FVector4> (TArray<FVector4> &OutArray, tinygltf::Accessor* Accessor, bool Append );
/// @param OutArray The array to fill with data from the imported file. ///@}
/// @param Data A pointer to the raw data to use as the argument to <B>BufferValue()</B>.
/// @param Count The number of elements to add to the array i.e. the number of calls to <B>BufferValue()</B>. /// @name Level 2: BufferCopy
/// @param Stride The number of bytes between the first byte of each element - usually the size of one element. ///@{
template <typename SrcType, typename DstType> void BufferCopy(TArray<DstType> &OutArray, unsigned char* Data, int32 Count, size_t Stride); /// Handles filling the TArray at the data type level.
template <typename SrcType> void BufferCopy(TArray<FVector2D> &OutArray, unsigned char* Data, int32 Count, size_t Stride); /// @param OutArray The array to fill with data from the imported file.
template <typename SrcType> void BufferCopy(TArray<FVector> &OutArray, unsigned char* Data, int32 Count, size_t Stride); /// @param Data A pointer to the raw data to use as the argument to <B>BufferValue()</B>.
template <typename SrcType> void BufferCopy(TArray<FVector4> &OutArray, unsigned char* Data, int32 Count, size_t Stride); /// @param Count The number of elements to add to the array i.e. the number of calls to <B>BufferValue()</B>.
template <typename SrcType> void BufferCopy(TArray<FColor> &OutArray, int Type, unsigned char* Data, int32 Count, size_t Stride); /// @param Stride The number of bytes between the first byte of each element - usually the size of one element.
///@} template <typename SrcType, typename DstType> void BufferCopy(TArray<DstType> &OutArray, unsigned char* Data, int32 Count, size_t Stride);
template <typename SrcType> void BufferCopy(TArray<FVector2D> &OutArray, unsigned char* Data, int32 Count, size_t Stride);
/// @name Level 1: BufferValue template <typename SrcType> void BufferCopy(TArray<FVector> &OutArray, unsigned char* Data, int32 Count, size_t Stride);
///@{ template <typename SrcType> void BufferCopy(TArray<FVector4> &OutArray, unsigned char* Data, int32 Count, size_t Stride);
/// Obtains a single value from the geometry data buffer, accounting for endianness. template <typename SrcType> void BufferCopy(TArray<FColor> &OutArray, int Type, unsigned char* Data, int32 Count, size_t Stride);
/// Adapted from http://stackoverflow.com/questions/13001183/how-to-read-little-endian-integers-from-file-in-c ///@}
/// @param Data A pointer to the raw data to cast to the desired type.
/// @return The typed data value. /// @name Level 1: BufferValue
template <typename T> T BufferValue(void* Data); ///@{
///@} /// Obtains a single value from the geometry data buffer, accounting for endianness.
/// Adapted from http://stackoverflow.com/questions/13001183/how-to-read-little-endian-integers-from-file-in-c
/// Separate function to obtain material indices since it is not stored as a buffer. Should be called after MeshMaterials has been filled in. /// @param Data A pointer to the raw data to cast to the desired type.
void GetMaterialIndices(TArray<int32>& OutArray, tinygltf::Mesh& Mesh); /// @return The typed data value.
template <typename T> T BufferValue(void* Data);
// Miscellaneous helper functions ///@}
/// Reverses the order of every group of 3 elements. /// Separate function to obtain material indices since it is not stored as a buffer. Should be called after MeshMaterials has been filled in.
template<typename T> void ReverseTriDirection(TArray<T>& OutArray); void GetMaterialIndices(TArray<int32>& OutArray, tinygltf::Mesh& Mesh);
/// Whether a mesh's geometry has a specified attribute.
bool HasAttribute(tinygltf::Mesh* Mesh, std::string AttribName) const; // Miscellaneous helper functions
/// Similar to TArray's Find() function; returns the array index if the specified object was found, -1 otherwise.
template <typename T> int32 FindInStdVector(const std::vector<T> &InVector, const T &InElement) const; /// Reverses the order of every group of 3 elements.
/// Returns the transform of a node relative to its parent. template<typename T> void ReverseTriDirection(TArray<T>& OutArray);
FMatrix GetNodeTransform(tinygltf::Node* Node); /// Whether a mesh's geometry has a specified attribute.
/// Returns the size of the C++ data type given the corresponding glTF type. bool HasAttribute(tinygltf::Mesh* Mesh, std::string AttribName) const;
size_t TypeSize(int Type) const; /// Similar to TArray's Find() function; returns the array index if the specified object was found, -1 otherwise.
/// Returns the number of triangle corners given a glTF primitive, taking into account its draw mode. template <typename T> int32 FindInStdVector(const std::vector<T> &InVector, const T &InElement) const;
int32 GetNumWedges(tinygltf::Primitive* Prim) const; /// Returns the transform of a node relative to its parent.
/// Returns the owning node of a given mesh. FMatrix GetNodeTransform(tinygltf::Node* Node);
tinygltf::Node* GetMeshParentNode(tinygltf::Mesh* InMesh); /// Returns the size of the C++ data type given the corresponding glTF type.
size_t TypeSize(int Type) const;
/// @name String Conversion /// Returns the number of triangle corners given a glTF primitive, taking into account its draw mode.
///@{ int32 GetNumWedges(tinygltf::Primitive* Prim) const;
/// Helper functions for converting between Unreal's and STL's strings. /// Returns the owning node of a given mesh.
static FString ToFString(std::string InString); tinygltf::Node* GetMeshParentNode(tinygltf::Mesh* InMesh);
static std::string ToStdString(FString InString);
///@} /// @name String Conversion
///@{
/// /// Helper functions for converting between Unreal's and STL's strings.
TWeakObjectPtr<UObject> Parent; static FString ToFString(std::string InString);
tinygltf::TinyGLTFLoader* Loader; static std::string ToStdString(FString InString);
tinygltf::Scene* Scene; ///@}
TArray<FString> MeshMaterials;
bool LoadSuccess; ///
FString Error; TWeakObjectPtr<UObject> Parent;
}; tinygltf::TinyGLTFLoader* Loader;
tinygltf::Scene* Scene;
TArray<FString> MeshMaterials;
bool LoadSuccess;
FString Error;
};
/* /*
* Copyright 2009-2010 Cybozu Labs, Inc. * Copyright 2009-2010 Cybozu Labs, Inc.
* Copyright 2011-2014 Kazuho Oku * Copyright 2011-2014 Kazuho Oku
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
* *
* 1. Redistributions of source code must retain the above copyright notice, * 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer. * this list of conditions and the following disclaimer.
* *
* 2. Redistributions in binary form must reproduce the above copyright notice, * 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation * this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution. * and/or other materials provided with the distribution.
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE. * POSSIBILITY OF SUCH DAMAGE.
*/ */
#ifndef picojson_h #ifndef picojson_h
#define picojson_h #define picojson_h
#include <algorithm> #include <algorithm>
#include <cstdio> #include <cstdio>
#include <cstdlib> #include <cstdlib>
#include <cstring> #include <cstring>
#include <cstddef> #include <cstddef>
#include <iostream> #include <iostream>
#include <iterator> #include <iterator>
#include <limits> #include <limits>
#include <map> #include <map>
#include <stdexcept> #include <stdexcept>
#include <string> #include <string>
#include <vector> #include <vector>
// for isnan/isinf // for isnan/isinf
#if __cplusplus>=201103L #if __cplusplus>=201103L
# include <cmath> # include <cmath>
#else #else
extern "C" { extern "C" {
# ifdef _MSC_VER # ifdef _MSC_VER
# include <float.h> # include <float.h>
# elif defined(__INTEL_COMPILER) # elif defined(__INTEL_COMPILER)
# include <mathimf.h> # include <mathimf.h>
# else # else
# include <math.h> # include <math.h>
# endif # endif
} }
#endif #endif
#ifndef PICOJSON_USE_RVALUE_REFERENCE #ifndef PICOJSON_USE_RVALUE_REFERENCE
# if (defined(__cpp_rvalue_references) && __cpp_rvalue_references >= 200610) || (defined(_MSC_VER) && _MSC_VER >= 1600) # if (defined(__cpp_rvalue_references) && __cpp_rvalue_references >= 200610) || (defined(_MSC_VER) && _MSC_VER >= 1600)
# define PICOJSON_USE_RVALUE_REFERENCE 1 # define PICOJSON_USE_RVALUE_REFERENCE 1
# else # else
# define PICOJSON_USE_RVALUE_REFERENCE 0 # define PICOJSON_USE_RVALUE_REFERENCE 0
# endif # endif
#endif//PICOJSON_USE_RVALUE_REFERENCE #endif//PICOJSON_USE_RVALUE_REFERENCE
// experimental support for int64_t (see README.mkdn for detail) // experimental support for int64_t (see README.mkdn for detail)
#ifdef PICOJSON_USE_INT64 #ifdef PICOJSON_USE_INT64
# define __STDC_FORMAT_MACROS # define __STDC_FORMAT_MACROS
# include <errno.h> # include <errno.h>
# include <inttypes.h> # include <inttypes.h>
#endif #endif
// to disable the use of localeconv(3), set PICOJSON_USE_LOCALE to 0 // to disable the use of localeconv(3), set PICOJSON_USE_LOCALE to 0
#ifndef PICOJSON_USE_LOCALE #ifndef PICOJSON_USE_LOCALE
# define PICOJSON_USE_LOCALE 1 # define PICOJSON_USE_LOCALE 1
#endif #endif
#if PICOJSON_USE_LOCALE #if PICOJSON_USE_LOCALE
extern "C" { extern "C" {
# include <locale.h> # include <locale.h>
} }
#endif #endif
#ifndef PICOJSON_ASSERT #ifndef PICOJSON_ASSERT
# define PICOJSON_ASSERT(e) do { if (! (e)) throw std::runtime_error(#e); } while (0) # define PICOJSON_ASSERT(e) do { if (! (e)) throw std::runtime_error(#e); } while (0)
#endif #endif
#ifdef _MSC_VER #ifdef _MSC_VER
#define SNPRINTF _snprintf_s #define SNPRINTF _snprintf_s
#pragma warning(push) #pragma warning(push)
#pragma warning(disable : 4244) // conversion from int to char #pragma warning(disable : 4244) // conversion from int to char
#pragma warning(disable : 4127) // conditional expression is constant #pragma warning(disable : 4127) // conditional expression is constant
#pragma warning(disable : 4702) // unreachable code #pragma warning(disable : 4702) // unreachable code
#else #else
#define SNPRINTF snprintf #define SNPRINTF snprintf
#endif #endif
namespace picojson { namespace picojson {
enum { enum {
null_type, null_type,
boolean_type, boolean_type,
number_type, number_type,
string_type, string_type,
array_type, array_type,
object_type object_type
#ifdef PICOJSON_USE_INT64 #ifdef PICOJSON_USE_INT64
, int64_type , int64_type
#endif #endif
}; };
enum { enum {
INDENT_WIDTH = 2 INDENT_WIDTH = 2
}; };
struct null {}; struct null {};
class value { class value {
public: public:
typedef std::vector<value> array; typedef std::vector<value> array;
typedef std::map<std::string, value> object; typedef std::map<std::string, value> object;
union _storage { union _storage {
bool boolean_; bool boolean_;
double number_; double number_;
#ifdef PICOJSON_USE_INT64 #ifdef PICOJSON_USE_INT64
int64_t int64_; int64_t int64_;
#endif #endif
std::string* string_; std::string* string_;
array* array_; array* array_;
object* object_; object* object_;
}; };
protected: protected:
int type_; int type_;
_storage u_; _storage u_;
public: public:
value(); value();
value(int type, bool); value(int type, bool);
explicit value(bool b); explicit value(bool b);
#ifdef PICOJSON_USE_INT64 #ifdef PICOJSON_USE_INT64
explicit value(int64_t i); explicit value(int64_t i);
#endif #endif
explicit value(double n); explicit value(double n);
explicit value(const std::string& s); explicit value(const std::string& s);
explicit value(const array& a); explicit value(const array& a);
explicit value(const object& o); explicit value(const object& o);
explicit value(const char* s); explicit value(const char* s);
value(const char* s, size_t len); value(const char* s, size_t len);
~value(); ~value();
value(const value& x); value(const value& x);
value& operator=(const value& x); value& operator=(const value& x);
#if PICOJSON_USE_RVALUE_REFERENCE #if PICOJSON_USE_RVALUE_REFERENCE
value(value&& x)throw(); value(value&& x)throw();
value& operator=(value&& x)throw(); value& operator=(value&& x)throw();
#endif #endif
void swap(value& x)throw(); void swap(value& x)throw();
template <typename T> bool is() const; template <typename T> bool is() const;
template <typename T> const T& get() const; template <typename T> const T& get() const;
template <typename T> T& get(); template <typename T> T& get();
bool evaluate_as_boolean() const; bool evaluate_as_boolean() const;
const value& get(size_t idx) const; const value& get(size_t idx) const;
const value& get(const std::string& key) const; const value& get(const std::string& key) const;
value& get(size_t idx); value& get(size_t idx);
value& get(const std::string& key); value& get(const std::string& key);
bool contains(size_t idx) const; bool contains(size_t idx) const;
bool contains(const std::string& key) const; bool contains(const std::string& key) const;
std::string to_str() const; std::string to_str() const;
template <typename Iter> void serialize(Iter os, bool prettify = false) const; template <typename Iter> void serialize(Iter os, bool prettify = false) const;
std::string serialize(bool prettify = false) const; std::string serialize(bool prettify = false) const;
private: private:
template <typename T> value(const T*); // intentionally defined to block implicit conversion of pointer to bool template <typename T> value(const T*); // intentionally defined to block implicit conversion of pointer to bool
template <typename Iter> static void _indent(Iter os, int indent); template <typename Iter> static void _indent(Iter os, int indent);
template <typename Iter> void _serialize(Iter os, int indent) const; template <typename Iter> void _serialize(Iter os, int indent) const;
std::string _serialize(int indent) const; std::string _serialize(int indent) const;
}; };
typedef value::array array; typedef value::array array;
typedef value::object object; typedef value::object object;
inline value::value() : type_(null_type) {} inline value::value() : type_(null_type) {}
inline value::value(int type, bool) : type_(type) { inline value::value(int type, bool) : type_(type) {
switch (type) { switch (type) {
#define INIT(p, v) case p##type: u_.p = v; break #define INIT(p, v) case p##type: u_.p = v; break
INIT(boolean_, false); INIT(boolean_, false);
INIT(number_, 0.0); INIT(number_, 0.0);
#ifdef PICOJSON_USE_INT64 #ifdef PICOJSON_USE_INT64
INIT(int64_, 0); INIT(int64_, 0);
#endif #endif
INIT(string_, new std::string()); INIT(string_, new std::string());
INIT(array_, new array()); INIT(array_, new array());
INIT(object_, new object()); INIT(object_, new object());
#undef INIT #undef INIT
default: break; default: break;
} }
} }
inline value::value(bool b) : type_(boolean_type) { inline value::value(bool b) : type_(boolean_type) {
u_.boolean_ = b; u_.boolean_ = b;
} }
#ifdef PICOJSON_USE_INT64 #ifdef PICOJSON_USE_INT64
inline value::value(int64_t i) : type_(int64_type) { inline value::value(int64_t i) : type_(int64_type) {
u_.int64_ = i; u_.int64_ = i;
} }
#endif #endif
inline value::value(double n) : type_(number_type) { inline value::value(double n) : type_(number_type) {
if ( if (
#ifdef _MSC_VER #ifdef _MSC_VER
! _finite(n) ! _finite(n)
#elif __cplusplus>=201103L || !(defined(isnan) && defined(isinf)) #elif __cplusplus>=201103L || !(defined(isnan) && defined(isinf))
std::isnan(n) || std::isinf(n) std::isnan(n) || std::isinf(n)
#else #else
isnan(n) || isinf(n) isnan(n) || isinf(n)
#endif #endif
) { ) {
throw std::overflow_error(""); throw std::overflow_error("");
} }
u_.number_ = n; u_.number_ = n;
} }
inline value::value(const std::string& s) : type_(string_type) { inline value::value(const std::string& s) : type_(string_type) {
u_.string_ = new std::string(s); u_.string_ = new std::string(s);
} }
inline value::value(const array& a) : type_(array_type) { inline value::value(const array& a) : type_(array_type) {
u_.array_ = new array(a); u_.array_ = new array(a);
} }
inline value::value(const object& o) : type_(object_type) { inline value::value(const object& o) : type_(object_type) {
u_.object_ = new object(o); u_.object_ = new object(o);
} }
inline value::value(const char* s) : type_(string_type) { inline value::value(const char* s) : type_(string_type) {
u_.string_ = new std::string(s); u_.string_ = new std::string(s);
} }
inline value::value(const char* s, size_t len) : type_(string_type) { inline value::value(const char* s, size_t len) : type_(string_type) {
u_.string_ = new std::string(s, len); u_.string_ = new std::string(s, len);
} }
inline value::~value() { inline value::~value() {
switch (type_) { switch (type_) {
#define DEINIT(p) case p##type: delete u_.p; break #define DEINIT(p) case p##type: delete u_.p; break
DEINIT(string_); DEINIT(string_);
DEINIT(array_); DEINIT(array_);
DEINIT(object_); DEINIT(object_);
#undef DEINIT #undef DEINIT
default: break; default: break;
} }
} }
inline value::value(const value& x) : type_(x.type_) { inline value::value(const value& x) : type_(x.type_) {
switch (type_) { switch (type_) {
#define INIT(p, v) case p##type: u_.p = v; break #define INIT(p, v) case p##type: u_.p = v; break
INIT(string_, new std::string(*x.u_.string_)); INIT(string_, new std::string(*x.u_.string_));
INIT(array_, new array(*x.u_.array_)); INIT(array_, new array(*x.u_.array_));
INIT(object_, new object(*x.u_.object_)); INIT(object_, new object(*x.u_.object_));
#undef INIT #undef INIT
default: default:
u_ = x.u_; u_ = x.u_;
break; break;
} }
} }
inline value& value::operator=(const value& x) { inline value& value::operator=(const value& x) {
if (this != &x) { if (this != &x) {
value t(x); value t(x);
swap(t); swap(t);
} }
return *this; return *this;
} }
#if PICOJSON_USE_RVALUE_REFERENCE #if PICOJSON_USE_RVALUE_REFERENCE
inline value::value(value&& x)throw() : type_(null_type) { inline value::value(value&& x)throw() : type_(null_type) {
swap(x); swap(x);
} }
inline value& value::operator=(value&& x)throw() { inline value& value::operator=(value&& x)throw() {
swap(x); swap(x);
return *this; return *this;
} }
#endif #endif
inline void value::swap(value& x)throw() { inline void value::swap(value& x)throw() {
std::swap(type_, x.type_); std::swap(type_, x.type_);
std::swap(u_, x.u_); std::swap(u_, x.u_);
} }
#define IS(ctype, jtype) \ #define IS(ctype, jtype) \
template <> inline bool value::is<ctype>() const { \ template <> inline bool value::is<ctype>() const { \
return type_ == jtype##_type; \ return type_ == jtype##_type; \
} }
IS(null, null) IS(null, null)
IS(bool, boolean) IS(bool, boolean)
#ifdef PICOJSON_USE_INT64 #ifdef PICOJSON_USE_INT64
IS(int64_t, int64) IS(int64_t, int64)
#endif #endif
IS(std::string, string) IS(std::string, string)
IS(array, array) IS(array, array)
IS(object, object) IS(object, object)
#undef IS #undef IS
template <> inline bool value::is<double>() const { template <> inline bool value::is<double>() const {
return type_ == number_type return type_ == number_type
#ifdef PICOJSON_USE_INT64 #ifdef PICOJSON_USE_INT64
|| type_ == int64_type || type_ == int64_type
#endif #endif
; ;
} }
#define GET(ctype, var) \ #define GET(ctype, var) \
template <> inline const ctype& value::get<ctype>() const { \ template <> inline const ctype& value::get<ctype>() const { \
PICOJSON_ASSERT("type mismatch! call is<type>() before get<type>()" \ PICOJSON_ASSERT("type mismatch! call is<type>() before get<type>()" \
&& is<ctype>()); \ && is<ctype>()); \
return var; \ return var; \
} \ } \
template <> inline ctype& value::get<ctype>() { \ template <> inline ctype& value::get<ctype>() { \
PICOJSON_ASSERT("type mismatch! call is<type>() before get<type>()" \ PICOJSON_ASSERT("type mismatch! call is<type>() before get<type>()" \
&& is<ctype>()); \ && is<ctype>()); \
return var; \ return var; \
} }
GET(bool, u_.boolean_) GET(bool, u_.boolean_)
GET(std::string, *u_.string_) GET(std::string, *u_.string_)
GET(array, *u_.array_) GET(array, *u_.array_)
GET(object, *u_.object_) GET(object, *u_.object_)
#ifdef PICOJSON_USE_INT64 #ifdef PICOJSON_USE_INT64
GET(double, (type_ == int64_type && (const_cast<value*>(this)->type_ = number_type, const_cast<value*>(this)->u_.number_ = u_.int64_), u_.number_)) GET(double, (type_ == int64_type && (const_cast<value*>(this)->type_ = number_type, const_cast<value*>(this)->u_.number_ = u_.int64_), u_.number_))
GET(int64_t, u_.int64_) GET(int64_t, u_.int64_)
#else #else
GET(double, u_.number_) GET(double, u_.number_)
#endif #endif
#undef GET #undef GET
inline bool value::evaluate_as_boolean() const { inline bool value::evaluate_as_boolean() const {
switch (type_) { switch (type_) {
case null_type: case null_type:
return false; return false;
case boolean_type: case boolean_type:
return u_.boolean_; return u_.boolean_;
case number_type: case number_type:
return u_.number_ != 0; return u_.number_ != 0;
#ifdef PICOJSON_USE_INT64 #ifdef PICOJSON_USE_INT64
case int64_type: case int64_type:
return u_.int64_ != 0; return u_.int64_ != 0;
#endif #endif
case string_type: case string_type:
return ! u_.string_->empty(); return ! u_.string_->empty();
default: default:
return true; return true;
} }
} }
inline const value& value::get(size_t idx) const { inline const value& value::get(size_t idx) const {
static value s_null; static value s_null;
PICOJSON_ASSERT(is<array>()); PICOJSON_ASSERT(is<array>());
return idx < u_.array_->size() ? (*u_.array_)[idx] : s_null; return idx < u_.array_->size() ? (*u_.array_)[idx] : s_null;
} }
inline value& value::get(size_t idx) { inline value& value::get(size_t idx) {
static value s_null; static value s_null;
PICOJSON_ASSERT(is<array>()); PICOJSON_ASSERT(is<array>());
return idx < u_.array_->size() ? (*u_.array_)[idx] : s_null; return idx < u_.array_->size() ? (*u_.array_)[idx] : s_null;
} }
inline const value& value::get(const std::string& key) const { inline const value& value::get(const std::string& key) const {
static value s_null; static value s_null;
PICOJSON_ASSERT(is<object>()); PICOJSON_ASSERT(is<object>());
object::const_iterator i = u_.object_->find(key); object::const_iterator i = u_.object_->find(key);
return i != u_.object_->end() ? i->second : s_null; return i != u_.object_->end() ? i->second : s_null;
} }
inline value& value::get(const std::string& key) { inline value& value::get(const std::string& key) {
static value s_null; static value s_null;
PICOJSON_ASSERT(is<object>()); PICOJSON_ASSERT(is<object>());
object::iterator i = u_.object_->find(key); object::iterator i = u_.object_->find(key);
return i != u_.object_->end() ? i->second : s_null; return i != u_.object_->end() ? i->second : s_null;
} }
inline bool value::contains(size_t idx) const { inline bool value::contains(size_t idx) const {
PICOJSON_ASSERT(is<array>()); PICOJSON_ASSERT(is<array>());
return idx < u_.array_->size(); return idx < u_.array_->size();
} }
inline bool value::contains(const std::string& key) const { inline bool value::contains(const std::string& key) const {
PICOJSON_ASSERT(is<object>()); PICOJSON_ASSERT(is<object>());
object::const_iterator i = u_.object_->find(key); object::const_iterator i = u_.object_->find(key);
return i != u_.object_->end(); return i != u_.object_->end();
} }
inline std::string value::to_str() const { inline std::string value::to_str() const {
switch (type_) { switch (type_) {
case null_type: return "null"; case null_type: return "null";
case boolean_type: return u_.boolean_ ? "true" : "false"; case boolean_type: return u_.boolean_ ? "true" : "false";
#ifdef PICOJSON_USE_INT64 #ifdef PICOJSON_USE_INT64
case int64_type: { case int64_type: {
char buf[sizeof("-9223372036854775808")]; char buf[sizeof("-9223372036854775808")];
SNPRINTF(buf, sizeof(buf), "%" PRId64, u_.int64_); SNPRINTF(buf, sizeof(buf), "%" PRId64, u_.int64_);
return buf; return buf;
} }
#endif #endif
case number_type: { case number_type: {
char buf[256]; char buf[256];
double tmp; double tmp;
SNPRINTF(buf, sizeof(buf), fabs(u_.number_) < (1ULL << 53) && modf(u_.number_, &tmp) == 0 ? "%.f" : "%.17g", u_.number_); SNPRINTF(buf, sizeof(buf), fabs(u_.number_) < (1ULL << 53) && modf(u_.number_, &tmp) == 0 ? "%.f" : "%.17g", u_.number_);
#if PICOJSON_USE_LOCALE #if PICOJSON_USE_LOCALE
char *decimal_point = localeconv()->decimal_point; char *decimal_point = localeconv()->decimal_point;
if (strcmp(decimal_point, ".") != 0) { if (strcmp(decimal_point, ".") != 0) {
size_t decimal_point_len = strlen(decimal_point); size_t decimal_point_len = strlen(decimal_point);
for (char *p = buf; *p != '\0'; ++p) { for (char *p = buf; *p != '\0'; ++p) {
if (strncmp(p, decimal_point, decimal_point_len) == 0) { if (strncmp(p, decimal_point, decimal_point_len) == 0) {
return std::string(buf, p) + "." + (p + decimal_point_len); return std::string(buf, p) + "." + (p + decimal_point_len);
} }
} }
} }
#endif #endif
return buf; return buf;
} }
case string_type: return *u_.string_; case string_type: return *u_.string_;
case array_type: return "array"; case array_type: return "array";
case object_type: return "object"; case object_type: return "object";
default: PICOJSON_ASSERT(0); default: PICOJSON_ASSERT(0);
#ifdef _MSC_VER #ifdef _MSC_VER
__assume(0); __assume(0);
#endif #endif
} }
return std::string(); return std::string();
} }
template <typename Iter> void copy(const std::string& s, Iter oi) { template <typename Iter> void copy(const std::string& s, Iter oi) {
std::copy(s.begin(), s.end(), oi); std::copy(s.begin(), s.end(), oi);
} }
template <typename Iter> void serialize_str(const std::string& s, Iter oi) { template <typename Iter> void serialize_str(const std::string& s, Iter oi) {
*oi++ = '"'; *oi++ = '"';
for (std::string::const_iterator i = s.begin(); i != s.end(); ++i) { for (std::string::const_iterator i = s.begin(); i != s.end(); ++i) {
switch (*i) { switch (*i) {
#define MAP(val, sym) case val: copy(sym, oi); break #define MAP(val, sym) case val: copy(sym, oi); break
MAP('"', "\\\""); MAP('"', "\\\"");
MAP('\\', "\\\\"); MAP('\\', "\\\\");
MAP('/', "\\/"); MAP('/', "\\/");
MAP('\b', "\\b"); MAP('\b', "\\b");
MAP('\f', "\\f"); MAP('\f', "\\f");
MAP('\n', "\\n"); MAP('\n', "\\n");
MAP('\r', "\\r"); MAP('\r', "\\r");
MAP('\t', "\\t"); MAP('\t', "\\t");
#undef MAP #undef MAP
default: default:
if (static_cast<unsigned char>(*i) < 0x20 || *i == 0x7f) { if (static_cast<unsigned char>(*i) < 0x20 || *i == 0x7f) {
char buf[7]; char buf[7];
SNPRINTF(buf, sizeof(buf), "\\u%04x", *i & 0xff); SNPRINTF(buf, sizeof(buf), "\\u%04x", *i & 0xff);
copy(buf, buf + 6, oi); copy(buf, buf + 6, oi);
} else { } else {
*oi++ = *i; *oi++ = *i;
} }
break; break;
} }
} }
*oi++ = '"'; *oi++ = '"';
} }
template <typename Iter> void value::serialize(Iter oi, bool prettify) const { template <typename Iter> void value::serialize(Iter oi, bool prettify) const {
return _serialize(oi, prettify ? 0 : -1); return _serialize(oi, prettify ? 0 : -1);
} }
inline std::string value::serialize(bool prettify) const { inline std::string value::serialize(bool prettify) const {
return _serialize(prettify ? 0 : -1); return _serialize(prettify ? 0 : -1);
} }
template <typename Iter> void value::_indent(Iter oi, int indent) { template <typename Iter> void value::_indent(Iter oi, int indent) {
*oi++ = '\n'; *oi++ = '\n';
for (int i = 0; i < indent * INDENT_WIDTH; ++i) { for (int i = 0; i < indent * INDENT_WIDTH; ++i) {
*oi++ = ' '; *oi++ = ' ';
} }
} }
template <typename Iter> void value::_serialize(Iter oi, int indent) const { template <typename Iter> void value::_serialize(Iter oi, int indent) const {
switch (type_) { switch (type_) {
case string_type: case string_type:
serialize_str(*u_.string_, oi); serialize_str(*u_.string_, oi);
break; break;
case array_type: { case array_type: {
*oi++ = '['; *oi++ = '[';
if (indent != -1) { if (indent != -1) {
++indent; ++indent;
} }
for (array::const_iterator i = u_.array_->begin(); for (array::const_iterator i = u_.array_->begin();
i != u_.array_->end(); i != u_.array_->end();
++i) { ++i) {
if (i != u_.array_->begin()) { if (i != u_.array_->begin()) {
*oi++ = ','; *oi++ = ',';
} }
if (indent != -1) { if (indent != -1) {
_indent(oi, indent); _indent(oi, indent);
} }
i->_serialize(oi, indent); i->_serialize(oi, indent);
} }
if (indent != -1) { if (indent != -1) {
--indent; --indent;
if (! u_.array_->empty()) { if (! u_.array_->empty()) {
_indent(oi, indent); _indent(oi, indent);
} }
} }
*oi++ = ']'; *oi++ = ']';
break; break;
} }
case object_type: { case object_type: {
*oi++ = '{'; *oi++ = '{';
if (indent != -1) { if (indent != -1) {
++indent; ++indent;
} }
for (object::const_iterator i = u_.object_->begin(); for (object::const_iterator i = u_.object_->begin();
i != u_.object_->end(); i != u_.object_->end();
++i) { ++i) {
if (i != u_.object_->begin()) { if (i != u_.object_->begin()) {
*oi++ = ','; *oi++ = ',';
} }
if (indent != -1) { if (indent != -1) {
_indent(oi, indent); _indent(oi, indent);
} }
serialize_str(i->first, oi); serialize_str(i->first, oi);
*oi++ = ':'; *oi++ = ':';
if (indent != -1) { if (indent != -1) {
*oi++ = ' '; *oi++ = ' ';
} }
i->second._serialize(oi, indent); i->second._serialize(oi, indent);
} }
if (indent != -1) { if (indent != -1) {
--indent; --indent;
if (! u_.object_->empty()) { if (! u_.object_->empty()) {
_indent(oi, indent); _indent(oi, indent);
} }
} }
*oi++ = '}'; *oi++ = '}';
break; break;
} }
default: default:
copy(to_str(), oi); copy(to_str(), oi);
break; break;
} }
if (indent == 0) { if (indent == 0) {
*oi++ = '\n'; *oi++ = '\n';
} }
} }
inline std::string value::_serialize(int indent) const { inline std::string value::_serialize(int indent) const {
std::string s; std::string s;
_serialize(std::back_inserter(s), indent); _serialize(std::back_inserter(s), indent);
return s; return s;
} }
template <typename Iter> class input { template <typename Iter> class input {
protected: protected:
Iter cur_, end_; Iter cur_, end_;
int last_ch_; int last_ch_;
bool ungot_; bool ungot_;
int line_; int line_;
public: public:
input(const Iter& first, const Iter& last) : cur_(first), end_(last), last_ch_(-1), ungot_(false), line_(1) {} input(const Iter& first, const Iter& last) : cur_(first), end_(last), last_ch_(-1), ungot_(false), line_(1) {}
int getc() { int getc() {
if (ungot_) { if (ungot_) {
ungot_ = false; ungot_ = false;
return last_ch_; return last_ch_;
} }
if (cur_ == end_) { if (cur_ == end_) {
last_ch_ = -1; last_ch_ = -1;
return -1; return -1;
} }
if (last_ch_ == '\n') { if (last_ch_ == '\n') {
line_++; line_++;
} }
last_ch_ = *cur_ & 0xff; last_ch_ = *cur_ & 0xff;
++cur_; ++cur_;
return last_ch_; return last_ch_;
} }
void ungetc() { void ungetc() {
if (last_ch_ != -1) { if (last_ch_ != -1) {
PICOJSON_ASSERT(! ungot_); PICOJSON_ASSERT(! ungot_);
ungot_ = true; ungot_ = true;
} }
} }
Iter cur() const { return cur_; } Iter cur() const { return cur_; }
int line() const { return line_; } int line() const { return line_; }
void skip_ws() { void skip_ws() {
while (1) { while (1) {
int ch = getc(); int ch = getc();
if (! (ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r')) { if (! (ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r')) {
ungetc(); ungetc();
break; break;
} }
} }
} }
bool expect(int expect) { bool expect(int expect) {
skip_ws(); skip_ws();
if (getc() != expect) { if (getc() != expect) {
ungetc(); ungetc();
return false; return false;
} }
return true; return true;
} }
bool match(const std::string& pattern) { bool match(const std::string& pattern) {
for (std::string::const_iterator pi(pattern.begin()); for (std::string::const_iterator pi(pattern.begin());
pi != pattern.end(); pi != pattern.end();
++pi) { ++pi) {
if (getc() != *pi) { if (getc() != *pi) {
ungetc(); ungetc();
return false; return false;
} }
} }
return true; return true;
} }
}; };
template<typename Iter> inline int _parse_quadhex(input<Iter> &in) { template<typename Iter> inline int _parse_quadhex(input<Iter> &in) {
int uni_ch = 0, hex; int uni_ch = 0, hex;
for (int i = 0; i < 4; i++) { for (int i = 0; i < 4; i++) {
if ((hex = in.getc()) == -1) { if ((hex = in.getc()) == -1) {
return -1; return -1;
} }
if ('0' <= hex && hex <= '9') { if ('0' <= hex && hex <= '9') {
hex -= '0'; hex -= '0';
} else if ('A' <= hex && hex <= 'F') { } else if ('A' <= hex && hex <= 'F') {
hex -= 'A' - 0xa; hex -= 'A' - 0xa;
} else if ('a' <= hex && hex <= 'f') { } else if ('a' <= hex && hex <= 'f') {
hex -= 'a' - 0xa; hex -= 'a' - 0xa;
} else { } else {
in.ungetc(); in.ungetc();
return -1; return -1;
} }
uni_ch = uni_ch * 16 + hex; uni_ch = uni_ch * 16 + hex;
} }
return uni_ch; return uni_ch;
} }
template<typename String, typename Iter> inline bool _parse_codepoint(String& out, input<Iter>& in) { template<typename String, typename Iter> inline bool _parse_codepoint(String& out, input<Iter>& in) {
int uni_ch; int uni_ch;
if ((uni_ch = _parse_quadhex(in)) == -1) { if ((uni_ch = _parse_quadhex(in)) == -1) {
return false; return false;
} }
if (0xd800 <= uni_ch && uni_ch <= 0xdfff) { if (0xd800 <= uni_ch && uni_ch <= 0xdfff) {
if (0xdc00 <= uni_ch) { if (0xdc00 <= uni_ch) {
// a second 16-bit of a surrogate pair appeared // a second 16-bit of a surrogate pair appeared
return false; return false;
} }
// first 16-bit of surrogate pair, get the next one // first 16-bit of surrogate pair, get the next one
if (in.getc() != '\\' || in.getc() != 'u') { if (in.getc() != '\\' || in.getc() != 'u') {
in.ungetc(); in.ungetc();
return false; return false;
} }
int second = _parse_quadhex(in); int second = _parse_quadhex(in);
if (! (0xdc00 <= second && second <= 0xdfff)) { if (! (0xdc00 <= second && second <= 0xdfff)) {
return false; return false;
} }
uni_ch = ((uni_ch - 0xd800) << 10) | ((second - 0xdc00) & 0x3ff); uni_ch = ((uni_ch - 0xd800) << 10) | ((second - 0xdc00) & 0x3ff);
uni_ch += 0x10000; uni_ch += 0x10000;
} }
if (uni_ch < 0x80) { if (uni_ch < 0x80) {
out.push_back(uni_ch); out.push_back(uni_ch);
} else { } else {
if (uni_ch < 0x800) { if (uni_ch < 0x800) {
out.push_back(0xc0 | (uni_ch >> 6)); out.push_back(0xc0 | (uni_ch >> 6));
} else { } else {
if (uni_ch < 0x10000) { if (uni_ch < 0x10000) {
out.push_back(0xe0 | (uni_ch >> 12)); out.push_back(0xe0 | (uni_ch >> 12));
} else { } else {
out.push_back(0xf0 | (uni_ch >> 18)); out.push_back(0xf0 | (uni_ch >> 18));
out.push_back(0x80 | ((uni_ch >> 12) & 0x3f)); out.push_back(0x80 | ((uni_ch >> 12) & 0x3f));
} }
out.push_back(0x80 | ((uni_ch >> 6) & 0x3f)); out.push_back(0x80 | ((uni_ch >> 6) & 0x3f));
} }
out.push_back(0x80 | (uni_ch & 0x3f)); out.push_back(0x80 | (uni_ch & 0x3f));
} }
return true; return true;
} }
template<typename String, typename Iter> inline bool _parse_string(String& out, input<Iter>& in) { template<typename String, typename Iter> inline bool _parse_string(String& out, input<Iter>& in) {
while (1) { while (1) {
int ch = in.getc(); int ch = in.getc();
if (ch < ' ') { if (ch < ' ') {
in.ungetc(); in.ungetc();
return false; return false;
} else if (ch == '"') { } else if (ch == '"') {
return true; return true;
} else if (ch == '\\') { } else if (ch == '\\') {
if ((ch = in.getc()) == -1) { if ((ch = in.getc()) == -1) {
return false; return false;
} }
switch (ch) { switch (ch) {
#define MAP(sym, val) case sym: out.push_back(val); break #define MAP(sym, val) case sym: out.push_back(val); break
MAP('"', '\"'); MAP('"', '\"');
MAP('\\', '\\'); MAP('\\', '\\');
MAP('/', '/'); MAP('/', '/');
MAP('b', '\b'); MAP('b', '\b');
MAP('f', '\f'); MAP('f', '\f');
MAP('n', '\n'); MAP('n', '\n');
MAP('r', '\r'); MAP('r', '\r');
MAP('t', '\t'); MAP('t', '\t');
#undef MAP #undef MAP
case 'u': case 'u':
if (! _parse_codepoint(out, in)) { if (! _parse_codepoint(out, in)) {
return false; return false;
} }
break; break;
default: default:
return false; return false;
} }
} else { } else {
out.push_back(ch); out.push_back(ch);
} }
} }
return false; return false;
} }
template <typename Context, typename Iter> inline bool _parse_array(Context& ctx, input<Iter>& in) { template <typename Context, typename Iter> inline bool _parse_array(Context& ctx, input<Iter>& in) {
if (! ctx.parse_array_start()) { if (! ctx.parse_array_start()) {
return false; return false;
} }
size_t idx = 0; size_t idx = 0;
if (in.expect(']')) { if (in.expect(']')) {
return ctx.parse_array_stop(idx); return ctx.parse_array_stop(idx);
} }
do { do {
if (! ctx.parse_array_item(in, idx)) { if (! ctx.parse_array_item(in, idx)) {
return false; return false;
} }
idx++; idx++;
} while (in.expect(',')); } while (in.expect(','));
return in.expect(']') && ctx.parse_array_stop(idx); return in.expect(']') && ctx.parse_array_stop(idx);
} }
template <typename Context, typename Iter> inline bool _parse_object(Context& ctx, input<Iter>& in) { template <typename Context, typename Iter> inline bool _parse_object(Context& ctx, input<Iter>& in) {
if (! ctx.parse_object_start()) { if (! ctx.parse_object_start()) {
return false; return false;
} }
if (in.expect('}')) { if (in.expect('}')) {
return true; return true;
} }
do { do {
std::string key; std::string key;
if (! in.expect('"') if (! in.expect('"')
|| ! _parse_string(key, in) || ! _parse_string(key, in)
|| ! in.expect(':')) { || ! in.expect(':')) {
return false; return false;
} }
if (! ctx.parse_object_item(in, key)) { if (! ctx.parse_object_item(in, key)) {
return false; return false;
} }
} while (in.expect(',')); } while (in.expect(','));
return in.expect('}'); return in.expect('}');
} }
template <typename Iter> inline std::string _parse_number(input<Iter>& in) { template <typename Iter> inline std::string _parse_number(input<Iter>& in) {
std::string num_str; std::string num_str;
while (1) { while (1) {
int ch = in.getc(); int ch = in.getc();
if (('0' <= ch && ch <= '9') || ch == '+' || ch == '-' if (('0' <= ch && ch <= '9') || ch == '+' || ch == '-'
|| ch == 'e' || ch == 'E') { || ch == 'e' || ch == 'E') {
num_str.push_back(ch); num_str.push_back(ch);
} else if (ch == '.') { } else if (ch == '.') {
#if PICOJSON_USE_LOCALE #if PICOJSON_USE_LOCALE
num_str += localeconv()->decimal_point; num_str += localeconv()->decimal_point;
#else #else
num_str.push_back('.'); num_str.push_back('.');
#endif #endif
} else { } else {
in.ungetc(); in.ungetc();
break; break;
} }
} }
return num_str; return num_str;
} }
template <typename Context, typename Iter> inline bool _parse(Context& ctx, input<Iter>& in) { template <typename Context, typename Iter> inline bool _parse(Context& ctx, input<Iter>& in) {
in.skip_ws(); in.skip_ws();
int ch = in.getc(); int ch = in.getc();
switch (ch) { switch (ch) {
#define IS(ch, text, op) case ch: \ #define IS(ch, text, op) case ch: \
if (in.match(text) && op) { \ if (in.match(text) && op) { \
return true; \ return true; \
} else { \ } else { \
return false; \ return false; \
} }
IS('n', "ull", ctx.set_null()); IS('n', "ull", ctx.set_null());
IS('f', "alse", ctx.set_bool(false)); IS('f', "alse", ctx.set_bool(false));
IS('t', "rue", ctx.set_bool(true)); IS('t', "rue", ctx.set_bool(true));
#undef IS #undef IS
case '"': case '"':
return ctx.parse_string(in); return ctx.parse_string(in);
case '[': case '[':
return _parse_array(ctx, in); return _parse_array(ctx, in);
case '{': case '{':
return _parse_object(ctx, in); return _parse_object(ctx, in);
default: default:
if (('0' <= ch && ch <= '9') || ch == '-') { if (('0' <= ch && ch <= '9') || ch == '-') {
double f; double f;
char *endp; char *endp;
in.ungetc(); in.ungetc();
std::string num_str = _parse_number(in); std::string num_str = _parse_number(in);
if (num_str.empty()) { if (num_str.empty()) {
return false; return false;
} }
#ifdef PICOJSON_USE_INT64 #ifdef PICOJSON_USE_INT64
{ {
errno = 0; errno = 0;
intmax_t ival = strtoimax(num_str.c_str(), &endp, 10); intmax_t ival = strtoimax(num_str.c_str(), &endp, 10);
if (errno == 0 if (errno == 0
&& std::numeric_limits<int64_t>::min() <= ival && std::numeric_limits<int64_t>::min() <= ival
&& ival <= std::numeric_limits<int64_t>::max() && ival <= std::numeric_limits<int64_t>::max()
&& endp == num_str.c_str() + num_str.size()) { && endp == num_str.c_str() + num_str.size()) {
ctx.set_int64(ival); ctx.set_int64(ival);
return true; return true;
} }
} }
#endif #endif
f = strtod(num_str.c_str(), &endp); f = strtod(num_str.c_str(), &endp);
if (endp == num_str.c_str() + num_str.size()) { if (endp == num_str.c_str() + num_str.size()) {
ctx.set_number(f); ctx.set_number(f);
return true; return true;
} }
return false; return false;
} }
break; break;
} }
in.ungetc(); in.ungetc();
return false; return false;
} }
class deny_parse_context { class deny_parse_context {
public: public:
bool set_null() { return false; } bool set_null() { return false; }
bool set_bool(bool) { return false; } bool set_bool(bool) { return false; }
#ifdef PICOJSON_USE_INT64 #ifdef PICOJSON_USE_INT64
bool set_int64(int64_t) { return false; } bool set_int64(int64_t) { return false; }
#endif #endif
bool set_number(double) { return false; } bool set_number(double) { return false; }
template <typename Iter> bool parse_string(input<Iter>&) { return false; } template <typename Iter> bool parse_string(input<Iter>&) { return false; }
bool parse_array_start() { return false; } bool parse_array_start() { return false; }
template <typename Iter> bool parse_array_item(input<Iter>&, size_t) { template <typename Iter> bool parse_array_item(input<Iter>&, size_t) {
return false; return false;
} }
bool parse_array_stop(size_t) { return false; } bool parse_array_stop(size_t) { return false; }
bool parse_object_start() { return false; } bool parse_object_start() { return false; }
template <typename Iter> bool parse_object_item(input<Iter>&, const std::string&) { template <typename Iter> bool parse_object_item(input<Iter>&, const std::string&) {
return false; return false;
} }
}; };
class default_parse_context { class default_parse_context {
protected: protected:
value* out_; value* out_;
public: public:
default_parse_context(value* out) : out_(out) {} default_parse_context(value* out) : out_(out) {}
bool set_null() { bool set_null() {
*out_ = value(); *out_ = value();
return true; return true;
} }
bool set_bool(bool b) { bool set_bool(bool b) {
*out_ = value(b); *out_ = value(b);
return true; return true;
} }
#ifdef PICOJSON_USE_INT64 #ifdef PICOJSON_USE_INT64
bool set_int64(int64_t i) { bool set_int64(int64_t i) {
*out_ = value(i); *out_ = value(i);
return true; return true;
} }
#endif #endif
bool set_number(double f) { bool set_number(double f) {
*out_ = value(f); *out_ = value(f);
return true; return true;
} }
template<typename Iter> bool parse_string(input<Iter>& in) { template<typename Iter> bool parse_string(input<Iter>& in) {
*out_ = value(string_type, false); *out_ = value(string_type, false);
return _parse_string(out_->get<std::string>(), in); return _parse_string(out_->get<std::string>(), in);
} }
bool parse_array_start() { bool parse_array_start() {
*out_ = value(array_type, false); *out_ = value(array_type, false);
return true; return true;
} }
template <typename Iter> bool parse_array_item(input<Iter>& in, size_t) { template <typename Iter> bool parse_array_item(input<Iter>& in, size_t) {
array& a = out_->get<array>(); array& a = out_->get<array>();
a.push_back(value()); a.push_back(value());
default_parse_context ctx(&a.back()); default_parse_context ctx(&a.back());
return _parse(ctx, in); return _parse(ctx, in);
} }
bool parse_array_stop(size_t) { return true; } bool parse_array_stop(size_t) { return true; }
bool parse_object_start() { bool parse_object_start() {
*out_ = value(object_type, false); *out_ = value(object_type, false);
return true; return true;
} }
template <typename Iter> bool parse_object_item(input<Iter>& in, const std::string& key) { template <typename Iter> bool parse_object_item(input<Iter>& in, const std::string& key) {
object& o = out_->get<object>(); object& o = out_->get<object>();
default_parse_context ctx(&o[key]); default_parse_context ctx(&o[key]);
return _parse(ctx, in); return _parse(ctx, in);
} }
private: private:
default_parse_context(const default_parse_context&); default_parse_context(const default_parse_context&);
default_parse_context& operator=(const default_parse_context&); default_parse_context& operator=(const default_parse_context&);
}; };
class null_parse_context { class null_parse_context {
public: public:
struct dummy_str { struct dummy_str {
void push_back(int) {} void push_back(int) {}
}; };
public: public:
null_parse_context() {} null_parse_context() {}
bool set_null() { return true; } bool set_null() { return true; }
bool set_bool(bool) { return true; } bool set_bool(bool) { return true; }
#ifdef PICOJSON_USE_INT64 #ifdef PICOJSON_USE_INT64
bool set_int64(int64_t) { return true; } bool set_int64(int64_t) { return true; }
#endif #endif
bool set_number(double) { return true; } bool set_number(double) { return true; }
template <typename Iter> bool parse_string(input<Iter>& in) { template <typename Iter> bool parse_string(input<Iter>& in) {
dummy_str s; dummy_str s;
return _parse_string(s, in); return _parse_string(s, in);
} }
bool parse_array_start() { return true; } bool parse_array_start() { return true; }
template <typename Iter> bool parse_array_item(input<Iter>& in, size_t) { template <typename Iter> bool parse_array_item(input<Iter>& in, size_t) {
return _parse(*this, in); return _parse(*this, in);
} }
bool parse_array_stop(size_t) { return true; } bool parse_array_stop(size_t) { return true; }
bool parse_object_start() { return true; } bool parse_object_start() { return true; }
template <typename Iter> bool parse_object_item(input<Iter>& in, const std::string&) { template <typename Iter> bool parse_object_item(input<Iter>& in, const std::string&) {
return _parse(*this, in); return _parse(*this, in);
} }
private: private:
null_parse_context(const null_parse_context&); null_parse_context(const null_parse_context&);
null_parse_context& operator=(const null_parse_context&); null_parse_context& operator=(const null_parse_context&);
}; };
// obsolete, use the version below // obsolete, use the version below
template <typename Iter> inline std::string parse(value& out, Iter& pos, const Iter& last) { template <typename Iter> inline std::string parse(value& out, Iter& pos, const Iter& last) {
std::string err; std::string err;
pos = parse(out, pos, last, &err); pos = parse(out, pos, last, &err);
return err; return err;
} }
template <typename Context, typename Iter> inline Iter _parse(Context& ctx, const Iter& first, const Iter& last, std::string* err) { template <typename Context, typename Iter> inline Iter _parse(Context& ctx, const Iter& first, const Iter& last, std::string* err) {
input<Iter> in(first, last); input<Iter> in(first, last);
if (! _parse(ctx, in) && err != NULL) { if (! _parse(ctx, in) && err != NULL) {
char buf[64]; char buf[64];
SNPRINTF(buf, sizeof(buf), "syntax error at line %d near: ", in.line()); SNPRINTF(buf, sizeof(buf), "syntax error at line %d near: ", in.line());
*err = buf; *err = buf;
while (1) { while (1) {
int ch = in.getc(); int ch = in.getc();
if (ch == -1 || ch == '\n') { if (ch == -1 || ch == '\n') {
break; break;
} else if (ch >= ' ') { } else if (ch >= ' ') {
err->push_back(ch); err->push_back(ch);
} }
} }
} }
return in.cur(); return in.cur();
} }
template <typename Iter> inline Iter parse(value& out, const Iter& first, const Iter& last, std::string* err) { template <typename Iter> inline Iter parse(value& out, const Iter& first, const Iter& last, std::string* err) {
default_parse_context ctx(&out); default_parse_context ctx(&out);
return _parse(ctx, first, last, err); return _parse(ctx, first, last, err);
} }
inline std::string parse(value& out, const std::string& s) { inline std::string parse(value& out, const std::string& s) {
std::string err; std::string err;
parse(out, s.begin(), s.end(), &err); parse(out, s.begin(), s.end(), &err);
return err; return err;
} }
inline std::string parse(value& out, std::istream& is) { inline std::string parse(value& out, std::istream& is) {
std::string err; std::string err;
parse(out, std::istreambuf_iterator<char>(is.rdbuf()), parse(out, std::istreambuf_iterator<char>(is.rdbuf()),
std::istreambuf_iterator<char>(), &err); std::istreambuf_iterator<char>(), &err);
return err; return err;
} }
template <typename T> struct last_error_t { template <typename T> struct last_error_t {
static std::string s; static std::string s;
}; };
template <typename T> std::string last_error_t<T>::s; template <typename T> std::string last_error_t<T>::s;
inline void set_last_error(const std::string& s) { inline void set_last_error(const std::string& s) {
last_error_t<bool>::s = s; last_error_t<bool>::s = s;
} }
inline const std::string& get_last_error() { inline const std::string& get_last_error() {
return last_error_t<bool>::s; return last_error_t<bool>::s;
} }
inline bool operator==(const value& x, const value& y) { inline bool operator==(const value& x, const value& y) {
if (x.is<null>()) if (x.is<null>())
return y.is<null>(); return y.is<null>();
#define PICOJSON_CMP(type) \ #define PICOJSON_CMP(type) \
if (x.is<type>()) \ if (x.is<type>()) \
return y.is<type>() && x.get<type>() == y.get<type>() return y.is<type>() && x.get<type>() == y.get<type>()
PICOJSON_CMP(bool); PICOJSON_CMP(bool);
PICOJSON_CMP(double); PICOJSON_CMP(double);
PICOJSON_CMP(std::string); PICOJSON_CMP(std::string);
PICOJSON_CMP(array); PICOJSON_CMP(array);
PICOJSON_CMP(object); PICOJSON_CMP(object);
#undef PICOJSON_CMP #undef PICOJSON_CMP
PICOJSON_ASSERT(0); PICOJSON_ASSERT(0);
#ifdef _MSC_VER #ifdef _MSC_VER
__assume(0); __assume(0);
#endif #endif
return false; return false;
} }
inline bool operator!=(const value& x, const value& y) { inline bool operator!=(const value& x, const value& y) {
return ! (x == y); return ! (x == y);
} }
} }
#if !PICOJSON_USE_RVALUE_REFERENCE #if !PICOJSON_USE_RVALUE_REFERENCE
namespace std { namespace std {
template<> inline void swap(picojson::value& x, picojson::value& y) template<> inline void swap(picojson::value& x, picojson::value& y)
{ {
x.swap(y); x.swap(y);
} }
} }
#endif #endif
inline std::istream& operator>>(std::istream& is, picojson::value& x) inline std::istream& operator>>(std::istream& is, picojson::value& x)
{ {
picojson::set_last_error(std::string()); picojson::set_last_error(std::string());
std::string err = picojson::parse(x, is); std::string err = picojson::parse(x, is);
if (! err.empty()) { if (! err.empty()) {
picojson::set_last_error(err); picojson::set_last_error(err);
is.setstate(std::ios::failbit); is.setstate(std::ios::failbit);
} }
return is; return is;
} }
inline std::ostream& operator<<(std::ostream& os, const picojson::value& x) inline std::ostream& operator<<(std::ostream& os, const picojson::value& x)
{ {
x.serialize(std::ostream_iterator<char>(os)); x.serialize(std::ostream_iterator<char>(os));
return os; return os;
} }
#ifdef _MSC_VER #ifdef _MSC_VER
#pragma warning(pop) #pragma warning(pop)
#endif #endif
#endif #endif
This source diff could not be displayed because it is too large. You can view the blob instead.
// //
// Tiny glTF loader. // Tiny glTF loader.
// //
// Copyright (c) 2015, Syoyo Fujita. // Copyright (c) 2015, Syoyo Fujita.
// All rights reserved. // All rights reserved.
// (Licensed under 2-clause BSD liecense) // (Licensed under 2-clause BSD liecense)
// //
// Redistribution and use in source and binary forms, with or without // Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met: // modification, are permitted provided that the following conditions are met:
// //
// 1. Redistributions of source code must retain the above copyright notice, // 1. Redistributions of source code must retain the above copyright notice,
// this // this
// list of conditions and the following disclaimer. // list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice, // 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation // this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution. // and/or other materials provided with the distribution.
// //
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND // AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
// FOR // FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL // ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES // DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// //
// //
// Version: // Version:
// - v0.9.2 Support parsing `texture` // - v0.9.2 Support parsing `texture`
// - v0.9.1 Support loading glTF asset from memory // - v0.9.1 Support loading glTF asset from memory
// - v0.9.0 Initial // - v0.9.0 Initial
// //
// Tiny glTF loader is using following libraries: // Tiny glTF loader is using following libraries:
// //
// - picojson: C++ JSON library. // - picojson: C++ JSON library.
// - base64: base64 decode/encode library. // - base64: base64 decode/encode library.
// - stb_image: Image loading library. // - stb_image: Image loading library.
// //
#ifndef TINY_GLTF_LOADER_H #ifndef TINY_GLTF_LOADER_H
#define TINY_GLTF_LOADER_H #define TINY_GLTF_LOADER_H
#include <string> #include <string>
#include <vector> #include <vector>
#include <map> #include <map>
namespace tinygltf { namespace tinygltf {
#define TINYGLTF_MODE_POINTS (0) #define TINYGLTF_MODE_POINTS (0)
#define TINYGLTF_MODE_LINE (1) #define TINYGLTF_MODE_LINE (1)
#define TINYGLTF_MODE_LINE_LOOP (2) #define TINYGLTF_MODE_LINE_LOOP (2)
#define TINYGLTF_MODE_TRIANGLES (4) #define TINYGLTF_MODE_TRIANGLES (4)
#define TINYGLTF_MODE_TRIANGLE_STRIP (5) #define TINYGLTF_MODE_TRIANGLE_STRIP (5)
#define TINYGLTF_MODE_TRIANGLE_FAN (6) #define TINYGLTF_MODE_TRIANGLE_FAN (6)
#define TINYGLTF_COMPONENT_TYPE_BYTE (5120) #define TINYGLTF_COMPONENT_TYPE_BYTE (5120)
#define TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE (5121) #define TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE (5121)
#define TINYGLTF_COMPONENT_TYPE_SHORT (5122) #define TINYGLTF_COMPONENT_TYPE_SHORT (5122)
#define TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT (5123) #define TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT (5123)
#define TINYGLTF_COMPONENT_TYPE_INT (5124) #define TINYGLTF_COMPONENT_TYPE_INT (5124)
#define TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT (5125) #define TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT (5125)
#define TINYGLTF_COMPONENT_TYPE_FLOAT (5126) #define TINYGLTF_COMPONENT_TYPE_FLOAT (5126)
#define TINYGLTF_COMPONENT_TYPE_DOUBLE (5127) #define TINYGLTF_COMPONENT_TYPE_DOUBLE (5127)
#define TINYGLTF_TYPE_VEC2 (2) #define TINYGLTF_TYPE_VEC2 (2)
#define TINYGLTF_TYPE_VEC3 (3) #define TINYGLTF_TYPE_VEC3 (3)
#define TINYGLTF_TYPE_VEC4 (4) #define TINYGLTF_TYPE_VEC4 (4)
#define TINYGLTF_TYPE_MAT2 (32 + 2) #define TINYGLTF_TYPE_MAT2 (32 + 2)
#define TINYGLTF_TYPE_MAT3 (32 + 3) #define TINYGLTF_TYPE_MAT3 (32 + 3)
#define TINYGLTF_TYPE_MAT4 (32 + 4) #define TINYGLTF_TYPE_MAT4 (32 + 4)
#define TINYGLTF_TYPE_SCALAR (64 + 1) #define TINYGLTF_TYPE_SCALAR (64 + 1)
#define TINYGLTF_TYPE_VECTOR (64 + 4) #define TINYGLTF_TYPE_VECTOR (64 + 4)
#define TINYGLTF_TYPE_MATRIX (64 + 16) #define TINYGLTF_TYPE_MATRIX (64 + 16)
#define TINYGLTF_IMAGE_FORMAT_JPEG (0) #define TINYGLTF_IMAGE_FORMAT_JPEG (0)
#define TINYGLTF_IMAGE_FORMAT_PNG (1) #define TINYGLTF_IMAGE_FORMAT_PNG (1)
#define TINYGLTF_IMAGE_FORMAT_BMP (2) #define TINYGLTF_IMAGE_FORMAT_BMP (2)
#define TINYGLTF_IMAGE_FORMAT_GIF (3) #define TINYGLTF_IMAGE_FORMAT_GIF (3)
#define TINYGLTF_TEXTURE_FORMAT_RGBA (6408) #define TINYGLTF_TEXTURE_FORMAT_RGBA (6408)
#define TINYGLTF_TEXTURE_TARGET_TEXTURE2D (3553) #define TINYGLTF_TEXTURE_TARGET_TEXTURE2D (3553)
#define TINYGLTF_TEXTURE_TYPE_UNSIGNED_BYTE (5121) #define TINYGLTF_TEXTURE_TYPE_UNSIGNED_BYTE (5121)
#define TINYGLTF_TARGET_ARRAY_BUFFER (34962) #define TINYGLTF_TARGET_ARRAY_BUFFER (34962)
#define TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER (34963) #define TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER (34963)
typedef struct PARAMETER { typedef struct PARAMETER {
std::string stringValue; std::string stringValue;
std::vector<double> numberArray; std::vector<double> numberArray;
} Parameter; } Parameter;
typedef std::map<std::string, Parameter> ParameterMap; typedef std::map<std::string, Parameter> ParameterMap;
typedef struct IMAGE { typedef struct IMAGE {
std::string name; std::string name;
int width; int width;
int height; int height;
int component; int component;
std::vector<unsigned char> image; std::vector<unsigned char> image;
} Image; } Image;
typedef struct TEXTURE { typedef struct TEXTURE {
int format; int format;
int internalFormat; int internalFormat;
std::string sampler; // Required std::string sampler; // Required
std::string source; // Required std::string source; // Required
int target; int target;
int type; int type;
std::string name; std::string name;
} Texture; } Texture;
typedef struct MATERIAL { typedef struct MATERIAL {
std::string name; std::string name;
std::string technique; std::string technique;
ParameterMap values; ParameterMap values;
} Material; } Material;
typedef struct BUFFERVIEW { typedef struct BUFFERVIEW {
std::string name; std::string name;
std::string buffer; // Required std::string buffer; // Required
size_t byteOffset; // Required size_t byteOffset; // Required
size_t byteLength; // default: 0 size_t byteLength; // default: 0
int target; int target;
} BufferView; } BufferView;
typedef struct ACCESSOR { typedef struct ACCESSOR {
std::string bufferView; std::string bufferView;
std::string name; std::string name;
size_t byteOffset; size_t byteOffset;
size_t byteStride; size_t byteStride;
int componentType; // One of TINYGLTF_COMPONENT_TYPE_*** int componentType; // One of TINYGLTF_COMPONENT_TYPE_***
size_t count; size_t count;
int type; // One of TINYGLTF_TYPE_*** int type; // One of TINYGLTF_TYPE_***
std::vector<double> minValues; // Optional std::vector<double> minValues; // Optional
std::vector<double> maxValues; // Optional std::vector<double> maxValues; // Optional
} Accessor; } Accessor;
class Camera { class Camera {
public: public:
Camera() {} Camera() {}
~Camera() {} ~Camera() {}
std::string name; std::string name;
bool isOrthographic; // false = perspective. bool isOrthographic; // false = perspective.
// Some common properties. // Some common properties.
float aspectRatio; float aspectRatio;
float yFov; float yFov;
float zFar; float zFar;
float zNear; float zNear;
}; };
typedef struct PRIMITIVE { typedef struct PRIMITIVE {
std::map<std::string, std::string> attributes; // A dictionary object of std::map<std::string, std::string> attributes; // A dictionary object of
// strings, where each string // strings, where each string
// is the ID of the accessor // is the ID of the accessor
// containing an attribute. // containing an attribute.
std::string material; // The ID of the material to apply to this primitive std::string material; // The ID of the material to apply to this primitive
// when rendering. // when rendering.
std::string indices; // The ID of the accessor that contains the indices. std::string indices; // The ID of the accessor that contains the indices.
int mode; // one of TINYGLTF_MODE_*** int mode; // one of TINYGLTF_MODE_***
} Primitive; } Primitive;
typedef struct MESH { typedef struct MESH {
std::string name; std::string name;
std::vector<Primitive> primitives; std::vector<Primitive> primitives;
} Mesh; } Mesh;
class Node { class Node {
public: public:
Node() {} Node() {}
~Node() {} ~Node() {}
std::string camera; // camera object referenced by this node. std::string camera; // camera object referenced by this node.
std::string name; std::string name;
std::vector<std::string> children; std::vector<std::string> children;
std::vector<double> rotation; // length must be 0 or 4 std::vector<double> rotation; // length must be 0 or 4
std::vector<double> scale; // length must be 0 or 3 std::vector<double> scale; // length must be 0 or 3
std::vector<double> translation; // length must be 0 or 3 std::vector<double> translation; // length must be 0 or 3
std::vector<double> matrix; // length must be 0 or 16 std::vector<double> matrix; // length must be 0 or 16
std::vector<std::string> meshes; std::vector<std::string> meshes;
}; };
typedef struct BUFFER { typedef struct BUFFER {
std::string name; std::string name;
std::vector<unsigned char> data; std::vector<unsigned char> data;
} Buffer; } Buffer;
typedef struct ASSET { typedef struct ASSET {
std::string generator; std::string generator;
std::string version; std::string version;
std::string profile_api; std::string profile_api;
std::string profile_version; std::string profile_version;
bool premultipliedAlpha; bool premultipliedAlpha;
} Asset; } Asset;
class Scene { class Scene {
public: public:
Scene() {} Scene() {}
~Scene() {} ~Scene() {}
std::map<std::string, Accessor> accessors; std::map<std::string, Accessor> accessors;
std::map<std::string, Buffer> buffers; std::map<std::string, Buffer> buffers;
std::map<std::string, BufferView> bufferViews; std::map<std::string, BufferView> bufferViews;
std::map<std::string, Material> materials; std::map<std::string, Material> materials;
std::map<std::string, Mesh> meshes; std::map<std::string, Mesh> meshes;
std::map<std::string, Node> nodes; std::map<std::string, Node> nodes;
std::map<std::string, Texture> textures; std::map<std::string, Texture> textures;
std::map<std::string, Image> images; std::map<std::string, Image> images;
std::map<std::string, std::vector<std::string> > scenes; // list of nodes std::map<std::string, std::vector<std::string> > scenes; // list of nodes
std::string defaultScene; std::string defaultScene;
Asset asset; Asset asset;
}; };
class TinyGLTFLoader { class TinyGLTFLoader {
public: public:
TinyGLTFLoader(){}; TinyGLTFLoader(){};
~TinyGLTFLoader(){}; ~TinyGLTFLoader(){};
/// Loads glTF asset from a file. /// Loads glTF asset from a file.
/// Returns false and set error string to `err` if there's an error. /// Returns false and set error string to `err` if there's an error.
bool LoadFromFile(Scene &scene, std::string &err, bool LoadFromFile(Scene &scene, std::string &err,
const std::string &filename); const std::string &filename);
/// Loads glTF asset from string(memory). /// Loads glTF asset from string(memory).
/// `length` = strlen(str); /// `length` = strlen(str);
/// Returns false and set error string to `err` if there's an error. /// Returns false and set error string to `err` if there's an error.
bool LoadFromString(Scene &scene, std::string &err, const char *str, bool LoadFromString(Scene &scene, std::string &err, const char *str,
const unsigned int length, const std::string &baseDir); const unsigned int length, const std::string &baseDir);
}; };
} // namespace tinygltf } // namespace tinygltf
#ifdef TINYGLTF_LOADER_IMPLEMENTATION #ifdef TINYGLTF_LOADER_IMPLEMENTATION
#include <sstream> #include <sstream>
#include <fstream> #include <fstream>
#include <cassert> #include <cassert>
#include "picojson.h" #include "picojson.h"
#include "stb_image.h" #include "stb_image.h"
#ifdef _WIN32 #ifdef _WIN32
#include <Windows.h> #include <Windows.h>
#else #else
#include <wordexp.h> #include <wordexp.h>
#endif #endif
using namespace tinygltf; using namespace tinygltf;
namespace { namespace {
bool FileExists(const std::string &abs_filename) { bool FileExists(const std::string &abs_filename) {
bool ret; bool ret;
FILE *fp = fopen(abs_filename.c_str(), "rb"); FILE *fp = fopen(abs_filename.c_str(), "rb");
if (fp) { if (fp) {
ret = true; ret = true;
fclose(fp); fclose(fp);
} else { } else {
ret = false; ret = false;
} }
return ret; return ret;
} }
std::string ExpandFilePath(const std::string &filepath) { std::string ExpandFilePath(const std::string &filepath) {
#ifdef _WIN32 #ifdef _WIN32
uint32 len = ExpandEnvironmentStringsA(filepath.c_str(), NULL, 0); uint32 len = ExpandEnvironmentStringsA(filepath.c_str(), NULL, 0);
char *str = new char[len]; char *str = new char[len];
ExpandEnvironmentStringsA(filepath.c_str(), str, len); ExpandEnvironmentStringsA(filepath.c_str(), str, len);
std::string s(str); std::string s(str);
delete[] str; delete[] str;
return s; return s;
#else #else
#if TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR #if TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR
// no expansion // no expansion
std::string s = filepath; std::string s = filepath;
#else #else
std::string s; std::string s;
wordexp_t p; wordexp_t p;
if (filepath.empty()) { if (filepath.empty()) {
return ""; return "";
} }
// char** w; // char** w;
int ret = wordexp(filepath.c_str(), &p, 0); int ret = wordexp(filepath.c_str(), &p, 0);
if (ret) { if (ret) {
// err // err
s = filepath; s = filepath;
return s; return s;
} }
// Use first element only. // Use first element only.
if (p.we_wordv) { if (p.we_wordv) {
s = std::string(p.we_wordv[0]); s = std::string(p.we_wordv[0]);
wordfree(&p); wordfree(&p);
} else { } else {
s = filepath; s = filepath;
} }
#endif #endif
return s; return s;
#endif #endif
} }
std::string JoinPath(const std::string &path0, const std::string &path1) { std::string JoinPath(const std::string &path0, const std::string &path1) {
if (path0.empty()) { if (path0.empty()) {
return path1; return path1;
} else { } else {
// check '/' // check '/'
char lastChar = *path0.rbegin(); char lastChar = *path0.rbegin();
if (lastChar != '/') { if (lastChar != '/') {
return path0 + std::string("/") + path1; return path0 + std::string("/") + path1;
} else { } else {
return path0 + path1; return path0 + path1;
} }
} }
} }
std::string FindFile(const std::vector<std::string> &paths, std::string FindFile(const std::vector<std::string> &paths,
const std::string &filepath) { const std::string &filepath) {
for (size_t i = 0; i < paths.size(); i++) { for (size_t i = 0; i < paths.size(); i++) {
std::string absPath = ExpandFilePath(JoinPath(paths[i], filepath)); std::string absPath = ExpandFilePath(JoinPath(paths[i], filepath));
if (FileExists(absPath)) { if (FileExists(absPath)) {
return absPath; return absPath;
} }
} }
return std::string(); return std::string();
} }
// std::string GetFilePathExtension(const std::string& FileName) // std::string GetFilePathExtension(const std::string& FileName)
//{ //{
// if(FileName.find_last_of(".") != std::string::npos) // if(FileName.find_last_of(".") != std::string::npos)
// return FileName.substr(FileName.find_last_of(".")+1); // return FileName.substr(FileName.find_last_of(".")+1);
// return ""; // return "";
//} //}
std::string GetBaseDir(const std::string &filepath) { std::string GetBaseDir(const std::string &filepath) {
if (filepath.find_last_of("/\\") != std::string::npos) if (filepath.find_last_of("/\\") != std::string::npos)
return filepath.substr(0, filepath.find_last_of("/\\")); return filepath.substr(0, filepath.find_last_of("/\\"));
return ""; return "";
} }
// std::string base64_encode(unsigned char const* , unsigned int len); // std::string base64_encode(unsigned char const* , unsigned int len);
std::string base64_decode(std::string const &s); std::string base64_decode(std::string const &s);
/* /*
base64.cpp and base64.h base64.cpp and base64.h
Copyright (C) 2004-2008 René Nyffenegger Copyright (C) 2004-2008 René Nyffenegger
This source code is provided 'as-is', without any express or implied This source code is provided 'as-is', without any express or implied
warranty. In no event will the author be held liable for any damages warranty. In no event will the author be held liable for any damages
arising from the use of this software. arising from the use of this software.
Permission is granted to anyone to use this software for any purpose, Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions: freely, subject to the following restrictions:
1. The origin of this source code must not be misrepresented; you must not 1. The origin of this source code must not be misrepresented; you must not
claim that you wrote the original source code. If you use this source code claim that you wrote the original source code. If you use this source code
in a product, an acknowledgment in the product documentation would be in a product, an acknowledgment in the product documentation would be
appreciated but is not required. appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be 2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original source code. misrepresented as being the original source code.
3. This notice may not be removed or altered from any source distribution. 3. This notice may not be removed or altered from any source distribution.
René Nyffenegger rene.nyffenegger@adp-gmbh.ch René Nyffenegger rene.nyffenegger@adp-gmbh.ch
*/ */
//#include "base64.h" //#include "base64.h"
//#include <iostream> //#include <iostream>
static const std::string base64_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" static const std::string base64_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz" "abcdefghijklmnopqrstuvwxyz"
"0123456789+/"; "0123456789+/";
static inline bool is_base64(unsigned char c) { static inline bool is_base64(unsigned char c) {
return (isalnum(c) || (c == '+') || (c == '/')); return (isalnum(c) || (c == '+') || (c == '/'));
} }
#if 0 #if 0
std::string base64_encode(unsigned char const* bytes_to_encode, unsigned int in_len) { std::string base64_encode(unsigned char const* bytes_to_encode, unsigned int in_len) {
std::string ret; std::string ret;
int i = 0; int i = 0;
int j = 0; int j = 0;
unsigned char char_array_3[3]; unsigned char char_array_3[3];
unsigned char char_array_4[4]; unsigned char char_array_4[4];
while (in_len--) { while (in_len--) {
char_array_3[i++] = *(bytes_to_encode++); char_array_3[i++] = *(bytes_to_encode++);
if (i == 3) { if (i == 3) {
char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4); char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
char_array_4[3] = char_array_3[2] & 0x3f; char_array_4[3] = char_array_3[2] & 0x3f;
for(i = 0; (i <4) ; i++) for(i = 0; (i <4) ; i++)
ret += base64_chars[char_array_4[i]]; ret += base64_chars[char_array_4[i]];
i = 0; i = 0;
} }
} }
if (i) if (i)
{ {
for(j = i; j < 3; j++) for(j = i; j < 3; j++)
char_array_3[j] = '\0'; char_array_3[j] = '\0';
char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4); char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
char_array_4[3] = char_array_3[2] & 0x3f; char_array_4[3] = char_array_3[2] & 0x3f;
for (j = 0; (j < i + 1); j++) for (j = 0; (j < i + 1); j++)
ret += base64_chars[char_array_4[j]]; ret += base64_chars[char_array_4[j]];
while((i++ < 3)) while((i++ < 3))
ret += '='; ret += '=';
} }
return ret; return ret;
} }
#endif #endif
std::string base64_decode(std::string const &encoded_string) { std::string base64_decode(std::string const &encoded_string) {
int in_len = encoded_string.size(); int in_len = encoded_string.size();
int i = 0; int i = 0;
int j = 0; int j = 0;
int in_ = 0; int in_ = 0;
unsigned char char_array_4[4], char_array_3[3]; unsigned char char_array_4[4], char_array_3[3];
std::string ret; std::string ret;
while (in_len-- && (encoded_string[in_] != '=') && while (in_len-- && (encoded_string[in_] != '=') &&
is_base64(encoded_string[in_])) { is_base64(encoded_string[in_])) {
char_array_4[i++] = encoded_string[in_]; char_array_4[i++] = encoded_string[in_];
in_++; in_++;
if (i == 4) { if (i == 4) {
for (i = 0; i < 4; i++) for (i = 0; i < 4; i++)
char_array_4[i] = base64_chars.find(char_array_4[i]); char_array_4[i] = base64_chars.find(char_array_4[i]);
char_array_3[0] = char_array_3[0] =
(char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
char_array_3[1] = char_array_3[1] =
((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
for (i = 0; (i < 3); i++) for (i = 0; (i < 3); i++)
ret += char_array_3[i]; ret += char_array_3[i];
i = 0; i = 0;
} }
} }
if (i) { if (i) {
for (j = i; j < 4; j++) for (j = i; j < 4; j++)
char_array_4[j] = 0; char_array_4[j] = 0;
for (j = 0; j < 4; j++) for (j = 0; j < 4; j++)
char_array_4[j] = base64_chars.find(char_array_4[j]); char_array_4[j] = base64_chars.find(char_array_4[j]);
char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
char_array_3[1] = char_array_3[1] =
((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
for (j = 0; (j < i - 1); j++) for (j = 0; (j < i - 1); j++)
ret += char_array_3[j]; ret += char_array_3[j];
} }
return ret; return ret;
} }
bool LoadExternalFile(std::vector<unsigned char> &out, std::string &err, bool LoadExternalFile(std::vector<unsigned char> &out, std::string &err,
const std::string &filename, const std::string &basedir, const std::string &filename, const std::string &basedir,
size_t reqBytes, bool checkSize) { size_t reqBytes, bool checkSize) {
out.clear(); out.clear();
std::vector<std::string> paths; std::vector<std::string> paths;
paths.push_back(basedir); paths.push_back(basedir);
paths.push_back("."); paths.push_back(".");
std::string filepath = FindFile(paths, filename); std::string filepath = FindFile(paths, filename);
if (filepath.empty()) { if (filepath.empty()) {
err += "File not found : " + filename; err += "File not found : " + filename;
return false; return false;
} }
std::ifstream f(filepath.c_str(), std::ifstream::binary); std::ifstream f(filepath.c_str(), std::ifstream::binary);
if (!f) { if (!f) {
err += "File open error : " + filepath; err += "File open error : " + filepath;
return false; return false;
} }
f.seekg(0, f.end); f.seekg(0, f.end);
size_t sz = f.tellg(); size_t sz = f.tellg();
std::vector<unsigned char> buf(sz); std::vector<unsigned char> buf(sz);
f.seekg(0, f.beg); f.seekg(0, f.beg);
f.read(reinterpret_cast<char *>(&buf.at(0)), sz); f.read(reinterpret_cast<char *>(&buf.at(0)), sz);
f.close(); f.close();
if (checkSize) { if (checkSize) {
if (reqBytes == sz) { if (reqBytes == sz) {
out.swap(buf); out.swap(buf);
return true; return true;
} else { } else {
std::stringstream ss; std::stringstream ss;
ss << "File size mismatch : " << filepath << ", requestedBytes " ss << "File size mismatch : " << filepath << ", requestedBytes "
<< reqBytes << ", but got " << sz << std::endl; << reqBytes << ", but got " << sz << std::endl;
err += ss.str(); err += ss.str();
return false; return false;
} }
} }
out.swap(buf); out.swap(buf);
return true; return true;
} }
bool IsDataURI(const std::string &in) { bool IsDataURI(const std::string &in) {
std::string header = "data:application/octet-stream;base64,"; std::string header = "data:application/octet-stream;base64,";
if (in.find(header) == 0) { if (in.find(header) == 0) {
return true; return true;
} }
header = "data:image/png;base64,"; header = "data:image/png;base64,";
if (in.find(header) == 0) { if (in.find(header) == 0) {
return true; return true;
} }
header = "data:image/jpeg;base64,"; header = "data:image/jpeg;base64,";
if (in.find(header) == 0) { if (in.find(header) == 0) {
return true; return true;
} }
return false; return false;
} }
bool DecodeDataURI(std::vector<unsigned char> &out, const std::string &in, bool DecodeDataURI(std::vector<unsigned char> &out, const std::string &in,
size_t reqBytes, bool checkSize) { size_t reqBytes, bool checkSize) {
std::string header = "data:application/octet-stream;base64,"; std::string header = "data:application/octet-stream;base64,";
std::string data; std::string data;
if (in.find(header) == 0) { if (in.find(header) == 0) {
data = base64_decode(in.substr(header.size())); // cut mime string. data = base64_decode(in.substr(header.size())); // cut mime string.
} }
if (data.empty()) { if (data.empty()) {
header = "data:image/jpeg;base64,"; header = "data:image/jpeg;base64,";
if (in.find(header) == 0) { if (in.find(header) == 0) {
data = base64_decode(in.substr(header.size())); // cut mime string. data = base64_decode(in.substr(header.size())); // cut mime string.
} }
} }
if (data.empty()) { if (data.empty()) {
header = "data:image/png;base64,"; header = "data:image/png;base64,";
if (in.find(header) == 0) { if (in.find(header) == 0) {
data = base64_decode(in.substr(header.size())); // cut mime string. data = base64_decode(in.substr(header.size())); // cut mime string.
} }
} }
if (data.empty()) { if (data.empty()) {
return false; return false;
} }
if (checkSize) { if (checkSize) {
if (data.size() != reqBytes) { if (data.size() != reqBytes) {
return false; return false;
} }
out.resize(reqBytes); out.resize(reqBytes);
} else { } else {
out.resize(data.size()); out.resize(data.size());
} }
std::copy(data.begin(), data.end(), out.begin()); std::copy(data.begin(), data.end(), out.begin());
return true; return true;
return false; return false;
} }
bool ParseBooleanProperty(bool &ret, std::string &err, bool ParseBooleanProperty(bool &ret, std::string &err,
const picojson::object &o, const picojson::object &o,
const std::string &property, bool required) { const std::string &property, bool required) {
picojson::object::const_iterator it = o.find(property); picojson::object::const_iterator it = o.find(property);
if (it == o.end()) { if (it == o.end()) {
if (required) { if (required) {
err += "'" + property + "' property is missing.\n"; err += "'" + property + "' property is missing.\n";
} }
return false; return false;
} }
if (!it->second.is<bool>()) { if (!it->second.is<bool>()) {
if (required) { if (required) {
err += "'" + property + "' property is not a bool type.\n"; err += "'" + property + "' property is not a bool type.\n";
} }
return false; return false;
} }
ret = it->second.get<bool>(); ret = it->second.get<bool>();
return true; return true;
} }
bool ParseNumberProperty(double &ret, std::string &err, bool ParseNumberProperty(double &ret, std::string &err,
const picojson::object &o, const std::string &property, const picojson::object &o, const std::string &property,
bool required) { bool required) {
picojson::object::const_iterator it = o.find(property); picojson::object::const_iterator it = o.find(property);
if (it == o.end()) { if (it == o.end()) {
if (required) { if (required) {
err += "'" + property + "' property is missing.\n"; err += "'" + property + "' property is missing.\n";
} }
return false; return false;
} }
if (!it->second.is<double>()) { if (!it->second.is<double>()) {
if (required) { if (required) {
err += "'" + property + "' property is not a number type.\n"; err += "'" + property + "' property is not a number type.\n";
} }
return false; return false;
} }
ret = it->second.get<double>(); ret = it->second.get<double>();
return true; return true;
} }
bool ParseNumberArrayProperty(std::vector<double> &ret, std::string &err, bool ParseNumberArrayProperty(std::vector<double> &ret, std::string &err,
const picojson::object &o, const picojson::object &o,
const std::string &property, bool required) { const std::string &property, bool required) {
picojson::object::const_iterator it = o.find(property); picojson::object::const_iterator it = o.find(property);
if (it == o.end()) { if (it == o.end()) {
if (required) { if (required) {
err += "'" + property + "' property is missing.\n"; err += "'" + property + "' property is missing.\n";
} }
return false; return false;
} }
if (!it->second.is<picojson::array>()) { if (!it->second.is<picojson::array>()) {
if (required) { if (required) {
err += "'" + property + "' property is not an array.\n"; err += "'" + property + "' property is not an array.\n";
} }
return false; return false;
} }
ret.clear(); ret.clear();
const picojson::array &arr = it->second.get<picojson::array>(); const picojson::array &arr = it->second.get<picojson::array>();
for (size_t i = 0; i < arr.size(); i++) { for (size_t i = 0; i < arr.size(); i++) {
if (!arr[i].is<double>()) { if (!arr[i].is<double>()) {
if (required) { if (required) {
err += "'" + property + "' property is not a number.\n"; err += "'" + property + "' property is not a number.\n";
} }
return false; return false;
} }
ret.push_back(arr[i].get<double>()); ret.push_back(arr[i].get<double>());
} }
return true; return true;
} }
bool ParseStringProperty(std::string &ret, std::string &err, bool ParseStringProperty(std::string &ret, std::string &err,
const picojson::object &o, const std::string &property, const picojson::object &o, const std::string &property,
bool required) { bool required) {
picojson::object::const_iterator it = o.find(property); picojson::object::const_iterator it = o.find(property);
if (it == o.end()) { if (it == o.end()) {
if (required) { if (required) {
err += "'" + property + "' property is missing.\n"; err += "'" + property + "' property is missing.\n";
} }
return false; return false;
} }
if (!it->second.is<std::string>()) { if (!it->second.is<std::string>()) {
if (required) { if (required) {
err += "'" + property + "' property is not a string type.\n"; err += "'" + property + "' property is not a string type.\n";
} }
return false; return false;
} }
ret = it->second.get<std::string>(); ret = it->second.get<std::string>();
return true; return true;
} }
bool ParseStringArrayProperty(std::vector<std::string> &ret, std::string &err, bool ParseStringArrayProperty(std::vector<std::string> &ret, std::string &err,
const picojson::object &o, const picojson::object &o,
const std::string &property, bool required) { const std::string &property, bool required) {
picojson::object::const_iterator it = o.find(property); picojson::object::const_iterator it = o.find(property);
if (it == o.end()) { if (it == o.end()) {
if (required) { if (required) {
err += "'" + property + "' property is missing.\n"; err += "'" + property + "' property is missing.\n";
} }
return false; return false;
} }
if (!it->second.is<picojson::array>()) { if (!it->second.is<picojson::array>()) {
if (required) { if (required) {
err += "'" + property + "' property is not an array.\n"; err += "'" + property + "' property is not an array.\n";
} }
return false; return false;
} }
ret.clear(); ret.clear();
const picojson::array &arr = it->second.get<picojson::array>(); const picojson::array &arr = it->second.get<picojson::array>();
for (size_t i = 0; i < arr.size(); i++) { for (size_t i = 0; i < arr.size(); i++) {
if (!arr[i].is<std::string>()) { if (!arr[i].is<std::string>()) {
if (required) { if (required) {
err += "'" + property + "' property is not a string.\n"; err += "'" + property + "' property is not a string.\n";
} }
return false; return false;
} }
ret.push_back(arr[i].get<std::string>()); ret.push_back(arr[i].get<std::string>());
} }
return true; return true;
} }
bool ParseAsset(Asset &asset, std::string &err, const picojson::object &o) { bool ParseAsset(Asset &asset, std::string &err, const picojson::object &o) {
ParseStringProperty(asset.generator, err, o, "generator", false); ParseStringProperty(asset.generator, err, o, "generator", false);
ParseBooleanProperty(asset.premultipliedAlpha, err, o, "premultipliedAlpha", ParseBooleanProperty(asset.premultipliedAlpha, err, o, "premultipliedAlpha",
false); false);
ParseStringProperty(asset.version, err, o, "version", false); ParseStringProperty(asset.version, err, o, "version", false);
picojson::object::const_iterator profile = o.find("profile"); picojson::object::const_iterator profile = o.find("profile");
if (profile != o.end()) { if (profile != o.end()) {
const picojson::value &v = profile->second; const picojson::value &v = profile->second;
if (v.contains("api") & v.get("api").is<std::string>()) { if (v.contains("api") & v.get("api").is<std::string>()) {
asset.profile_api = v.get("api").get<std::string>(); asset.profile_api = v.get("api").get<std::string>();
} }
if (v.contains("version") & v.get("version").is<std::string>()) { if (v.contains("version") & v.get("version").is<std::string>()) {
asset.profile_version = v.get("version").get<std::string>(); asset.profile_version = v.get("version").get<std::string>();
} }
} }
return true; return true;
} }
bool ParseImage(Image &image, std::string &err, const picojson::object &o, bool ParseImage(Image &image, std::string &err, const picojson::object &o,
const std::string &basedir) { const std::string &basedir) {
std::string uri; std::string uri;
if (!ParseStringProperty(uri, err, o, "uri", true)) { if (!ParseStringProperty(uri, err, o, "uri", true)) {
return false; return false;
} }
ParseStringProperty(image.name, err, o, "name", false); ParseStringProperty(image.name, err, o, "name", false);
std::vector<unsigned char> img; std::vector<unsigned char> img;
if (IsDataURI(uri)) { if (IsDataURI(uri)) {
if (!DecodeDataURI(img, uri, 0, false)) { if (!DecodeDataURI(img, uri, 0, false)) {
err += "Failed to decode 'uri'.\n"; err += "Failed to decode 'uri'.\n";
return false; return false;
} }
} else { } else {
// Assume external file // Assume external file
if (!LoadExternalFile(img, err, uri, basedir, 0, false)) { if (!LoadExternalFile(img, err, uri, basedir, 0, false)) {
err += "Failed to load external 'uri'.\n"; err += "Failed to load external 'uri'.\n";
return false; return false;
} }
if (img.empty()) { if (img.empty()) {
err += "File is empty.\n"; err += "File is empty.\n";
return false; return false;
} }
} }
int w, h, comp; int w, h, comp;
unsigned char *data = unsigned char *data =
stbi_load_from_memory(&img.at(0), img.size(), &w, &h, &comp, 0); stbi_load_from_memory(&img.at(0), img.size(), &w, &h, &comp, 0);
if (!data) { if (!data) {
err += "Unknown image format.\n"; err += "Unknown image format.\n";
return false; return false;
} }
if (w < 1 || h < 1) { if (w < 1 || h < 1) {
err += "Unknown image format.\n"; err += "Unknown image format.\n";
return false; return false;
} }
image.width = w; image.width = w;
image.height = h; image.height = h;
image.component = comp; image.component = comp;
image.image.resize(w * h * comp); image.image.resize(w * h * comp);
std::copy(data, data + w * h * comp, image.image.begin()); std::copy(data, data + w * h * comp, image.image.begin());
return true; return true;
} }
bool ParseTexture(Texture &texture, std::string &err, const picojson::object &o, bool ParseTexture(Texture &texture, std::string &err, const picojson::object &o,
const std::string &basedir) { const std::string &basedir) {
if (!ParseStringProperty(texture.sampler, err, o, "sampler", true)) { if (!ParseStringProperty(texture.sampler, err, o, "sampler", true)) {
return false; return false;
} }
if (!ParseStringProperty(texture.source, err, o, "source", true)) { if (!ParseStringProperty(texture.source, err, o, "source", true)) {
return false; return false;
} }
ParseStringProperty(texture.name, err, o, "name", false); ParseStringProperty(texture.name, err, o, "name", false);
double format = TINYGLTF_TEXTURE_FORMAT_RGBA; double format = TINYGLTF_TEXTURE_FORMAT_RGBA;
ParseNumberProperty(format, err, o, "format", false); ParseNumberProperty(format, err, o, "format", false);
double internalFormat = TINYGLTF_TEXTURE_FORMAT_RGBA; double internalFormat = TINYGLTF_TEXTURE_FORMAT_RGBA;
ParseNumberProperty(internalFormat, err, o, "internalFormat", false); ParseNumberProperty(internalFormat, err, o, "internalFormat", false);
double target = TINYGLTF_TEXTURE_TARGET_TEXTURE2D; double target = TINYGLTF_TEXTURE_TARGET_TEXTURE2D;
ParseNumberProperty(target, err, o, "target", false); ParseNumberProperty(target, err, o, "target", false);
double type = TINYGLTF_TEXTURE_TYPE_UNSIGNED_BYTE; double type = TINYGLTF_TEXTURE_TYPE_UNSIGNED_BYTE;
ParseNumberProperty(type, err, o, "type", false); ParseNumberProperty(type, err, o, "type", false);
texture.format = static_cast<int>(format); texture.format = static_cast<int>(format);
texture.internalFormat = static_cast<int>(internalFormat); texture.internalFormat = static_cast<int>(internalFormat);
texture.target = static_cast<int>(target); texture.target = static_cast<int>(target);
texture.type = static_cast<int>(type); texture.type = static_cast<int>(type);
return true; return true;
} }
bool ParseBuffer(Buffer &buffer, std::string &err, const picojson::object &o, bool ParseBuffer(Buffer &buffer, std::string &err, const picojson::object &o,
const std::string &basedir) { const std::string &basedir) {
double byteLength; double byteLength;
if (!ParseNumberProperty(byteLength, err, o, "byteLength", true)) { if (!ParseNumberProperty(byteLength, err, o, "byteLength", true)) {
return false; return false;
} }
std::string uri; std::string uri;
if (!ParseStringProperty(uri, err, o, "uri", true)) { if (!ParseStringProperty(uri, err, o, "uri", true)) {
return false; return false;
} }
picojson::object::const_iterator type = o.find("type"); picojson::object::const_iterator type = o.find("type");
if (type != o.end()) { if (type != o.end()) {
if (type->second.is<std::string>()) { if (type->second.is<std::string>()) {
const std::string &ty = (type->second).get<std::string>(); const std::string &ty = (type->second).get<std::string>();
if (ty.compare("arraybuffer") == 0) { if (ty.compare("arraybuffer") == 0) {
// buffer.type = "arraybuffer"; // buffer.type = "arraybuffer";
} }
} }
} }
size_t bytes = static_cast<size_t>(byteLength); size_t bytes = static_cast<size_t>(byteLength);
if (IsDataURI(uri)) { if (IsDataURI(uri)) {
if (!DecodeDataURI(buffer.data, uri, bytes, true)) { if (!DecodeDataURI(buffer.data, uri, bytes, true)) {
err += "Failed to decode 'uri'.\n"; err += "Failed to decode 'uri'.\n";
return false; return false;
} }
} else { } else {
// Assume external .bin file. // Assume external .bin file.
if (!LoadExternalFile(buffer.data, err, uri, basedir, bytes, true)) { if (!LoadExternalFile(buffer.data, err, uri, basedir, bytes, true)) {
return false; return false;
} }
} }
ParseStringProperty(buffer.name, err, o, "name", false); ParseStringProperty(buffer.name, err, o, "name", false);
return true; return true;
} }
bool ParseBufferView(BufferView &bufferView, std::string &err, bool ParseBufferView(BufferView &bufferView, std::string &err,
const picojson::object &o) { const picojson::object &o) {
std::string buffer; std::string buffer;
if (!ParseStringProperty(buffer, err, o, "buffer", true)) { if (!ParseStringProperty(buffer, err, o, "buffer", true)) {
return false; return false;
} }
double byteOffset; double byteOffset;
if (!ParseNumberProperty(byteOffset, err, o, "byteOffset", true)) { if (!ParseNumberProperty(byteOffset, err, o, "byteOffset", true)) {
return false; return false;
} }
double byteLength = 0.0; double byteLength = 0.0;
ParseNumberProperty(byteLength, err, o, "byteLength", false); ParseNumberProperty(byteLength, err, o, "byteLength", false);
double target = 0.0; double target = 0.0;
ParseNumberProperty(target, err, o, "target", false); ParseNumberProperty(target, err, o, "target", false);
int targetValue = static_cast<int>(target); int targetValue = static_cast<int>(target);
if ((targetValue == TINYGLTF_TARGET_ARRAY_BUFFER) || if ((targetValue == TINYGLTF_TARGET_ARRAY_BUFFER) ||
(targetValue == TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER)) { (targetValue == TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER)) {
// OK // OK
} else { } else {
targetValue = 0; targetValue = 0;
} }
bufferView.target = targetValue; bufferView.target = targetValue;
ParseStringProperty(bufferView.name, err, o, "name", false); ParseStringProperty(bufferView.name, err, o, "name", false);
bufferView.buffer = buffer; bufferView.buffer = buffer;
bufferView.byteOffset = static_cast<size_t>(byteOffset); bufferView.byteOffset = static_cast<size_t>(byteOffset);
bufferView.byteLength = static_cast<size_t>(byteLength); bufferView.byteLength = static_cast<size_t>(byteLength);
return true; return true;
} }
bool ParseAccessor(Accessor &accessor, std::string &err, bool ParseAccessor(Accessor &accessor, std::string &err,
const picojson::object &o) { const picojson::object &o) {
std::string bufferView; std::string bufferView;
if (!ParseStringProperty(bufferView, err, o, "bufferView", true)) { if (!ParseStringProperty(bufferView, err, o, "bufferView", true)) {
return false; return false;
} }
double byteOffset; double byteOffset;
if (!ParseNumberProperty(byteOffset, err, o, "byteOffset", true)) { if (!ParseNumberProperty(byteOffset, err, o, "byteOffset", true)) {
return false; return false;
} }
double componentType; double componentType;
if (!ParseNumberProperty(componentType, err, o, "componentType", true)) { if (!ParseNumberProperty(componentType, err, o, "componentType", true)) {
return false; return false;
} }
double count = 0.0; double count = 0.0;
if (!ParseNumberProperty(count, err, o, "count", true)) { if (!ParseNumberProperty(count, err, o, "count", true)) {
return false; return false;
} }
std::string type; std::string type;
if (!ParseStringProperty(type, err, o, "type", true)) { if (!ParseStringProperty(type, err, o, "type", true)) {
return false; return false;
} }
if (type.compare("SCALAR") == 0) { if (type.compare("SCALAR") == 0) {
accessor.type = TINYGLTF_TYPE_SCALAR; accessor.type = TINYGLTF_TYPE_SCALAR;
} else if (type.compare("VEC2") == 0) { } else if (type.compare("VEC2") == 0) {
accessor.type = TINYGLTF_TYPE_VEC2; accessor.type = TINYGLTF_TYPE_VEC2;
} else if (type.compare("VEC3") == 0) { } else if (type.compare("VEC3") == 0) {
accessor.type = TINYGLTF_TYPE_VEC3; accessor.type = TINYGLTF_TYPE_VEC3;
} else if (type.compare("VEC4") == 0) { } else if (type.compare("VEC4") == 0) {
accessor.type = TINYGLTF_TYPE_VEC4; accessor.type = TINYGLTF_TYPE_VEC4;
} else if (type.compare("MAT2") == 0) { } else if (type.compare("MAT2") == 0) {
accessor.type = TINYGLTF_TYPE_MAT2; accessor.type = TINYGLTF_TYPE_MAT2;
} else if (type.compare("MAT3") == 0) { } else if (type.compare("MAT3") == 0) {
accessor.type = TINYGLTF_TYPE_MAT3; accessor.type = TINYGLTF_TYPE_MAT3;
} else if (type.compare("MAT4") == 0) { } else if (type.compare("MAT4") == 0) {
accessor.type = TINYGLTF_TYPE_MAT4; accessor.type = TINYGLTF_TYPE_MAT4;
} else { } else {
std::stringstream ss; std::stringstream ss;
ss << "Unsupported `type` for accessor object. Got \"" << type << "\"\n"; ss << "Unsupported `type` for accessor object. Got \"" << type << "\"\n";
err += ss.str(); err += ss.str();
return false; return false;
} }
double byteStride = 0.0; double byteStride = 0.0;
ParseNumberProperty(byteStride, err, o, "byteStride", false); ParseNumberProperty(byteStride, err, o, "byteStride", false);
ParseStringProperty(accessor.name, err, o, "name", false); ParseStringProperty(accessor.name, err, o, "name", false);
accessor.minValues.clear(); accessor.minValues.clear();
accessor.maxValues.clear(); accessor.maxValues.clear();
ParseNumberArrayProperty(accessor.minValues, err, o, "min", false); ParseNumberArrayProperty(accessor.minValues, err, o, "min", false);
ParseNumberArrayProperty(accessor.maxValues, err, o, "max", false); ParseNumberArrayProperty(accessor.maxValues, err, o, "max", false);
accessor.count = static_cast<size_t>(count); accessor.count = static_cast<size_t>(count);
accessor.bufferView = bufferView; accessor.bufferView = bufferView;
accessor.byteOffset = static_cast<size_t>(byteOffset); accessor.byteOffset = static_cast<size_t>(byteOffset);
accessor.byteStride = static_cast<size_t>(byteStride); accessor.byteStride = static_cast<size_t>(byteStride);
{ {
int comp = static_cast<size_t>(componentType); int comp = static_cast<size_t>(componentType);
if (comp >= TINYGLTF_COMPONENT_TYPE_BYTE && if (comp >= TINYGLTF_COMPONENT_TYPE_BYTE &&
comp <= TINYGLTF_COMPONENT_TYPE_DOUBLE) { comp <= TINYGLTF_COMPONENT_TYPE_DOUBLE) {
// OK // OK
accessor.componentType = comp; accessor.componentType = comp;
} else { } else {
std::stringstream ss; std::stringstream ss;
ss << "Invalid `componentType` in accessor. Got " << comp << "\n"; ss << "Invalid `componentType` in accessor. Got " << comp << "\n";
err += ss.str(); err += ss.str();
return false; return false;
} }
} }
return true; return true;
} }
bool ParsePrimitive(Primitive &primitive, std::string &err, bool ParsePrimitive(Primitive &primitive, std::string &err,
const picojson::object &o) { const picojson::object &o) {
if (!ParseStringProperty(primitive.material, err, o, "material", true)) { if (!ParseStringProperty(primitive.material, err, o, "material", true)) {
return false; return false;
} }
double mode = static_cast<double>(TINYGLTF_MODE_TRIANGLES); double mode = static_cast<double>(TINYGLTF_MODE_TRIANGLES);
ParseNumberProperty(mode, err, o, "mode", false); ParseNumberProperty(mode, err, o, "mode", false);
int primMode = static_cast<int>(mode); int primMode = static_cast<int>(mode);
if (primMode != TINYGLTF_MODE_TRIANGLES) { if (primMode != TINYGLTF_MODE_TRIANGLES) {
err += "Currently TinyGLTFLoader doesn not support primitive mode other \n" err += "Currently TinyGLTFLoader doesn not support primitive mode other \n"
"than TRIANGLES.\n"; "than TRIANGLES.\n";
return false; return false;
} }
primitive.mode = primMode; primitive.mode = primMode;
primitive.indices = ""; primitive.indices = "";
ParseStringProperty(primitive.indices, err, o, "indices", false); ParseStringProperty(primitive.indices, err, o, "indices", false);
primitive.attributes.clear(); primitive.attributes.clear();
picojson::object::const_iterator attribsObject = o.find("attributes"); picojson::object::const_iterator attribsObject = o.find("attributes");
if ((attribsObject != o.end()) && if ((attribsObject != o.end()) &&
(attribsObject->second).is<picojson::object>()) { (attribsObject->second).is<picojson::object>()) {
const picojson::object &attribs = const picojson::object &attribs =
(attribsObject->second).get<picojson::object>(); (attribsObject->second).get<picojson::object>();
picojson::object::const_iterator it(attribs.begin()); picojson::object::const_iterator it(attribs.begin());
picojson::object::const_iterator itEnd(attribs.end()); picojson::object::const_iterator itEnd(attribs.end());
for (; it != itEnd; it++) { for (; it != itEnd; it++) {
const std::string &name = it->first; const std::string &name = it->first;
if (!(it->second).is<std::string>()) { if (!(it->second).is<std::string>()) {
err += "attribute expects string value.\n"; err += "attribute expects string value.\n";
return false; return false;
} }
const std::string &value = (it->second).get<std::string>(); const std::string &value = (it->second).get<std::string>();
primitive.attributes[name] = value; primitive.attributes[name] = value;
} }
} }
return true; return true;
} }
bool ParseMesh(Mesh &mesh, std::string &err, const picojson::object &o) { bool ParseMesh(Mesh &mesh, std::string &err, const picojson::object &o) {
ParseStringProperty(mesh.name, err, o, "name", false); ParseStringProperty(mesh.name, err, o, "name", false);
mesh.primitives.clear(); mesh.primitives.clear();
picojson::object::const_iterator primObject = o.find("primitives"); picojson::object::const_iterator primObject = o.find("primitives");
if ((primObject != o.end()) && (primObject->second).is<picojson::array>()) { if ((primObject != o.end()) && (primObject->second).is<picojson::array>()) {
const picojson::array &primArray = const picojson::array &primArray =
(primObject->second).get<picojson::array>(); (primObject->second).get<picojson::array>();
for (size_t i = 0; i < primArray.size(); i++) { for (size_t i = 0; i < primArray.size(); i++) {
Primitive primitive; Primitive primitive;
ParsePrimitive(primitive, err, primArray[i].get<picojson::object>()); ParsePrimitive(primitive, err, primArray[i].get<picojson::object>());
mesh.primitives.push_back(primitive); mesh.primitives.push_back(primitive);
} }
} }
return true; return true;
} }
bool ParseNode(Node &node, std::string &err, const picojson::object &o) { bool ParseNode(Node &node, std::string &err, const picojson::object &o) {
ParseStringProperty(node.name, err, o, "name", false); ParseStringProperty(node.name, err, o, "name", false);
ParseNumberArrayProperty(node.rotation, err, o, "rotation", false); ParseNumberArrayProperty(node.rotation, err, o, "rotation", false);
ParseNumberArrayProperty(node.scale, err, o, "scale", false); ParseNumberArrayProperty(node.scale, err, o, "scale", false);
ParseNumberArrayProperty(node.translation, err, o, "translation", false); ParseNumberArrayProperty(node.translation, err, o, "translation", false);
ParseNumberArrayProperty(node.matrix, err, o, "matrix", false); ParseNumberArrayProperty(node.matrix, err, o, "matrix", false);
ParseStringArrayProperty(node.meshes, err, o, "meshes", false); ParseStringArrayProperty(node.meshes, err, o, "meshes", false);
node.children.clear(); node.children.clear();
picojson::object::const_iterator childrenObject = o.find("children"); picojson::object::const_iterator childrenObject = o.find("children");
if ((childrenObject != o.end()) && if ((childrenObject != o.end()) &&
(childrenObject->second).is<picojson::array>()) { (childrenObject->second).is<picojson::array>()) {
const picojson::array &childrenArray = const picojson::array &childrenArray =
(childrenObject->second).get<picojson::array>(); (childrenObject->second).get<picojson::array>();
for (size_t i = 0; i < childrenArray.size(); i++) { for (size_t i = 0; i < childrenArray.size(); i++) {
Node node; Node node;
if (!childrenArray[i].is<std::string>()) { if (!childrenArray[i].is<std::string>()) {
err += "Invalid `children` array.\n"; err += "Invalid `children` array.\n";
return false; return false;
} }
const std::string &childrenNode = childrenArray[i].get<std::string>(); const std::string &childrenNode = childrenArray[i].get<std::string>();
node.children.push_back(childrenNode); node.children.push_back(childrenNode);
} }
} }
return true; return true;
} }
bool ParseMaterial(Material &material, std::string &err, bool ParseMaterial(Material &material, std::string &err,
const picojson::object &o) { const picojson::object &o) {
ParseStringProperty(material.name, err, o, "name", false); ParseStringProperty(material.name, err, o, "name", false);
ParseStringProperty(material.technique, err, o, "technique", false); ParseStringProperty(material.technique, err, o, "technique", false);
material.values.clear(); material.values.clear();
picojson::object::const_iterator valuesIt = o.find("values"); picojson::object::const_iterator valuesIt = o.find("values");
if ((valuesIt != o.end()) && (valuesIt->second).is<picojson::object>()) { if ((valuesIt != o.end()) && (valuesIt->second).is<picojson::object>()) {
const picojson::object &valuesObject = const picojson::object &valuesObject =
(valuesIt->second).get<picojson::object>(); (valuesIt->second).get<picojson::object>();
picojson::object::const_iterator it(valuesObject.begin()); picojson::object::const_iterator it(valuesObject.begin());
picojson::object::const_iterator itEnd(valuesObject.end()); picojson::object::const_iterator itEnd(valuesObject.end());
for (; it != itEnd; it++) { for (; it != itEnd; it++) {
// Assume number values. // Assume number values.
Parameter param; Parameter param;
if (ParseStringProperty(param.stringValue, err, valuesObject, it->first, if (ParseStringProperty(param.stringValue, err, valuesObject, it->first,
false)) { false)) {
// Found string property. // Found string property.
} else if (!ParseNumberArrayProperty(param.numberArray, err, valuesObject, } else if (!ParseNumberArrayProperty(param.numberArray, err, valuesObject,
it->first, false)) { it->first, false)) {
// Fallback to numer property. // Fallback to numer property.
double value; double value;
if (ParseNumberProperty(value, err, valuesObject, it->first, false)) { if (ParseNumberProperty(value, err, valuesObject, it->first, false)) {
param.numberArray.push_back(value); param.numberArray.push_back(value);
} }
} }
material.values[it->first] = param; material.values[it->first] = param;
} }
} }
return true; return true;
} }
} }
bool TinyGLTFLoader::LoadFromString(Scene &scene, std::string &err, bool TinyGLTFLoader::LoadFromString(Scene &scene, std::string &err,
const char *str, unsigned int length, const char *str, unsigned int length,
const std::string &baseDir) { const std::string &baseDir) {
picojson::value v; picojson::value v;
std::string perr = picojson::parse(v, str, str + length); std::string perr = picojson::parse(v, str, str + length);
if (!perr.empty()) { if (!perr.empty()) {
err = perr; err = perr;
return false; return false;
} }
if (v.contains("scene") && v.get("scene").is<std::string>()) { if (v.contains("scene") && v.get("scene").is<std::string>()) {
// OK // OK
} else { } else {
err += "\"scene\" object not found in .gltf\n"; err += "\"scene\" object not found in .gltf\n";
return false; return false;
} }
if (v.contains("scenes") && v.get("scenes").is<picojson::object>()) { if (v.contains("scenes") && v.get("scenes").is<picojson::object>()) {
// OK // OK
} else { } else {
err += "\"scenes\" object not found in .gltf\n"; err += "\"scenes\" object not found in .gltf\n";
return false; return false;
} }
if (v.contains("nodes") && v.get("nodes").is<picojson::object>()) { if (v.contains("nodes") && v.get("nodes").is<picojson::object>()) {
// OK // OK
} else { } else {
err += "\"nodes\" object not found in .gltf\n"; err += "\"nodes\" object not found in .gltf\n";
return false; return false;
} }
if (v.contains("accessors") && v.get("accessors").is<picojson::object>()) { if (v.contains("accessors") && v.get("accessors").is<picojson::object>()) {
// OK // OK
} else { } else {
err += "\"accessors\" object not found in .gltf\n"; err += "\"accessors\" object not found in .gltf\n";
return false; return false;
} }
if (v.contains("buffers") && v.get("buffers").is<picojson::object>()) { if (v.contains("buffers") && v.get("buffers").is<picojson::object>()) {
// OK // OK
} else { } else {
err += "\"buffers\" object not found in .gltf\n"; err += "\"buffers\" object not found in .gltf\n";
return false; return false;
} }
if (v.contains("bufferViews") && if (v.contains("bufferViews") &&
v.get("bufferViews").is<picojson::object>()) { v.get("bufferViews").is<picojson::object>()) {
// OK // OK
} else { } else {
err += "\"bufferViews\" object not found in .gltf\n"; err += "\"bufferViews\" object not found in .gltf\n";
return false; return false;
} }
scene.buffers.clear(); scene.buffers.clear();
scene.bufferViews.clear(); scene.bufferViews.clear();
scene.accessors.clear(); scene.accessors.clear();
scene.meshes.clear(); scene.meshes.clear();
scene.nodes.clear(); scene.nodes.clear();
scene.defaultScene = ""; scene.defaultScene = "";
// 0. Parse Asset // 0. Parse Asset
if (v.contains("asset") && v.get("asset").is<picojson::object>()) { if (v.contains("asset") && v.get("asset").is<picojson::object>()) {
const picojson::object &root = v.get("asset").get<picojson::object>(); const picojson::object &root = v.get("asset").get<picojson::object>();
ParseAsset(scene.asset, err, root); ParseAsset(scene.asset, err, root);
} }
// 1. Parse Buffer // 1. Parse Buffer
if (v.contains("buffers") && v.get("buffers").is<picojson::object>()) { if (v.contains("buffers") && v.get("buffers").is<picojson::object>()) {
const picojson::object &root = v.get("buffers").get<picojson::object>(); const picojson::object &root = v.get("buffers").get<picojson::object>();
picojson::object::const_iterator it(root.begin()); picojson::object::const_iterator it(root.begin());
picojson::object::const_iterator itEnd(root.end()); picojson::object::const_iterator itEnd(root.end());
for (; it != itEnd; it++) { for (; it != itEnd; it++) {
Buffer buffer; Buffer buffer;
if (!ParseBuffer(buffer, err, (it->second).get<picojson::object>(), if (!ParseBuffer(buffer, err, (it->second).get<picojson::object>(),
baseDir)) { baseDir)) {
return false; return false;
} }
scene.buffers[it->first] = buffer; scene.buffers[it->first] = buffer;
} }
} }
// 2. Parse BufferView // 2. Parse BufferView
if (v.contains("bufferViews") && if (v.contains("bufferViews") &&
v.get("bufferViews").is<picojson::object>()) { v.get("bufferViews").is<picojson::object>()) {
const picojson::object &root = v.get("bufferViews").get<picojson::object>(); const picojson::object &root = v.get("bufferViews").get<picojson::object>();
picojson::object::const_iterator it(root.begin()); picojson::object::const_iterator it(root.begin());
picojson::object::const_iterator itEnd(root.end()); picojson::object::const_iterator itEnd(root.end());
for (; it != itEnd; it++) { for (; it != itEnd; it++) {
BufferView bufferView; BufferView bufferView;
if (!ParseBufferView(bufferView, err, if (!ParseBufferView(bufferView, err,
(it->second).get<picojson::object>())) { (it->second).get<picojson::object>())) {
return false; return false;
} }
scene.bufferViews[it->first] = bufferView; scene.bufferViews[it->first] = bufferView;
} }
} }
// 3. Parse Accessor // 3. Parse Accessor
if (v.contains("accessors") && v.get("accessors").is<picojson::object>()) { if (v.contains("accessors") && v.get("accessors").is<picojson::object>()) {
const picojson::object &root = v.get("accessors").get<picojson::object>(); const picojson::object &root = v.get("accessors").get<picojson::object>();
picojson::object::const_iterator it(root.begin()); picojson::object::const_iterator it(root.begin());
picojson::object::const_iterator itEnd(root.end()); picojson::object::const_iterator itEnd(root.end());
for (; it != itEnd; it++) { for (; it != itEnd; it++) {
Accessor accessor; Accessor accessor;
if (!ParseAccessor(accessor, err, (it->second).get<picojson::object>())) { if (!ParseAccessor(accessor, err, (it->second).get<picojson::object>())) {
return false; return false;
} }
scene.accessors[it->first] = accessor; scene.accessors[it->first] = accessor;
} }
} }
// 4. Parse Mesh // 4. Parse Mesh
if (v.contains("meshes") && v.get("meshes").is<picojson::object>()) { if (v.contains("meshes") && v.get("meshes").is<picojson::object>()) {
const picojson::object &root = v.get("meshes").get<picojson::object>(); const picojson::object &root = v.get("meshes").get<picojson::object>();
picojson::object::const_iterator it(root.begin()); picojson::object::const_iterator it(root.begin());
picojson::object::const_iterator itEnd(root.end()); picojson::object::const_iterator itEnd(root.end());
for (; it != itEnd; it++) { for (; it != itEnd; it++) {
Mesh mesh; Mesh mesh;
if (!ParseMesh(mesh, err, (it->second).get<picojson::object>())) { if (!ParseMesh(mesh, err, (it->second).get<picojson::object>())) {
return false; return false;
} }
scene.meshes[it->first] = mesh; scene.meshes[it->first] = mesh;
} }
} }
// 5. Parse Node // 5. Parse Node
if (v.contains("nodes") && v.get("nodes").is<picojson::object>()) { if (v.contains("nodes") && v.get("nodes").is<picojson::object>()) {
const picojson::object &root = v.get("nodes").get<picojson::object>(); const picojson::object &root = v.get("nodes").get<picojson::object>();
picojson::object::const_iterator it(root.begin()); picojson::object::const_iterator it(root.begin());
picojson::object::const_iterator itEnd(root.end()); picojson::object::const_iterator itEnd(root.end());
for (; it != itEnd; it++) { for (; it != itEnd; it++) {
Node node; Node node;
if (!ParseNode(node, err, (it->second).get<picojson::object>())) { if (!ParseNode(node, err, (it->second).get<picojson::object>())) {
return false; return false;
} }
scene.nodes[it->first] = node; scene.nodes[it->first] = node;
} }
} }
// 6. Parse scenes. // 6. Parse scenes.
if (v.contains("scenes") && v.get("scenes").is<picojson::object>()) { if (v.contains("scenes") && v.get("scenes").is<picojson::object>()) {
const picojson::object &root = v.get("scenes").get<picojson::object>(); const picojson::object &root = v.get("scenes").get<picojson::object>();
picojson::object::const_iterator it(root.begin()); picojson::object::const_iterator it(root.begin());
picojson::object::const_iterator itEnd(root.end()); picojson::object::const_iterator itEnd(root.end());
for (; it != itEnd; it++) { for (; it != itEnd; it++) {
const picojson::object &o = (it->second).get<picojson::object>(); const picojson::object &o = (it->second).get<picojson::object>();
std::vector<std::string> nodes; std::vector<std::string> nodes;
if (!ParseStringArrayProperty(nodes, err, o, "nodes", false)) { if (!ParseStringArrayProperty(nodes, err, o, "nodes", false)) {
return false; return false;
} }
scene.scenes[it->first] = nodes; scene.scenes[it->first] = nodes;
} }
} }
// 7. Parse default scenes. // 7. Parse default scenes.
if (v.contains("scene") && v.get("scene").is<std::string>()) { if (v.contains("scene") && v.get("scene").is<std::string>()) {
const std::string defaultScene = v.get("scene").get<std::string>(); const std::string defaultScene = v.get("scene").get<std::string>();
scene.defaultScene = defaultScene; scene.defaultScene = defaultScene;
} }
// 8. Parse Material // 8. Parse Material
if (v.contains("materials") && v.get("materials").is<picojson::object>()) { if (v.contains("materials") && v.get("materials").is<picojson::object>()) {
const picojson::object &root = v.get("materials").get<picojson::object>(); const picojson::object &root = v.get("materials").get<picojson::object>();
picojson::object::const_iterator it(root.begin()); picojson::object::const_iterator it(root.begin());
picojson::object::const_iterator itEnd(root.end()); picojson::object::const_iterator itEnd(root.end());
for (; it != itEnd; it++) { for (; it != itEnd; it++) {
Material material; Material material;
if (!ParseMaterial(material, err, (it->second).get<picojson::object>())) { if (!ParseMaterial(material, err, (it->second).get<picojson::object>())) {
return false; return false;
} }
scene.materials[it->first] = material; scene.materials[it->first] = material;
} }
} }
// 9. Parse Image // 9. Parse Image
if (v.contains("images") && v.get("images").is<picojson::object>()) { if (v.contains("images") && v.get("images").is<picojson::object>()) {
const picojson::object &root = v.get("images").get<picojson::object>(); const picojson::object &root = v.get("images").get<picojson::object>();
picojson::object::const_iterator it(root.begin()); picojson::object::const_iterator it(root.begin());
picojson::object::const_iterator itEnd(root.end()); picojson::object::const_iterator itEnd(root.end());
for (; it != itEnd; it++) { for (; it != itEnd; it++) {
Image image; Image image;
if (!ParseImage(image, err, (it->second).get<picojson::object>(), if (!ParseImage(image, err, (it->second).get<picojson::object>(),
baseDir)) { baseDir)) {
return false; return false;
} }
scene.images[it->first] = image; scene.images[it->first] = image;
} }
} }
// 9. Parse Texture // 9. Parse Texture
if (v.contains("textures") && v.get("textures").is<picojson::object>()) { if (v.contains("textures") && v.get("textures").is<picojson::object>()) {
const picojson::object &root = v.get("textures").get<picojson::object>(); const picojson::object &root = v.get("textures").get<picojson::object>();
picojson::object::const_iterator it(root.begin()); picojson::object::const_iterator it(root.begin());
picojson::object::const_iterator itEnd(root.end()); picojson::object::const_iterator itEnd(root.end());
for (; it != itEnd; it++) { for (; it != itEnd; it++) {
Texture texture; Texture texture;
if (!ParseTexture(texture, err, (it->second).get<picojson::object>(), if (!ParseTexture(texture, err, (it->second).get<picojson::object>(),
baseDir)) { baseDir)) {
return false; return false;
} }
scene.textures[it->first] = texture; scene.textures[it->first] = texture;
} }
} }
return true; return true;
} }
bool TinyGLTFLoader::LoadFromFile(Scene &scene, std::string &err, bool TinyGLTFLoader::LoadFromFile(Scene &scene, std::string &err,
const std::string &filename) { const std::string &filename) {
std::stringstream ss; std::stringstream ss;
std::ifstream f(filename.c_str()); std::ifstream f(filename.c_str());
if (!f) { if (!f) {
ss << "Failed to open file: " << filename << std::endl; ss << "Failed to open file: " << filename << std::endl;
err = ss.str(); err = ss.str();
return false; return false;
} }
f.seekg(0, f.end); f.seekg(0, f.end);
size_t sz = f.tellg(); size_t sz = f.tellg();
std::vector<char> buf(sz); std::vector<char> buf(sz);
f.seekg(0, f.beg); f.seekg(0, f.beg);
f.read(&buf.at(0), sz); f.read(&buf.at(0), sz);
f.close(); f.close();
std::string basedir = GetBaseDir(filename); std::string basedir = GetBaseDir(filename);
bool ret = LoadFromString(scene, err, &buf.at(0), buf.size(), basedir); bool ret = LoadFromString(scene, err, &buf.at(0), buf.size(), basedir);
return ret; return ret;
} }
#endif // TINYGLTF_LOADER_IMPLEMENTATION #endif // TINYGLTF_LOADER_IMPLEMENTATION
#endif // TINY_GLTF_LOADER_H #endif // TINY_GLTF_LOADER_H
Copyright (c) 2016, Syoyo Fujita Copyright (c) 2016, Syoyo Fujita
All rights reserved. All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment