Add PhysX and NvCloth to Web

This commit is contained in:
Wojtek Figat
2026-03-13 17:49:11 +01:00
parent a8e7faec3c
commit 6ae68bc6cc
22 changed files with 430 additions and 22 deletions

View File

@@ -39,12 +39,6 @@ public class Physics : EngineModule
{
base.Setup(options);
if (options.Platform.Target == TargetPlatform.Web) // TODO: build PhysX for Web
{
options.PrivateDefinitions.Add("COMPILE_WITH_EMPTY_PHYSICS");
return;
}
SetupPhysicsBackend(this, options);
if (WithCooking)
@@ -58,7 +52,7 @@ public class Physics : EngineModule
if (WithPhysX)
{
options.PrivateDependencies.Add("PhysX");
if (WithCloth && options.Platform.Target != TargetPlatform.PS4) // TODO: build nvcloth for ps4 with vs2017
if (WithCloth)
options.PrivateDependencies.Add("NvCloth");
}
else

View File

@@ -0,0 +1,39 @@
cmake_minimum_required(VERSION 3.3)
MESSAGE("[NvCloth]cmake/web/CMakeList.txt")
include(../common/CMakeLists.txt)
IF(NOT DEFINED TARGET_BUILD_PLATFORM) # Not defined, default to web
SET(TARGET_BUILD_PLATFORM "web")
ENDIF()
SET(PLATFORM_LIST web)
IF(NOT ${TARGET_BUILD_PLATFORM} IN_LIST PLATFORM_LIST)
MESSAGE(FATAL_ERROR "Invalid platform:" ${TARGET_BUILD_PLATFORM})
ENDIF()
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti -fno-exceptions -ffunction-sections -fdata-sections -fstrict-aliasing -Wstrict-aliasing=2")
# Enable SIMD
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -msimd128 -msse4.2 -mno-nontrapping-fptoint")
SET(CMAKE_SHARED_LINKER_FLAGS "")
# Build debug info for all configurations
SET(CMAKE_CXX_FLAGS_DEBUG "-O0 -g2")
SET(CMAKE_CXX_FLAGS_CHECKED "-g2 -O2")
SET(CMAKE_CXX_FLAGS_PROFILE "-O3 -g")
SET(CMAKE_CXX_FLAGS_RELEASE "-O3")
# Disable cuda and dx for all projects on web
SET(PHYSX_WEB_COMPILE_DEFS DISABLE_COMPUTE_PHYSX)
SET(PHYSX_WEB_DEBUG_COMPILE_DEFS _DEBUG;PX_DEBUG=1;PX_CHECKED=1;PX_NVTX=0;PX_SUPPORT_PVD=0)
SET(PHYSX_SWWEB_CHECKED_COMPILE_DEFS NDEBUG;PX_CHECKED=1;PX_NVTX=0;PX_SUPPORT_PVD=0)
SET(PHYSX_WEB_PROFILE_COMPILE_DEFS NDEBUG;PX_PROFILE=1;PX_NVTX=0;PX_SUPPORT_PVD=0)
SET(PHYSX_WEB_RELEASE_COMPILE_DEFS NDEBUG;PX_SUPPORT_PVD=0)
SET(PX_SELECT_COMPONENTS PxFoundation)
FIND_PACKAGE(PxShared REQUIRED)
# Include all of the projects
INCLUDE(NvCloth.cmake)

View File

@@ -0,0 +1,68 @@
#
# Build NvCloth (PROJECT not SOLUTION)
#
MESSAGE("[NvCloth]cmake/web/NvCloth.cmake")
SET(GW_DEPS_ROOT $ENV{GW_DEPS_ROOT})
FIND_PACKAGE(PxShared REQUIRED)
# No Cuda nor DX11
SET(NV_CLOTH_ENABLE_CUDA 0)
SET(NV_CLOTH_ENABLE_DX11 0)
#FIND_PACKAGE(nvToolsExt REQUIRED)
SET(NVCLOTH_PLATFORM_INCLUDES
${NVTOOLSEXT_INCLUDE_DIRS}
PRIVATE ${CUDA_INCLUDE_DIRS}
)
SET(NVCLOTH_PLATFORM_SOURCE_FILES
${PROJECT_ROOT_DIR}/src/ps/unix/PsUnixAtomic.cpp
${PROJECT_ROOT_DIR}/src/ps/unix/PsUnixFPU.h
)
IF(PX_STATIC_LIBRARIES)
SET(NVCLOTH_API_COMPILE_DEFS NV_CLOTH_IMPORT=;PX_CALL_CONV=;)
ELSE()
SET(NVCLOTH_API_COMPILE_DEFS NV_CLOTH_IMPORT=PX_DLL_EXPORT;)
ENDIF()
# Use generator expressions to set config specific preprocessor definitions
SET(NVCLOTH_COMPILE_DEFS
${NVCLOTH_API_COMPILE_DEFS}
NV_CLOTH_ENABLE_DX11=0
# Common to all configurations
${PHYSX_WEB_COMPILE_DEFS};PX_PHYSX_CORE_EXPORTS
$<$<CONFIG:debug>:${PHYSX_WEB_DEBUG_COMPILE_DEFS};PX_PHYSX_DLL_NAME_POSTFIX=DEBUG;>
$<$<CONFIG:checked>:${PHYSX_WEB_CHECKED_COMPILE_DEFS};PX_PHYSX_DLL_NAME_POSTFIX=CHECKED;>
$<$<CONFIG:profile>:${PHYSX_WEB_PROFILE_COMPILE_DEFS};PX_PHYSX_DLL_NAME_POSTFIX=PROFILE;>
$<$<CONFIG:release>:${PHYSX_WEB_RELEASE_COMPILE_DEFS};>
)
LIST(APPEND NVCLOTH_COMPILE_DEFS
NV_CLOTH_ENABLE_CUDA=0
)
IF(PX_STATIC_LIBRARIES)
SET(NVCLOTH_LIBTYPE STATIC)
ELSE()
SET(NVCLOTH_LIBTYPE SHARED)
ENDIF()
# include common NvCloth settings
INCLUDE(../common/NvCloth.cmake)
TARGET_LINK_LIBRARIES(NvCloth PUBLIC ${CUDA_CUDA_LIBRARY})
SET_TARGET_PROPERTIES(NvCloth PROPERTIES
LINK_FLAGS_DEBUG ""
LINK_FLAGS_CHECKED ""
LINK_FLAGS_PROFILE ""
LINK_FLAGS_RELEASE ""
)
SET_TARGET_PROPERTIES(NvCloth PROPERTIES POSITION_INDEPENDENT_CODE TRUE)
MESSAGE("[NvCloth]cmake/web/NvCloth.cmake END")

View File

@@ -0,0 +1,111 @@
#ifndef PX_FOUNDATION_PS_WEB_INTRINSICS_H
#define PX_FOUNDATION_PS_WEB_INTRINSICS_H
#include "NvCloth/ps/Ps.h"
// this file is for internal intrinsics - that is, intrinsics that are used in
// cross platform code but do not appear in the API
#if !PX_WEB
#error "This file should only be included by Web builds!!"
#endif
#include <math.h>
/** \brief NVidia namespace */
namespace nv
{
/** \brief nvcloth namespace */
namespace cloth
{
namespace ps
{
/*
* Implements a memory barrier
*/
PX_FORCE_INLINE void PxMemoryBarrier()
{
__sync_synchronize();
}
/*!
Returns the index of the highest set bit. Not valid for zero arg.
*/
PX_FORCE_INLINE uint32_t PxHighestSetBitUnsafe(uint32_t v)
{
// http://graphics.stanford.edu/~seander/bithacks.html
static const uint32_t MultiplyDeBruijnBitPosition[32] =
{
0, 9, 1, 10, 13, 21, 2, 29, 11, 14, 16, 18, 22, 25, 3, 30,
8, 12, 20, 28, 15, 17, 24, 7, 19, 27, 23, 6, 26, 5, 4, 31
};
v |= v >> 1; // first round up to one less than a power of 2
v |= v >> 2;
v |= v >> 4;
v |= v >> 8;
v |= v >> 16;
return MultiplyDeBruijnBitPosition[(uint32_t)(v * 0x07C4ACDDU) >> 27];
}
/*!
Returns the index of the highest set bit. Undefined for zero arg.
*/
PX_FORCE_INLINE uint32_t PxLowestSetBitUnsafe(uint32_t v)
{
// http://graphics.stanford.edu/~seander/bithacks.html
static const uint32_t MultiplyDeBruijnBitPosition[32] =
{
0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8,
31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9
};
int32_t w = v;
return MultiplyDeBruijnBitPosition[(uint32_t)((w & -w) * 0x077CB531U) >> 27];
}
/*!
Returns the number of leading zeros in v. Returns 32 for v=0.
*/
PX_FORCE_INLINE uint32_t PxCountLeadingZeros(uint32_t v)
{
int32_t result = 0;
uint32_t testBit = (1<<31);
while ((v & testBit) == 0 && testBit != 0)
{
result++;
testBit >>= 1;
}
return result;
}
/*!
Prefetch aligned cache size around \c ptr+offset.
*/
PX_FORCE_INLINE void PxPrefetchLine(const void* ptr, uint32_t offset = 0)
{
__builtin_prefetch((char* PX_RESTRICT)(const_cast<void*>(ptr)) + offset, 0, 3);
}
/*!
Prefetch \c count bytes starting at \c ptr.
*/
PX_FORCE_INLINE void PxPrefetch(const void* ptr, uint32_t count = 1)
{
const char* cp = (char*)const_cast<void*>(ptr);
uint64_t p = size_t(ptr);
uint64_t startLine = p >> 6, endLine = (p + count - 1) >> 6;
uint64_t lines = endLine - startLine + 1;
do
{
PxPrefetchLine(cp);
cp += 64;
} while(--lines);
}
} // namespace ps
} // namespace cloth
} // namespace nv
#endif

View File

@@ -0,0 +1,125 @@
#ifndef PX_FOUNDATION_PX_WEB_INTRINSICS_H
#define PX_FOUNDATION_PX_WEB_INTRINSICS_H
#include "foundation/Px.h"
#include "foundation/PxSharedAssert.h"
#if !PX_WEB
#error "This file should only be included by Web builds!!"
#endif
#include <math.h>
#include <float.h>
#include "nn/cstd/cstd_CMath.h"
#if !PX_DOXYGEN
namespace physx
{
#endif
namespace intrinsics
{
//! \brief platform-specific absolute value
PX_CUDA_CALLABLE PX_FORCE_INLINE float abs(float a) { return ::fabsf(a); }
//! \brief platform-specific select float
PX_CUDA_CALLABLE PX_FORCE_INLINE float fsel(float a, float b, float c) { return (a >= 0.0f) ? b : c; }
//! \brief platform-specific sign
PX_CUDA_CALLABLE PX_FORCE_INLINE float sign(float a) { return (a >= 0.0f) ? 1.0f : -1.0f; }
//! \brief platform-specific reciprocal
PX_CUDA_CALLABLE PX_FORCE_INLINE float recip(float a) { return 1.0f/a; }
//! \brief platform-specific reciprocal estimate
PX_CUDA_CALLABLE PX_FORCE_INLINE float recipFast(float a) { return 1.0f/a; }
//! \brief platform-specific square root
PX_CUDA_CALLABLE PX_FORCE_INLINE float sqrt(float a) { return ::sqrtf(a); }
//! \brief platform-specific reciprocal square root
PX_CUDA_CALLABLE PX_FORCE_INLINE float recipSqrt(float a) { return 1.0f/::sqrtf(a); }
//! \brief platform-specific reciprocal square root estimate
PX_CUDA_CALLABLE PX_FORCE_INLINE float recipSqrtFast(float a) { return 1.0f/::sqrtf(a); }
//! \brief platform-specific sine
PX_CUDA_CALLABLE PX_FORCE_INLINE float sin(float a) { return ::sinf(a); }
//! \brief platform-specific cosine
PX_CUDA_CALLABLE PX_FORCE_INLINE float cos(float a) { return ::cosf(a); }
//! \brief platform-specific minimum
PX_CUDA_CALLABLE PX_FORCE_INLINE float selectMin(float a, float b) { return a<b ? a : b; }
//! \brief platform-specific maximum
PX_CUDA_CALLABLE PX_FORCE_INLINE float selectMax(float a, float b) { return a>b ? a : b; }
//! \brief platform-specific finiteness check
PX_CUDA_CALLABLE PX_FORCE_INLINE bool isFinite(float a)
{
#ifdef __CUDACC__
return isfinite(a) ? true : false;
#else
return !nn::cstd::IsNan(a) && !nn::cstd::IsInf(a);
#endif
}
//! \brief platform-specific finiteness check
PX_CUDA_CALLABLE PX_FORCE_INLINE bool isFinite(double a)
{
#ifdef __CUDACC__
return isfinite(a) ? true : false;
#else
return !nn::cstd::IsNan(a) && !nn::cstd::IsInf(a);
#endif
}
/*!
Sets \c count bytes starting at \c dst to zero.
*/
PX_FORCE_INLINE void* memZero(void* PX_RESTRICT dest, uint32_t count)
{
return memset(dest, 0, count);
}
/*!
Sets \c count bytes starting at \c dst to \c c.
*/
PX_FORCE_INLINE void* memSet(void* PX_RESTRICT dest, int32_t c, uint32_t count)
{
return memset(dest, c, count);
}
/*!
Copies \c count bytes from \c src to \c dst. User memMove if regions overlap.
*/
PX_FORCE_INLINE void* memCopy(void* PX_RESTRICT dest, const void* PX_RESTRICT src, uint32_t count)
{
return memcpy(dest, src, count);
}
/*!
Copies \c count bytes from \c src to \c dst. Supports overlapping regions.
*/
PX_FORCE_INLINE void* memMove(void* PX_RESTRICT dest, const void* PX_RESTRICT src, uint32_t count)
{
return memmove(dest, src, count);
}
/*!
Set 128B to zero starting at \c dst+offset. Must be aligned.
*/
PX_FORCE_INLINE void memZero128(void* PX_RESTRICT dest, uint32_t offset = 0)
{
PX_SHARED_ASSERT(((size_t(dest)+offset) & 0x7f) == 0);
memSet((char* PX_RESTRICT)dest+offset, 0, 128);
}
} // namespace intrinsics
#if !PX_DOXYGEN
} // namespace physx
#endif
#endif

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -89,27 +89,29 @@ public class PhysX : EngineDepsModule
else
{
options.PublicDefinitions.Add("PX_PHYSX_STATIC_LIB");
if (options.Platform.Target != TargetPlatform.Web)
archPostFix = "_static" + archPostFix;
AddLib(options, depsRoot, string.Format("PhysX_static{0}", archPostFix));
AddLib(options, depsRoot, string.Format("PhysXCharacterKinematic_static{0}", archPostFix));
AddLib(options, depsRoot, string.Format("PhysXCommon_static{0}", archPostFix));
AddLib(options, depsRoot, string.Format("PhysXExtensions_static{0}", archPostFix));
AddLib(options, depsRoot, string.Format("PhysXFoundation_static{0}", archPostFix));
AddLib(options, depsRoot, string.Format("PhysX{0}", archPostFix));
AddLib(options, depsRoot, string.Format("PhysXCharacterKinematic{0}", archPostFix));
AddLib(options, depsRoot, string.Format("PhysXCommon{0}", archPostFix));
AddLib(options, depsRoot, string.Format("PhysXExtensions{0}", archPostFix));
AddLib(options, depsRoot, string.Format("PhysXFoundation{0}", archPostFix));
if (usePhysicsCooking)
{
AddLib(options, depsRoot, string.Format("PhysXCooking_static{0}", archPostFix));
AddLib(options, depsRoot, string.Format("PhysXCooking{0}", archPostFix));
}
if (usePVD)
{
AddLib(options, depsRoot, string.Format("PhysXPvdSDK_static{0}", archPostFix));
AddLib(options, depsRoot, string.Format("PhysXPvdSDK{0}", archPostFix));
}
if (useVehicle)
{
AddLib(options, depsRoot, string.Format("PhysXVehicle_static{0}", archPostFix));
//AddLib(options, depsRoot, string.Format("PhysXVehicle2_static{0}", archPostFix));
AddLib(options, depsRoot, string.Format("PhysXVehicle{0}", archPostFix));
//AddLib(options, depsRoot, string.Format("PhysXVehicle2{0}", archPostFix));
}
}
}

View File

@@ -86,7 +86,7 @@ PX_FORCE_INLINE uint32_t PxComputeHash(const uint64_t key)
return uint32_t(UINT32_MAX & k);
}
#if PX_APPLE_FAMILY
#if PX_APPLE_FAMILY || PX_EMSCRIPTEN
// hash for size_t, to make gcc happy
PX_INLINE uint32_t PxComputeHash(const size_t key)
{

View File

@@ -45,7 +45,7 @@
#pragma warning(pop)
#endif
#if (PX_LINUX_FAMILY && !PX_ARM_FAMILY)
#if (PX_LINUX_FAMILY && !PX_ARM_FAMILY && !PX_EMSCRIPTEN)
// Force linking against nothing newer than glibc v2.17 to remain compatible with platforms with older glibc versions
__asm__(".symver expf,expf@GLIBC_2.2.5");
__asm__(".symver powf,powf@GLIBC_2.2.5");

View File

@@ -70,6 +70,10 @@ namespace Flax.Deps.Dependencies
case TargetPlatform.Linux:
Build(options, platform, architecture);
break;
case TargetPlatform.Web:
Utilities.DirectoryCopy(Path.Combine(GetBinariesFolder(options, platform), "Data", "NvCloth"), root, true, true);
Build(options, platform, TargetArchitecture.x86);
break;
}
}
}
@@ -147,6 +151,15 @@ namespace Flax.Deps.Dependencies
envVars.Add("CC", "clang-" + Configuration.LinuxClangMinVer);
envVars.Add("CXX", "clang++-" + Configuration.LinuxClangMinVer);
break;
case TargetPlatform.Web:
cmakeArgs += " -DTARGET_BUILD_PLATFORM=web";
cmakeArgs += $" -DCMAKE_TOOLCHAIN_FILE=\"{EmscriptenSdk.Instance.CMakeToolchainPath}\"";
cmakeName = "web";
binariesPrefix = "lib";
// Disable SIMD and fix missing symbols during linking
Utilities.ReplaceInFile(Path.Combine(nvCloth, "src/NvSimd/NvSimdTypes.h"), "(__x86_64__) || PX_EMSCRIPTEN", "(__x86_64__) // || PX_EMSCRIPTEN");
break;
default: throw new InvalidPlatformException(platform);
}
var cmakeFolder = Path.Combine(nvCloth, "compiler", "cmake", cmakeName);
@@ -169,13 +182,15 @@ namespace Flax.Deps.Dependencies
Log.Info($"Building {File.ReadAllLines(Path.Combine(root, "README.md"))[0].Trim()} to {platform} {architecture}");
// Generate project files
var config = "Release";
SetupDirectory(buildFolder, false);
SetupDirectory(Path.Combine(root, "PxShared/src/foundation/include"), false);
Utilities.FileDelete(Path.Combine(cmakeFolder, "CMakeCache.txt"));
cmakeArgs += $" -DPX_STATIC_LIBRARIES=1 -DPX_OUTPUT_DLL_DIR=\"{Path.Combine(buildFolder, "bin")}\" -DPX_OUTPUT_LIB_DIR=\"{Path.Combine(buildFolder, "lib")}\" -DPX_OUTPUT_EXE_DIR=\"{Path.Combine(buildFolder, "bin")}\"";
RunCmake(cmakeFolder, platform, architecture, " -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=OFF " + cmakeArgs, envVars);
RunCmake(cmakeFolder, platform, architecture, $" -DCMAKE_BUILD_TYPE={config} -DBUILD_SHARED_LIBS=OFF " + cmakeArgs, envVars);
// Run build
BuildCmake(cmakeFolder, envVars);
BuildCmake(cmakeFolder, config, envVars);
// Deploy binaries
var libs = new[]

View File

@@ -185,6 +185,11 @@ namespace Flax.Deps.Dependencies
binariesPrefix = "lib";
envVars.Add("IPHONEOS_DEPLOYMENT_TARGET", Configuration.iOSMinVer);
break;
case TargetPlatform.Web:
binariesSubDir = "web32";
binariesPrefix = "lib";
envVars.Add("CMAKE_TOOLCHAIN_FILE", EmscriptenSdk.Instance.CMakeToolchainPath);
break;
default: throw new InvalidPlatformException(targetPlatform);
}
@@ -206,7 +211,6 @@ namespace Flax.Deps.Dependencies
envVars.Add("CC", "clang-" + Configuration.LinuxClangMinVer);
envVars.Add("CC_FOR_BUILD", "clang-" + Configuration.LinuxClangMinVer);
envVars.Add("CXX", "clang++-" + Configuration.LinuxClangMinVer);
envVars.Add("CMAKE_BUILD_PARALLEL_LEVEL", CmakeBuildParallel);
break;
case TargetPlatform.Mac: break;
default: throw new InvalidPlatformException(BuildPlatform);
@@ -259,6 +263,8 @@ namespace Flax.Deps.Dependencies
};
var dstBinaries = GetThirdPartyFolder(options, targetPlatform, architecture);
var srcBinaries = Path.Combine(root, "physx", "bin", binariesSubDir, configuration);
if (targetPlatform == TargetPlatform.Web)
srcBinaries = Path.Combine(root, "physx", "compiler", binariesSubDir, "sdk_source_bin");
switch (BuildPlatform)
{
case TargetPlatform.Windows:
@@ -267,6 +273,9 @@ namespace Flax.Deps.Dependencies
case TargetPlatform.Android:
Utilities.Run("cmake", "--build .", null, Path.Combine(root, "physx\\compiler\\android-" + configuration), Utilities.RunOptions.ConsoleLogOutput, envVars);
break;
case TargetPlatform.Web:
Utilities.Run("cmake", "--build .", null, Path.Combine(root, "physx\\compiler\\" + preset), Utilities.RunOptions.DefaultTool, envVars);
break;
default:
VCEnvironment.BuildSolution(Path.Combine(solutionFilesRoot, preset, "PhysXSDK.sln"), configuration, buildPlatform, msBuildProps, msBuild);
break;
@@ -287,6 +296,8 @@ namespace Flax.Deps.Dependencies
foreach (var physXLib in defaultPhysXLibs)
{
var filename = suppressBitsPostfix ? string.Format("{0}{1}_static", binariesPrefix, physXLib) : string.Format("{0}{1}_static_{2}", binariesPrefix, physXLib, bits);
if (targetPlatform == TargetPlatform.Web)
filename = binariesPrefix + physXLib;
filename += binariesExtension;
Utilities.FileCopy(Path.Combine(srcBinaries, filename), Path.Combine(dstBinaries, filename));
@@ -429,6 +440,11 @@ namespace Flax.Deps.Dependencies
Build(options, "ios64", platform, TargetArchitecture.ARM64);
break;
}
case TargetPlatform.Web:
{
Build(options, "web32", platform, TargetArchitecture.x86);
break;
}
}
}
}

View File

@@ -97,6 +97,7 @@ namespace Flax.Deps
TargetPlatform.PS5,
TargetPlatform.Android,
TargetPlatform.Switch,
TargetPlatform.Web,
};
case TargetPlatform.Linux:
return new[]
@@ -127,6 +128,7 @@ namespace Flax.Deps
case TargetPlatform.Windows:
return new[]
{
TargetArchitecture.x86,
TargetArchitecture.x64,
TargetArchitecture.ARM64,
};
@@ -562,7 +564,7 @@ namespace Flax.Deps
}
case TargetPlatform.Web:
{
cmdLine = string.Format("CMakeLists.txt -DCMAKE_TOOLCHAIN_FILE=\"{0}/emscripten/cmake/Modules/Platform/Emscripten.cmake\"", EmscriptenSdk.Instance.EmscriptenPath);
cmdLine = $"CMakeLists.txt -DCMAKE_TOOLCHAIN_FILE=\"{EmscriptenSdk.Instance.CMakeToolchainPath}\"";
if (BuildPlatform == TargetPlatform.Windows)
cmdLine += " -G \"Ninja\"";
break;

View File

@@ -30,6 +30,11 @@ namespace Flax.Build.Platforms
/// </summary>
public string EmscriptenPath;
/// <summary>
/// Full path to the CMake toolchain file (eg. '%EMSDK%\upstream\emscripten\cmake\Modules\Platform\Emscripten.cmake').
/// </summary>
public string CMakeToolchainPath;
/// <summary>
/// Initializes a new instance of the <see cref="AndroidSdk"/> class.
/// </summary>
@@ -52,6 +57,7 @@ namespace Flax.Build.Platforms
{
RootPath = sdkPath;
EmscriptenPath = Path.Combine(sdkPath, "upstream");
CMakeToolchainPath = Path.Combine(EmscriptenPath, "emscripten/cmake/Modules/Platform/Emscripten.cmake");
var versionPath = Path.Combine(EmscriptenPath, "emscripten", "emscripten-version.txt");
if (File.Exists(versionPath))
{