// Copyright (c) 2012-2022 Wojciech Figat. All rights reserved. #pragma once #if COMPILE_WITH_MODEL_TOOL #include "Engine/Core/Config.h" #include "Engine/Content/Assets/ModelBase.h" #if USE_EDITOR #include "Engine/Serialization/ISerializable.h" #include "Engine/Graphics/Models/ModelData.h" #include "Engine/Graphics/Models/SkeletonData.h" #include "Engine/Animations/AnimationData.h" class JsonWriter; /// /// The model file import data types (used as flags). /// enum class ImportDataTypes : int32 { /// /// Imports materials and meshes. /// Geometry = 1 << 0, /// /// Imports the skeleton bones hierarchy. /// Skeleton = 1 << 1, /// /// Imports the animations. /// Animations = 1 << 2, /// /// Imports the scene nodes hierarchy. /// Nodes = 1 << 3, /// /// Imports the materials. /// Materials = 1 << 4, /// /// Imports the textures. /// Textures = 1 << 5, }; DECLARE_ENUM_OPERATORS(ImportDataTypes); /// /// Imported model data container. Represents unified model source file data (meshes, animations, skeleton, materials). /// class ImportedModelData { public: struct LOD { Array Meshes; BoundingBox GetBox() const; }; struct Node { /// /// The parent node index. The root node uses value -1. /// int32 ParentIndex; /// /// The local transformation of the node, relative to the parent node. /// Transform LocalTransform; /// /// The name of this node. /// String Name; }; public: /// /// The import data types types. /// ImportDataTypes Types; /// /// The textures slots. /// Array Textures; /// /// The material slots. /// Array Materials; /// /// The level of details data. /// Array LODs; /// /// The skeleton data. /// SkeletonData Skeleton; /// /// The scene nodes. /// Array Nodes; /// /// The node animations. /// AnimationData Animation; public: /// /// Initializes a new instance of the class. /// /// The types. ImportedModelData(ImportDataTypes types) { Types = types; } /// /// Finalizes an instance of the class. /// ~ImportedModelData() { // Ensure to cleanup data for (int32 i = 0; i < LODs.Count(); i++) LODs[i].Meshes.ClearDelete(); } }; #endif struct ModelSDFHeader { Float3 LocalToUVWMul; float WorldUnitsPerVoxel; Float3 LocalToUVWAdd; float MaxDistance; Float3 LocalBoundsMin; int32 MipLevels; Float3 LocalBoundsMax; int32 Width; int32 Height; int32 Depth; PixelFormat Format; float ResolutionScale; int32 LOD; ModelSDFHeader() = default; ModelSDFHeader(const ModelBase::SDFData& sdf, const struct GPUTextureDescription& desc); }; struct ModelSDFMip { int32 MipIndex; uint32 RowPitch; uint32 SlicePitch; ModelSDFMip() = default; ModelSDFMip(int32 mipIndex, uint32 rowPitch, uint32 slicePitch); ModelSDFMip(int32 mipIndex, const TextureMipData& mip); }; /// /// Models data importing and processing utility. /// class FLAXENGINE_API ModelTool { public: // Optional: inputModel or modelData // Optional: outputSDF or null, outputStream or null static bool GenerateModelSDF(class Model* inputModel, class ModelData* modelData, float resolutionScale, int32 lodIndex, ModelBase::SDFData* outputSDF, class MemoryWriteStream* outputStream, const StringView& assetName, float backfacesThreshold = 0.6f); #if USE_EDITOR public: /// /// Declares the imported data type. /// DECLARE_ENUM_EX_3(ModelType, int32, 0, Model, SkinnedModel, Animation); /// /// Declares the imported animation clip duration. /// DECLARE_ENUM_EX_2(AnimationDuration, int32, 0, Imported, Custom); /// /// Importing model options /// struct Options : public ISerializable { ModelType Type = ModelType::Model; // Geometry bool CalculateNormals = false; float SmoothingNormalsAngle = 175.0f; bool FlipNormals = false; float SmoothingTangentsAngle = 45.0f; bool CalculateTangents = false; bool OptimizeMeshes = true; bool MergeMeshes = true; bool ImportLODs = true; bool ImportVertexColors = true; bool ImportBlendShapes = false; ModelLightmapUVsSource LightmapUVsSource = ModelLightmapUVsSource::Disable; String CollisionMeshesPrefix; // Transform float Scale = 1.0f; Quaternion Rotation = Quaternion::Identity; Float3 Translation = Float3::Zero; bool CenterGeometry = false; // Animation AnimationDuration Duration = AnimationDuration::Imported; Float2 FramesRange = Float2::Zero; float DefaultFrameRate = 0.0f; float SamplingRate = 0.0f; bool SkipEmptyCurves = true; bool OptimizeKeyframes = true; bool EnableRootMotion = false; String RootNodeName; // Level Of Detail bool GenerateLODs = false; int32 BaseLOD = 0; int32 LODCount = 4; float TriangleReduction = 0.5f; // Materials bool ImportMaterials = true; bool ImportTextures = true; bool RestoreMaterialsOnReimport = true; // SDF bool GenerateSDF = false; float SDFResolution = 1.0f; // Splitting bool SplitObjects = false; int32 ObjectIndex = -1; // Runtime data for objects splitting during import (used internally) void* SplitContext = nullptr; Function OnSplitImport; public: // [ISerializable] void Serialize(SerializeStream& stream, const void* otherObj) override; void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) override; }; public: /// /// Imports the model source file data. /// /// The file path. /// The output data. /// The import options. /// The error message container. /// True if fails, otherwise false. static bool ImportData(const String& path, ImportedModelData& data, Options& options, String& errorMsg); /// /// Imports the model. /// /// The file path. /// The output data. /// The import options. /// The error message container. /// The output folder for the additional imported data - optional. Used to auto-import textures and material assets. /// True if fails, otherwise false. static bool ImportModel(const String& path, ModelData& meshData, Options& options, String& errorMsg, const String& autoImportOutput = String::Empty); public: static int32 DetectLodIndex(const String& nodeName); static bool FindTexture(const String& sourcePath, const String& file, String& path); /// /// Gets the local transformations to go from rootIndex to index. /// /// The nodes containing the local transformations. /// The root index. /// The current index. /// The transformation at this index. template static Transform CombineTransformsFromNodeIndices(Array& nodes, int32 rootIndex, int32 index) { if (index == -1 || index == rootIndex) return Transform::Identity; auto result = nodes[index].LocalTransform; if (index != rootIndex) { const auto parentTransform = CombineTransformsFromNodeIndices(nodes, rootIndex, nodes[index].ParentIndex); result = parentTransform.LocalToWorld(result); } return result; } private: #if USE_ASSIMP static bool ImportDataAssimp(const char* path, ImportedModelData& data, Options& options, String& errorMsg); #endif #if USE_AUTODESK_FBX_SDK static bool ImportDataAutodeskFbxSdk(const char* path, ImportedModelData& data, Options& options, String& errorMsg); #endif #if USE_OPEN_FBX static bool ImportDataOpenFBX(const char* path, ImportedModelData& data, Options& options, String& errorMsg); #endif #endif }; #endif