diff --git a/.github/workflows/build_linux.yml b/.github/workflows/build_linux.yml index 3238ce5c2..d00019e0a 100644 --- a/.github/workflows/build_linux.yml +++ b/.github/workflows/build_linux.yml @@ -10,7 +10,7 @@ jobs: steps: - name: Install dependencies run: | - sudo apt-get install libx11-dev libxcursor-dev libxinerama-dev build-essential gettext curl libtool libtool-bin libpulse-dev libasound2-dev libjack-dev portaudio19-dev libcurl4-gnutls-dev + sudo apt-get install libx11-dev libxcursor-dev libxinerama-dev build-essential gettext curl libtool libtool-bin libpulse-dev libasound2-dev libjack-dev portaudio19-dev libcurl4 - name: Checkout repo uses: actions/checkout@v2 - name: Checkout LFS diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 236fa6d67..ada5e5fa5 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -32,3 +32,19 @@ Go check out our [Trello](https://trello.com/b/NQjLXRCP/flax-roadmap). Thank you for taking interest in contributing to Flax! + +## **Common issues** + +Below are some common issues that someone working with the FlaxEngine source code might run into. Hopefully some of those issues will get fixed in the future. If you know how, please contribute! + +* Missing MSVC toolset + * Install it through the Visual Studio Installer +* Building or attaching fails + * Run `GenerateProjectFiles.bat` + * Rebuild `Flax.Build` + * Make sure that there isn't a stray FlaxEngine process running in the background + * First start Flax and then attach the C# debugger + * Configure the C# FlaxEngine project by going into the project properties, then the debug tab and selecting "Start external program" `Flax\FlaxEngine\Binaries\Editor\Win64\Debug\FlaxEditor.exe` + * Then you can also set command line arguments such as `-project "C:\Users\PROFILE\Documents\Flax Projects\FlaxSamples\BasicTemplate"` +* Git LFS + * Push with `git push --no-verify` diff --git a/README.md b/README.md index 7d836ed55..4fddf1adc 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,7 @@ Flax Visual Studio extension provides better programming workflow, C# scripts de * Install Visual Studio Code * Install Mono ([https://www.mono-project.com/download/stable](https://www.mono-project.com/download/stable)) * Install Git with LFS -* Install requried packages: `sudo apt-get install libx11-dev libxcursor-dev libxinerama-dev nuget autoconf libogg-dev automake build-essential gettext cmake python curl libtool libtool-bin libpulse-dev libasound2-dev libjack-dev portaudio19-dev libcurl4-gnutls-dev` +* Install requried packages: `sudo apt-get install libx11-dev libxcursor-dev libxinerama-dev nuget autoconf libogg-dev automake build-essential gettext cmake python curl libtool libtool-bin libpulse-dev libasound2-dev libjack-dev portaudio19-dev libcurl4` * Install compiler `sudo apt-get install clang lldb lld` (Clang 6 or newer) * Clone repo (with LFS) * Run `./GenerateProjectFiles.sh` diff --git a/Source/Editor/GUI/Docking/DockWindow.cs b/Source/Editor/GUI/Docking/DockWindow.cs index 5e94ef547..1a28b1307 100644 --- a/Source/Editor/GUI/Docking/DockWindow.cs +++ b/Source/Editor/GUI/Docking/DockWindow.cs @@ -59,7 +59,10 @@ namespace FlaxEditor.GUI.Docking /// /// Gets the default window size. /// - public virtual Vector2 DefaultSize => new Vector2(900, 580); + /// + /// Scaled by the DPI, because the window should be large enough for its content on every monitor + /// + public virtual Vector2 DefaultSize => new Vector2(900, 580) * DpiScale; /// /// Gets the serialization typename. diff --git a/Source/Editor/SceneGraph/Actors/SplineNode.cs b/Source/Editor/SceneGraph/Actors/SplineNode.cs index 293e7d030..b4914941a 100644 --- a/Source/Editor/SceneGraph/Actors/SplineNode.cs +++ b/Source/Editor/SceneGraph/Actors/SplineNode.cs @@ -367,6 +367,15 @@ namespace FlaxEditor.SceneGraph.Actors } } + /// + public override bool RayCastSelf(ref RayCastData ray, out float distance, out Vector3 normal) + { + // Select only spline points + normal = Vector3.Up; + distance = float.MaxValue; + return false; + } + /// public override void OnDispose() { diff --git a/Source/Editor/Scripting/ScriptsBuilder.cpp b/Source/Editor/Scripting/ScriptsBuilder.cpp index 1c489b2a9..00a93b66c 100644 --- a/Source/Editor/Scripting/ScriptsBuilder.cpp +++ b/Source/Editor/Scripting/ScriptsBuilder.cpp @@ -341,7 +341,21 @@ void ScriptsBuilder::GetBinariesConfiguration(const Char*& target, const Char*& target = platform = architecture = configuration = nullptr; return; } - target = Editor::Project->EditorTarget.GetText(); + + // Pick game target + if (Editor::Project->EditorTarget.HasChars()) + { + target = Editor::Project->EditorTarget.Get(); + } + else if (Editor::Project->GameTarget.HasChars()) + { + target = Editor::Project->GameTarget.Get(); + } + else + { + target = TEXT(""); + LOG(Error, "Missing editor/game targets in project. Please specify EditorTarget and GameTarget properties in .flaxproj file."); + } #if PLATFORM_WINDOWS platform = TEXT("Windows"); @@ -551,19 +565,13 @@ bool ScriptsBuilderService::Init() } // Verify project - if (project->EditorTarget.IsEmpty() || project->GameTarget.IsEmpty()) + if (project->EditorTarget.IsEmpty()) { - const String& name = project->Name; - String codeName; - for (int32 i = 0; i < name.Length(); i++) - { - Char c = name[i]; - if (StringUtils::IsAlnum(c) && c != ' ' && c != '.') - codeName += c; - } - project->GameTarget = codeName + TEXT("Target"); - project->EditorTarget = codeName + TEXT("EditorTarget"); - LOG(Warning, "Missing EditorTarget property in opened project, using deducted target name {0}", Editor::Project->EditorTarget); + LOG(Warning, "Missing {0} property in opened project", TEXT("EditorTarget")); + } + if (project->GameTarget.IsEmpty()) + { + LOG(Warning, "Missing {0} property in opened project", TEXT("GameTarget")); } // Remove any remaining files from previous Editor run hot-reloads diff --git a/Source/Editor/Windows/Assets/ModelWindow.cs b/Source/Editor/Windows/Assets/ModelWindow.cs index e8a054222..b16a01a73 100644 --- a/Source/Editor/Windows/Assets/ModelWindow.cs +++ b/Source/Editor/Windows/Assets/ModelWindow.cs @@ -399,9 +399,18 @@ namespace FlaxEditor.Windows.Assets ShadowsCastingMode[] shadowsModes = new ShadowsCastingMode[value.Length]; for (int i = 0; i < value.Length; i++) { - materials[i] = value[i].Material; - names[i] = value[i].Name; - shadowsModes[i] = value[i].ShadowsMode; + if (value[i] != null) + { + materials[i] = value[i].Material; + names[i] = value[i].Name; + shadowsModes[i] = value[i].ShadowsMode; + } + else + { + materials[i] = null; + names[i] = "Material " + i; + shadowsModes[i] = ShadowsCastingMode.All; + } } Asset.SetupMaterialSlots(value.Length); diff --git a/Source/Editor/Windows/Assets/SkinnedModelWindow.cs b/Source/Editor/Windows/Assets/SkinnedModelWindow.cs index b002aa0fe..f3d228f45 100644 --- a/Source/Editor/Windows/Assets/SkinnedModelWindow.cs +++ b/Source/Editor/Windows/Assets/SkinnedModelWindow.cs @@ -511,9 +511,18 @@ namespace FlaxEditor.Windows.Assets ShadowsCastingMode[] shadowsModes = new ShadowsCastingMode[value.Length]; for (int i = 0; i < value.Length; i++) { - materials[i] = value[i].Material; - names[i] = value[i].Name; - shadowsModes[i] = value[i].ShadowsMode; + if (value[i] != null) + { + materials[i] = value[i].Material; + names[i] = value[i].Name; + shadowsModes[i] = value[i].ShadowsMode; + } + else + { + materials[i] = null; + names[i] = "Material " + i; + shadowsModes[i] = ShadowsCastingMode.All; + } } Asset.SetupMaterialSlots(value.Length); diff --git a/Source/Editor/Windows/SceneTreeWindow.cs b/Source/Editor/Windows/SceneTreeWindow.cs index 5f6ab1613..e33d658e6 100644 --- a/Source/Editor/Windows/SceneTreeWindow.cs +++ b/Source/Editor/Windows/SceneTreeWindow.cs @@ -209,9 +209,11 @@ namespace FlaxEditor.Windows public override void Draw() { var style = Style.Current; + // Draw overlay string overlayText = null; var state = Editor.StateMachine.CurrentState; + var textWrap = TextWrapping.NoWrap; if (state is LoadingState) { overlayText = "Loading..."; @@ -223,10 +225,11 @@ namespace FlaxEditor.Windows else if (((ContainerControl)_tree.GetChild(0)).ChildrenCount == 0) { overlayText = "No scene\nOpen one from the content window"; + textWrap = TextWrapping.WrapWords; } if (overlayText != null) { - Render2D.DrawText(Style.Current.FontLarge, overlayText, GetClientArea(), style.ForegroundDisabled, TextAlignment.Center, TextAlignment.Center); + Render2D.DrawText(style.FontLarge, overlayText, GetClientArea(), style.ForegroundDisabled, TextAlignment.Center, TextAlignment.Center, textWrap); } base.Draw(); diff --git a/Source/Engine/Physics/Physics.Queries.cpp b/Source/Engine/Physics/Physics.Queries.cpp index 15630e7ff..6cf3c786d 100644 --- a/Source/Engine/Physics/Physics.Queries.cpp +++ b/Source/Engine/Physics/Physics.Queries.cpp @@ -358,6 +358,51 @@ bool Physics::SphereCastAll(const Vector3& center, const float radius, const Vec return true; } +bool Physics::CapsuleCast(const Vector3& center, const float radius, const float height, const Vector3& direction, const Quaternion& rotation, const float maxDistance, uint32 layerMask, bool hitTriggers) +{ + // Prepare data + SCENE_QUERY_SETUP_SWEEP_1(); + const PxTransform pose(C2P(center), C2P(rotation)); + const PxCapsuleGeometry geometry(radius, height * 0.5f); + + // Perform sweep test + return GetScene()->sweep(geometry, pose, C2P(direction), maxDistance, buffer, hitFlags, filterData, GetQueryFilterCallback()); +} + +bool Physics::CapsuleCast(const Vector3& center, const float radius, const float height, const Vector3& direction, RayCastHit& hitInfo, const Quaternion& rotation, const float maxDistance, uint32 layerMask, bool hitTriggers) +{ + // Prepare data + SCENE_QUERY_SETUP_SWEEP_1(); + const PxTransform pose(C2P(center), C2P(rotation)); + const PxCapsuleGeometry geometry(radius, height * 0.5f); + + // Perform sweep test + if (!GetScene()->sweep(geometry, pose, C2P(direction), maxDistance, buffer, hitFlags, filterData, GetQueryFilterCallback())) + return false; + + // Collect results + SCENE_QUERY_COLLECT_SINGLE(); + + return true; +} + +bool Physics::CapsuleCastAll(const Vector3& center, const float radius, const float height, const Vector3& direction, Array& results, const Quaternion& rotation, const float maxDistance, uint32 layerMask, bool hitTriggers) +{ + // Prepare data + SCENE_QUERY_SETUP_SWEEP(); + const PxTransform pose(C2P(center), C2P(rotation)); + const PxCapsuleGeometry geometry(radius, height * 0.5f); + + // Perform sweep test + if (!GetScene()->sweep(geometry, pose, C2P(direction), maxDistance, buffer, hitFlags, filterData, GetQueryFilterCallback())) + return false; + + // Collect results + SCENE_QUERY_COLLECT_ALL(); + + return true; +} + bool Physics::CheckBox(const Vector3& center, const Vector3& halfExtents, const Quaternion& rotation, uint32 layerMask, bool hitTriggers) { // Prepare data @@ -380,6 +425,17 @@ bool Physics::CheckSphere(const Vector3& center, const float radius, uint32 laye return GetScene()->overlap(geometry, pose, buffer, filterData, GetQueryFilterCallback()); } +bool Physics::CheckCapsule(const Vector3& center, const float radius, const float height, uint32 layerMask, bool hitTriggers) +{ + // Prepare data + SCENE_QUERY_SETUP_OVERLAP_1(); + const PxTransform pose(C2P(center)); + const PxCapsuleGeometry geometry(radius, height * 0.5f); + + // Perform overlap test + return GetScene()->overlap(geometry, pose, buffer, filterData, GetQueryFilterCallback()); +} + bool Physics::OverlapBox(const Vector3& center, const Vector3& halfExtents, Array& results, const Quaternion& rotation, uint32 layerMask, bool hitTriggers) { // Prepare data @@ -414,6 +470,23 @@ bool Physics::OverlapSphere(const Vector3& center, const float radius, Array& results, uint32 layerMask, bool hitTriggers) +{ + // Prepare data + SCENE_QUERY_SETUP_OVERLAP(); + const PxTransform pose(C2P(center)); + const PxCapsuleGeometry geometry(radius, height * 0.5f); + + // Perform overlap test + if (!GetScene()->overlap(geometry, pose, buffer, filterData, GetQueryFilterCallback())) + return false; + + // Collect results + SCENE_QUERY_COLLECT_OVERLAP_COLLIDER(); + + return true; +} + bool Physics::OverlapBox(const Vector3& center, const Vector3& halfExtents, Array& results, const Quaternion& rotation, uint32 layerMask, bool hitTriggers) { // Prepare data @@ -447,3 +520,20 @@ bool Physics::OverlapSphere(const Vector3& center, const float radius, Array& results, uint32 layerMask, bool hitTriggers) +{ + // Prepare data + SCENE_QUERY_SETUP_OVERLAP(); + const PxTransform pose(C2P(center)); + const PxCapsuleGeometry geometry(radius, height * 0.5f); + + // Perform overlap test + if (!GetScene()->overlap(geometry, pose, buffer, filterData, GetQueryFilterCallback())) + return false; + + // Collect results + SCENE_QUERY_COLLECT_OVERLAP(); + + return true; +} diff --git a/Source/Engine/Physics/Physics.h b/Source/Engine/Physics/Physics.h index 05b64cb0f..71e172699 100644 --- a/Source/Engine/Physics/Physics.h +++ b/Source/Engine/Physics/Physics.h @@ -276,6 +276,50 @@ public: /// True if sphere hits an matching object, otherwise false. API_FUNCTION() static bool SphereCastAll(const Vector3& center, float radius, const Vector3& direction, API_PARAM(Out) Array& results, float maxDistance = MAX_float, uint32 layerMask = MAX_uint32, bool hitTriggers = true); + /// + /// Performs a sweep test against objects in the scene using a capsule geometry. + /// + /// The box center. + /// The radius of the capsule. + /// The height of the capsule, excluding the top and bottom spheres. + /// The normalized direction in which cast a box. + /// The capsule rotation. + /// The maximum distance the ray should check for collisions. + /// The layer mask used to filter the results. + /// If set to true triggers will be hit, otherwise will skip them. + /// True if capsule hits an matching object, otherwise false. + API_FUNCTION() static bool CapsuleCast(const Vector3& center, float radius, float height, const Vector3& direction, const Quaternion& rotation = Quaternion::Identity, float maxDistance = MAX_float, uint32 layerMask = MAX_uint32, bool hitTriggers = true); + + /// + /// Performs a sweep test against objects in the scene using a capsule geometry. + /// + /// The box center. + /// The radius of the capsule. + /// The height of the capsule, excluding the top and bottom spheres. + /// The normalized direction in which cast a box. + /// The result hit information. Valid only when method returns true. + /// The capsule rotation. + /// The maximum distance the ray should check for collisions. + /// The layer mask used to filter the results. + /// If set to true triggers will be hit, otherwise will skip them. + /// True if capsule hits an matching object, otherwise false. + API_FUNCTION() static bool CapsuleCast(const Vector3& center, float radius, float height, const Vector3& direction, API_PARAM(Out) RayCastHit& hitInfo, const Quaternion& rotation = Quaternion::Identity, float maxDistance = MAX_float, uint32 layerMask = MAX_uint32, bool hitTriggers = true); + + /// + /// Performs a sweep test against objects in the scene using a capsule geometry. + /// + /// The box center. + /// The radius of the capsule. + /// The height of the capsule, excluding the top and bottom spheres. + /// The normalized direction in which cast a box. + /// The result hits. Valid only when method returns true. + /// The capsule rotation. + /// The maximum distance the ray should check for collisions. + /// The layer mask used to filter the results. + /// If set to true triggers will be hit, otherwise will skip them. + /// True if capsule hits an matching object, otherwise false. + API_FUNCTION() static bool CapsuleCastAll(const Vector3& center, float radius, float height, const Vector3& direction, API_PARAM(Out) Array& results, const Quaternion& rotation = Quaternion::Identity, float maxDistance = MAX_float, uint32 layerMask = MAX_uint32, bool hitTriggers = true); + /// /// Checks whether the given box overlaps with other colliders or not. /// @@ -297,6 +341,17 @@ public: /// True if sphere overlaps any matching object, otherwise false. API_FUNCTION() static bool CheckSphere(const Vector3& center, float radius, uint32 layerMask = MAX_uint32, bool hitTriggers = true); + /// + /// Checks whether the given capsule overlaps with other colliders or not. + /// + /// The capsule center. + /// The radius of the capsule. + /// The height of the capsule, excluding the top and bottom spheres. + /// The layer mask used to filter the results. + /// If set to true triggers will be hit, otherwise will skip them. + /// True if capsule overlaps any matching object, otherwise false. + API_FUNCTION() static bool CheckCapsule(const Vector3& center, float radius, float height, uint32 layerMask = MAX_uint32, bool hitTriggers = true); + /// /// Finds all colliders touching or inside of the given box. /// @@ -320,6 +375,18 @@ public: /// True if sphere overlaps any matching object, otherwise false. API_FUNCTION() static bool OverlapSphere(const Vector3& center, float radius, API_PARAM(Out) Array& results, uint32 layerMask = MAX_uint32, bool hitTriggers = true); + /// + /// Finds all colliders touching or inside of the given sphere. + /// + /// The sphere center. + /// The radius of the capsule. + /// The height of the capsule, excluding the top and bottom spheres. + /// The result colliders that overlap with the given sphere. Valid only when method returns true. + /// The layer mask used to filter the results. + /// If set to true triggers will be hit, otherwise will skip them. + /// True if capsule overlaps any matching object, otherwise false. + API_FUNCTION() static bool OverlapCapsule(const Vector3& center, float radius, float height, API_PARAM(Out) Array& results, uint32 layerMask = MAX_uint32, bool hitTriggers = true); + /// /// Finds all colliders touching or inside of the given box. /// @@ -343,6 +410,18 @@ public: /// True if sphere overlaps any matching object, otherwise false. API_FUNCTION() static bool OverlapSphere(const Vector3& center, float radius, API_PARAM(Out) Array& results, uint32 layerMask = MAX_uint32, bool hitTriggers = true); + /// + /// Finds all colliders touching or inside of the given sphere. + /// + /// The capsule center. + /// The radius of the capsule. + /// The height of the capsule, excluding the top and bottom spheres. + /// The result colliders that overlap with the given sphere. Valid only when method returns true. + /// The layer mask used to filter the results. + /// If set to true triggers will be hit, otherwise will skip them. + /// True if capsule overlaps any matching object, otherwise false. + API_FUNCTION() static bool OverlapCapsule(const Vector3& center, float radius, float height, API_PARAM(Out) Array& results, uint32 layerMask = MAX_uint32, bool hitTriggers = true); + public: /// diff --git a/Source/Engine/Platform/Windows/WindowsFileSystem.cpp b/Source/Engine/Platform/Windows/WindowsFileSystem.cpp index 1816c9552..6cf467976 100644 --- a/Source/Engine/Platform/Windows/WindowsFileSystem.cpp +++ b/Source/Engine/Platform/Windows/WindowsFileSystem.cpp @@ -260,7 +260,7 @@ bool WindowsFileSystem::ShowSaveFileDialog(Window* parentWindow, const StringVie of.lpstrFilter = filter.HasChars() ? filter.Get() : nullptr; of.lpstrFile = fileNamesBuffer.Get(); of.nMaxFile = maxFilenamesSize; - of.Flags = OFN_EXPLORER | OFN_ENABLESIZING; + of.Flags = OFN_EXPLORER | OFN_ENABLESIZING | OFN_OVERWRITEPROMPT; of.lpstrTitle = title.HasChars() ? title.Get() : nullptr; of.lpstrInitialDir = initialDirectory.HasChars() ? initialDirectory.Get() : nullptr; if (parentWindow) diff --git a/Source/Engine/UI/GUI/Control.cs b/Source/Engine/UI/GUI/Control.cs index 96b7bf22b..1b6779ff8 100644 --- a/Source/Engine/UI/GUI/Control.cs +++ b/Source/Engine/UI/GUI/Control.cs @@ -313,7 +313,7 @@ namespace FlaxEngine.GUI /// /// Gets the control DPI scale factor (1 is default). Includes custom DPI scale. /// - public float DpiScale => _root?.RootWindow?.Window.DpiScale ?? Platform.DpiScale; + public float DpiScale => RootWindow?.Window.DpiScale ?? Platform.DpiScale; /// /// Gets screen position of the control (upper left corner). diff --git a/Source/ThirdParty/OpenFBX/ofbx.cpp b/Source/ThirdParty/OpenFBX/ofbx.cpp index 36f942cf8..94fc6684b 100644 --- a/Source/ThirdParty/OpenFBX/ofbx.cpp +++ b/Source/ThirdParty/OpenFBX/ofbx.cpp @@ -3172,6 +3172,8 @@ static bool parseObjects(const Element& root, Scene* scene, u64 flags, Allocator node->bone_link_property = con.property; } break; + default: + break; } switch (parent->getType()) @@ -3190,6 +3192,8 @@ static bool parseObjects(const Element& root, Scene* scene, u64 flags, Allocator mesh->geometry = (Geometry*)child; break; case Object::Type::MATERIAL: mesh->materials.push_back((Material*)child); break; + default: + break; } break; } @@ -3323,6 +3327,8 @@ static bool parseObjects(const Element& root, Scene* scene, u64 flags, Allocator } break; } + default: + break; } } @@ -3350,6 +3356,8 @@ static bool parseObjects(const Element& root, Scene* scene, u64 flags, Allocator return false; } break; + default: + break; } } } diff --git a/Source/Tools/Flax.Build/Build/Builder.Projects.cs b/Source/Tools/Flax.Build/Build/Builder.Projects.cs index b9ae1e319..ba3c4c7bd 100644 --- a/Source/Tools/Flax.Build/Build/Builder.Projects.cs +++ b/Source/Tools/Flax.Build/Build/Builder.Projects.cs @@ -246,7 +246,7 @@ namespace Flax.Build // Get all modules aggregated into all binary modules used in all configurations of this target foreach (var configurationData in mainProject.Configurations) { - var configurationBinaryModules = GetBinaryModules(rootProject, configurationData.Target, configurationData.Modules); + var configurationBinaryModules = GetBinaryModules(projectInfo, configurationData.Target, configurationData.Modules); foreach (var configurationBinaryModule in configurationBinaryModules) { // Skip if none of the included binary modules is inside the project workspace (eg. merged external binary modules from engine to game project) @@ -272,7 +272,7 @@ namespace Flax.Build { var referenceBuildOptions = GetBuildOptions(referenceTarget, configurationData.TargetBuildOptions.Platform, configurationData.TargetBuildOptions.Toolchain, configurationData.Architecture, configurationData.Configuration, reference.Project.ProjectFolderPath); var referenceModules = CollectModules(rules, referenceBuildOptions.Platform, referenceTarget, referenceBuildOptions, referenceBuildOptions.Toolchain, referenceBuildOptions.Architecture, referenceBuildOptions.Configuration); - var referenceBinaryModules = GetBinaryModules(rootProject, referenceTarget, referenceModules); + var referenceBinaryModules = GetBinaryModules(projectInfo, referenceTarget, referenceModules); foreach (var binaryModule in referenceBinaryModules) { project.Defines.Add(binaryModule.Key.ToUpperInvariant() + "_API="); diff --git a/Source/Tools/Flax.Build/Build/Builder.cs b/Source/Tools/Flax.Build/Build/Builder.cs index 187a59776..1bcfca18e 100644 --- a/Source/Tools/Flax.Build/Build/Builder.cs +++ b/Source/Tools/Flax.Build/Build/Builder.cs @@ -82,6 +82,17 @@ namespace Flax.Build return GetModuleProject(module, buildData.Project); } + /// + /// Checks if the project that contains a given module (checks for modules located in the given project Source folder). + /// + /// The module. + /// The project to check. + /// True if project contains that module inside, otherwise it's external or referenced. + public static bool IsModuleFromProject(Module module, ProjectInfo project) + { + return GetModuleProject(module, project) == project; + } + /// /// Builds the targets. /// diff --git a/Source/Tools/Flax.Build/Build/EngineTarget.cs b/Source/Tools/Flax.Build/Build/EngineTarget.cs index a084815fe..76191943c 100644 --- a/Source/Tools/Flax.Build/Build/EngineTarget.cs +++ b/Source/Tools/Flax.Build/Build/EngineTarget.cs @@ -101,7 +101,7 @@ namespace Flax.Build } // Mono on Linux is using dynamic linking and needs additional link files - if (buildOptions.Platform.Target == TargetPlatform.Linux && Platform.BuildTargetPlatform == TargetPlatform.Linux) + if (buildOptions.Platform.Target == TargetPlatform.Linux && Platform.BuildTargetPlatform == TargetPlatform.Linux && !IsPreBuilt) { var task = graph.Add(); task.PrerequisiteFiles.Add(Path.Combine(buildOptions.OutputFolder, "libmonosgen-2.0.so")); @@ -141,7 +141,7 @@ namespace Flax.Build // Build Main module var mainModule = rules.GetModule("Main"); var mainModuleOutputPath = Path.Combine(exeBuildOptions.IntermediateFolder, mainModule.Name); - if (!IsPreBuilt && !Directory.Exists(mainModuleOutputPath)) + if (!Directory.Exists(mainModuleOutputPath)) Directory.CreateDirectory(mainModuleOutputPath); var mainModuleOptions = new BuildOptions { diff --git a/Source/Tools/Flax.Build/Build/NativeCpp/Builder.NativeCpp.cs b/Source/Tools/Flax.Build/Build/NativeCpp/Builder.NativeCpp.cs index 5b748a481..0c3eedd53 100644 --- a/Source/Tools/Flax.Build/Build/NativeCpp/Builder.NativeCpp.cs +++ b/Source/Tools/Flax.Build/Build/NativeCpp/Builder.NativeCpp.cs @@ -696,6 +696,26 @@ namespace Flax.Build { // Export symbols from binary module moduleOptions.CompileEnv.PreprocessorDefinitions.Add(binaryModuleNameUpper + (target.UseSymbolsExports ? "_API=" + toolchain.DllExport : "_API=")); + + // Import symbols from binary modules containing the referenced modules (from this project only, external ones are handled via ReferenceBuilds below) + foreach (var moduleName in moduleOptions.PrivateDependencies) + { + var dependencyModule = buildData.Rules.GetModule(moduleName); + if (dependencyModule != null && !string.IsNullOrEmpty(dependencyModule.BinaryModuleName) && dependencyModule.BinaryModuleName != binaryModule.Key && IsModuleFromProject(dependencyModule, project) && buildData.Modules.TryGetValue(dependencyModule, out var dependencyOptions)) + { + // Import symbols from referenced binary module + moduleOptions.CompileEnv.PreprocessorDefinitions.Add(dependencyModule.BinaryModuleName.ToUpperInvariant() + "_API=" + toolchain.DllImport); + } + } + foreach (var moduleName in moduleOptions.PublicDependencies) + { + var dependencyModule = buildData.Rules.GetModule(moduleName); + if (dependencyModule != null && !string.IsNullOrEmpty(dependencyModule.BinaryModuleName) && dependencyModule.BinaryModuleName != binaryModule.Key && IsModuleFromProject(dependencyModule, project) && buildData.Modules.TryGetValue(dependencyModule, out var dependencyOptions)) + { + // Import symbols from referenced binary module + moduleOptions.CompileEnv.PreprocessorDefinitions.Add(dependencyModule.BinaryModuleName.ToUpperInvariant() + "_API=" + toolchain.DllImport); + } + } } else { diff --git a/Source/Tools/Flax.Build/Build/ProjectTarget.cs b/Source/Tools/Flax.Build/Build/ProjectTarget.cs index 19d23690f..790452b34 100644 --- a/Source/Tools/Flax.Build/Build/ProjectTarget.cs +++ b/Source/Tools/Flax.Build/Build/ProjectTarget.cs @@ -45,7 +45,7 @@ namespace Flax.Build throw new Exception($"Invalid or missing editor target {project.EditorTarget} specified in project {project.Name} (referenced by project {Project.Name})."); return result; } - if (!IsEditor && !string.IsNullOrEmpty(project.GameTarget)) + if (!string.IsNullOrEmpty(project.GameTarget)) { var result = projectTargets.FirstOrDefault(x => x.Name == project.GameTarget); if (result == null) diff --git a/Source/Tools/Flax.Build/ProjectInfo.cs b/Source/Tools/Flax.Build/ProjectInfo.cs index 5f144b2fd..c47c87331 100644 --- a/Source/Tools/Flax.Build/ProjectInfo.cs +++ b/Source/Tools/Flax.Build/ProjectInfo.cs @@ -119,6 +119,8 @@ namespace Flax.Build private bool IsTargetCSharpOnly(string name) { + if (string.IsNullOrWhiteSpace(name)) + return true; var rules = Builder.GenerateRulesAssembly(); var target = rules.GetTarget(name); return target == null || target.Modules.TrueForAll(x => !rules.GetModule(x).BuildNativeCode);