From 1bd924e478383450aecd5470d5f7e96c9372945b Mon Sep 17 00:00:00 2001 From: Chuck Walbourn Date: Tue, 17 Sep 2024 13:45:19 -0700 Subject: [PATCH] Refactor command-line tools to use shared header (#159) --- CMakeLists.txt | 6 + CMakePresets.json | 4 +- Meshconvert/CmdLineHelpers.h | 375 ++++++++++++++++++ Meshconvert/Meshconvert.cpp | 305 +------------- Meshconvert/Meshconvert_Desktop_2019.vcxproj | 1 + .../Meshconvert_Desktop_2019.vcxproj.filters | 1 + .../Meshconvert_Desktop_2019_Win10.vcxproj | 1 + ...convert_Desktop_2019_Win10.vcxproj.filters | 1 + Meshconvert/Meshconvert_Desktop_2022.vcxproj | 1 + .../Meshconvert_Desktop_2022.vcxproj.filters | 1 + .../Meshconvert_Desktop_2022_Win10.vcxproj | 1 + ...convert_Desktop_2022_Win10.vcxproj.filters | 1 + 12 files changed, 410 insertions(+), 288 deletions(-) create mode 100644 Meshconvert/CmdLineHelpers.h diff --git a/CMakeLists.txt b/CMakeLists.txt index b031127..5134555 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -197,6 +197,7 @@ if(BUILD_TOOLS AND WIN32) Meshconvert/Meshconvert.cpp Meshconvert/Meshconvert.rc Meshconvert/settings.manifest + Meshconvert/CmdLineHelpers.h Meshconvert/MeshOBJ.cpp Meshconvert/Mesh.h Meshconvert/Mesh.cpp @@ -211,6 +212,10 @@ if(BUILD_TOOLS AND WIN32) endif() endif() +if(TOOL_EXES) + message(STATUS "Building tools: ${TOOL_EXES}") +endif() + if(MSVC) foreach(t IN LISTS TOOL_EXES ITEMS ${PROJECT_NAME}) target_compile_options(${t} PRIVATE /Wall /GR-) @@ -248,6 +253,7 @@ elseif(CMAKE_CXX_COMPILER_ID MATCHES "Intel") set_target_properties(${PROJECT_NAME} PROPERTIES CXX_STANDARD 14) elseif(CMAKE_CXX_COMPILER_ID MATCHES "MSVC") if(ENABLE_CODE_ANALYSIS) + message(STATUS "Building with Code Analysis (PREFIX)") foreach(t IN LISTS TOOL_EXES ITEMS ${PROJECT_NAME}) target_compile_options(${t} PRIVATE /analyze /WX) endforeach() diff --git a/CMakePresets.json b/CMakePresets.json index ccec7da..e3c0456 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -265,7 +265,9 @@ { "name": "x64-Debug-Linux", "description": "WSL Linux x64 (Debug)", "inherits": [ "base", "x64", "Debug", "VCPKG" ] }, { "name": "x64-Release-Linux", "description": "WSL Linux x64 (Release)", "inherits": [ "base", "x64", "Release", "VCPKG" ] }, { "name": "arm64-Debug-Linux", "description": "WSL Linux ARM64 (Debug)", "inherits": [ "base", "ARM64", "Debug", "VCPKG" ] }, - { "name": "arm64-Release-Linux", "description": "WSL Linux ARM64 (Release)", "inherits": [ "base", "ARM64", "Release", "VCPKG" ] } + { "name": "arm64-Release-Linux", "description": "WSL Linux ARM64 (Release)", "inherits": [ "base", "ARM64", "Release", "VCPKG" ] }, + + { "name": "x64-Analyze" , "description": "MSVC for x64 (Debug) with DX12 using /analyze", "inherits": [ "base", "x64", "Debug", "MSVC" ], "cacheVariables": { "ENABLE_CODE_ANALYSIS": true } } ], "testPresets": [ { "name": "x64-Debug" , "configurePreset": "x64-Debug" }, diff --git a/Meshconvert/CmdLineHelpers.h b/Meshconvert/CmdLineHelpers.h new file mode 100644 index 0000000..b3af3ee --- /dev/null +++ b/Meshconvert/CmdLineHelpers.h @@ -0,0 +1,375 @@ +//-------------------------------------------------------------------------------------- +// File: CmdLineHelpers.h +// +// Command-line tool shared functions +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +//-------------------------------------------------------------------------------------- + +#pragma once + +#if __cplusplus < 201703L +#error Requires C++17 (and /Zc:__cplusplus with MSVC) +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef TOOL_VERSION +#error Define TOOL_VERSION before including this header +#endif + + +namespace Helpers +{ + struct handle_closer { void operator()(HANDLE h) { if (h) CloseHandle(h); } }; + + using ScopedHandle = std::unique_ptr; + + inline HANDLE safe_handle(HANDLE h) noexcept { return (h == INVALID_HANDLE_VALUE) ? nullptr : h; } + + struct find_closer { void operator()(HANDLE h) noexcept { assert(h != INVALID_HANDLE_VALUE); if (h) FindClose(h); } }; + + using ScopedFindHandle = std::unique_ptr; + +#ifdef _PREFAST_ +#pragma prefast(disable : 26018, "Only used with static internal arrays") +#endif + + struct SConversion + { + std::wstring szSrc; + std::wstring szFolder; + }; + + template + struct SValue + { + const wchar_t* name; + T value; + }; + + template + T LookupByName(const wchar_t _In_z_ *pName, const SValue *pArray) + { + while (pArray->name) + { + if (!_wcsicmp(pName, pArray->name)) + return pArray->value; + + pArray++; + } + + return static_cast(0); + } + + template + const wchar_t* LookupByValue(T value, const SValue *pArray) + { + while (pArray->name) + { + if (value == pArray->value) + return pArray->name; + + pArray++; + } + + return L""; + } + + void PrintFormat(DXGI_FORMAT Format, const SValue* pFormatList) + { + for (auto pFormat = pFormatList; pFormat->name; pFormat++) + { + if (pFormat->value == Format) + { + wprintf(L"%ls", pFormat->name); + return; + } + } + + wprintf(L"*UNKNOWN*"); + } + + void PrintFormat(DXGI_FORMAT Format, const SValue* pFormatList1, const SValue* pFormatList2) + { + for (auto pFormat = pFormatList1; pFormat->name; pFormat++) + { + if (pFormat->value == Format) + { + wprintf(L"%ls", pFormat->name); + return; + } + } + + for (auto pFormat = pFormatList2; pFormat->name; pFormat++) + { + if (pFormat->value == Format) + { + wprintf(L"%ls", pFormat->name); + return; + } + } + + wprintf(L"*UNKNOWN*"); + } + + template + void PrintList(size_t cch, const SValue *pValue) + { + while (pValue->name) + { + const size_t cchName = wcslen(pValue->name); + + if (cch + cchName + 2 >= 80) + { + wprintf(L"\n "); + cch = 6; + } + + wprintf(L"%ls ", pValue->name); + cch += cchName + 2; + pValue++; + } + + wprintf(L"\n"); + } + + void PrintLogo(bool versionOnly, _In_z_ const wchar_t* name, _In_z_ const wchar_t* desc) + { + wchar_t version[32] = {}; + + wchar_t appName[_MAX_PATH] = {}; + if (GetModuleFileNameW(nullptr, appName, _MAX_PATH)) + { + const DWORD size = GetFileVersionInfoSizeW(appName, nullptr); + if (size > 0) + { + auto verInfo = std::make_unique(size); + if (GetFileVersionInfoW(appName, 0, size, verInfo.get())) + { + LPVOID lpstr = nullptr; + UINT strLen = 0; + if (VerQueryValueW(verInfo.get(), L"\\StringFileInfo\\040904B0\\ProductVersion", &lpstr, &strLen)) + { + wcsncpy_s(version, reinterpret_cast(lpstr), strLen); + } + } + } + } + + if (!*version || wcscmp(version, L"1.0.0.0") == 0) + { + swprintf_s(version, L"%03d (library)", TOOL_VERSION); + } + + if (versionOnly) + { + wprintf(L"%ls version %ls\n", name, version); + } + else + { + wprintf(L"%ls Version %ls\n", desc, version); + wprintf(L"Copyright (C) Microsoft Corp.\n"); + #ifdef _DEBUG + wprintf(L"*** Debug build ***\n"); + #endif + wprintf(L"\n"); + } + } + + void SearchForFiles(const std::filesystem::path& path, std::list& files, bool recursive, _In_opt_z_ const wchar_t* folder) + { + // Process files + WIN32_FIND_DATAW findData = {}; + ScopedFindHandle hFile(safe_handle(FindFirstFileExW(path.c_str(), + FindExInfoBasic, &findData, + FindExSearchNameMatch, nullptr, + FIND_FIRST_EX_LARGE_FETCH))); + if (hFile) + { + for (;;) + { + if (!(findData.dwFileAttributes & (FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_DIRECTORY))) + { + SConversion conv = {}; + conv.szSrc = path.parent_path().append(findData.cFileName).native(); + if (folder) + { + conv.szFolder = folder; + } + files.push_back(conv); + } + + if (!FindNextFileW(hFile.get(), &findData)) + break; + } + } + + // Process directories + if (recursive) + { + auto searchDir = path.parent_path().append(L"*"); + + hFile.reset(safe_handle(FindFirstFileExW(searchDir.c_str(), + FindExInfoBasic, &findData, + FindExSearchLimitToDirectories, nullptr, + FIND_FIRST_EX_LARGE_FETCH))); + if (!hFile) + return; + + for (;;) + { + if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + { + if (findData.cFileName[0] != L'.') + { + auto subfolder = (folder) + ? (std::wstring(folder) + std::wstring(findData.cFileName) + std::filesystem::path::preferred_separator) + : (std::wstring(findData.cFileName) + std::filesystem::path::preferred_separator); + + auto subdir = path.parent_path().append(findData.cFileName).append(path.filename().c_str()); + + SearchForFiles(subdir, files, recursive, subfolder.c_str()); + } + } + + if (!FindNextFileW(hFile.get(), &findData)) + break; + } + } + } + + void ProcessFileList(std::wifstream& inFile, std::list& files) + { + std::list flist; + std::set excludes; + + for (;;) + { + std::wstring fname; + std::getline(inFile, fname); + if (!inFile) + break; + + if (fname[0] == L'#') + { + // Comment + } + else if (fname[0] == L'-') + { + if (flist.empty()) + { + wprintf(L"WARNING: Ignoring the line '%ls' in -flist\n", fname.c_str()); + } + else + { + std::filesystem::path path(fname.c_str() + 1); + auto& npath = path.make_preferred(); + if (wcspbrk(fname.c_str(), L"?*") != nullptr) + { + std::list removeFiles; + SearchForFiles(npath, removeFiles, false, nullptr); + + for (auto& it : removeFiles) + { + std::wstring name = it.szSrc; + std::transform(name.begin(), name.end(), name.begin(), towlower); + excludes.insert(name); + } + } + else + { + std::wstring name = npath.c_str(); + std::transform(name.begin(), name.end(), name.begin(), towlower); + excludes.insert(name); + } + } + } + else if (wcspbrk(fname.c_str(), L"?*") != nullptr) + { + std::filesystem::path path(fname.c_str()); + SearchForFiles(path.make_preferred(), flist, false, nullptr); + } + else + { + SConversion conv = {}; + std::filesystem::path path(fname.c_str()); + conv.szSrc = path.make_preferred().native(); + flist.push_back(conv); + } + } + + inFile.close(); + + if (!excludes.empty()) + { + // Remove any excluded files + for (auto it = flist.begin(); it != flist.end();) + { + std::wstring name = it->szSrc; + std::transform(name.begin(), name.end(), name.begin(), towlower); + auto item = it; + ++it; + if (excludes.find(name) != excludes.end()) + { + flist.erase(item); + } + } + } + + if (flist.empty()) + { + wprintf(L"WARNING: No file names found in -flist\n"); + } + else + { + files.splice(files.end(), flist); + } + } + + const wchar_t* GetErrorDesc(HRESULT hr) + { + static wchar_t desc[1024] = {}; + + LPWSTR errorText = nullptr; + + const DWORD result = FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_ALLOCATE_BUFFER, + nullptr, static_cast(hr), + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), reinterpret_cast(&errorText), 0, nullptr); + + *desc = 0; + + if (result > 0 && errorText) + { + swprintf_s(desc, L": %ls", errorText); + + size_t len = wcslen(desc); + if (len >= 1) + { + desc[len - 1] = 0; + } + + if (errorText) + LocalFree(errorText); + + for(wchar_t* ptr = desc; *ptr != 0; ++ptr) + { + if (*ptr == L'\r' || *ptr == L'\n') + { + *ptr = L' '; + } + } + } + + return desc; + } +} diff --git a/Meshconvert/Meshconvert.cpp b/Meshconvert/Meshconvert.cpp index c944afe..7d8594b 100644 --- a/Meshconvert/Meshconvert.cpp +++ b/Meshconvert/Meshconvert.cpp @@ -48,10 +48,17 @@ #include "Mesh.h" +#define TOOL_VERSION DIRECTX_MESH_VERSION +#include "CmdLineHelpers.h" + +using namespace Helpers; using namespace DirectX; namespace { + const wchar_t* g_ToolName = L"meshconvert"; + const wchar_t* g_Description = L"Microsoft (R) MeshConvert Command-line Tool [DirectXMesh]"; + enum OPTIONS : uint32_t { OPT_RECURSIVE = 1, @@ -90,22 +97,11 @@ namespace static_assert(OPT_MAX <= 32, "dwOptions is a unsigned int bitfield"); - struct SConversion - { - std::wstring szSrc; - }; - - struct SValue - { - const wchar_t* name; - uint32_t value; - }; - ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// - const SValue g_pOptions[] = + const SValue g_pOptions[] = { { L"r", OPT_RECURSIVE }, { L"ta", OPT_TOPOLOGICAL_ADJ }, @@ -141,22 +137,22 @@ namespace { nullptr, 0 } }; - const SValue g_vertexNormalFormats[] = + const SValue g_vertexNormalFormats[] = { { L"float3", DXGI_FORMAT_R32G32B32_FLOAT }, { L"float16_4", DXGI_FORMAT_R16G16B16A16_FLOAT }, { L"r11g11b10", DXGI_FORMAT_R11G11B10_FLOAT }, - { nullptr, 0 } + { nullptr, DXGI_FORMAT_UNKNOWN } }; - const SValue g_vertexUVFormats[] = + const SValue g_vertexUVFormats[] = { { L"float2", DXGI_FORMAT_R32G32_FLOAT }, { L"float16_2", DXGI_FORMAT_R16G16_FLOAT }, - { nullptr, 0 } + { nullptr, DXGI_FORMAT_UNKNOWN } }; - const SValue g_vertexColorFormats[] = + const SValue g_vertexColorFormats[] = { { L"bgra", DXGI_FORMAT_B8G8R8A8_UNORM }, { L"rgba", DXGI_FORMAT_R8G8B8A8_UNORM }, @@ -164,7 +160,7 @@ namespace { L"float16_4", DXGI_FORMAT_R16G16B16A16_FLOAT }, { L"rgba_10", DXGI_FORMAT_R10G10B10A2_UNORM }, { L"r11g11b10", DXGI_FORMAT_R11G11B10_FLOAT }, - { nullptr, 0 } + { nullptr, DXGI_FORMAT_UNKNOWN } }; } @@ -182,237 +178,9 @@ HRESULT LoadFromOBJ(const wchar_t* szFilename, namespace { - inline HANDLE safe_handle(HANDLE h) noexcept { return (h == INVALID_HANDLE_VALUE) ? nullptr : h; } - - struct find_closer { void operator()(HANDLE h) noexcept { assert(h != INVALID_HANDLE_VALUE); if (h) FindClose(h); } }; - - using ScopedFindHandle = std::unique_ptr; - -#ifdef __PREFAST__ -#pragma prefast(disable : 26018, "Only used with static internal arrays") -#endif - - uint32_t LookupByName(const wchar_t* pName, const SValue* pArray) - { - while (pArray->name) - { - if (!_wcsicmp(pName, pArray->name)) - return pArray->value; - - pArray++; - } - - return 0; - } - - void SearchForFiles(const std::filesystem::path& path, std::list& files, bool recursive) - { - // Process files - WIN32_FIND_DATAW findData = {}; - ScopedFindHandle hFile(safe_handle(FindFirstFileExW(path.c_str(), - FindExInfoBasic, &findData, - FindExSearchNameMatch, nullptr, - FIND_FIRST_EX_LARGE_FETCH))); - if (hFile) - { - for (;;) - { - if (!(findData.dwFileAttributes & (FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_DIRECTORY))) - { - SConversion conv = {}; - conv.szSrc = path.parent_path().append(findData.cFileName).native(); - files.push_back(conv); - } - - if (!FindNextFileW(hFile.get(), &findData)) - break; - } - } - - // Process directories - if (recursive) - { - auto searchDir = path.parent_path().append(L"*"); - - hFile.reset(safe_handle(FindFirstFileExW(searchDir.c_str(), - FindExInfoBasic, &findData, - FindExSearchLimitToDirectories, nullptr, - FIND_FIRST_EX_LARGE_FETCH))); - if (!hFile) - return; - - for (;;) - { - if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) - { - if (findData.cFileName[0] != L'.') - { - auto subdir = path.parent_path().append(findData.cFileName).append(path.filename().c_str()); - - SearchForFiles(subdir, files, recursive); - } - } - - if (!FindNextFileW(hFile.get(), &findData)) - break; - } - } - } - - void ProcessFileList(std::wifstream& inFile, std::list& files) - { - std::list flist; - std::set excludes; - - for (;;) - { - std::wstring fname; - std::getline(inFile, fname); - if (!inFile) - break; - - if (fname[0] == L'#') - { - // Comment - } - else if (fname[0] == L'-') - { - if (flist.empty()) - { - wprintf(L"WARNING: Ignoring the line '%ls' in -flist\n", fname.c_str()); - } - else - { - std::filesystem::path path(fname.c_str() + 1); - auto& npath = path.make_preferred(); - if (wcspbrk(fname.c_str(), L"?*") != nullptr) - { - std::list removeFiles; - SearchForFiles(npath, removeFiles, false); - - for (auto& it : removeFiles) - { - std::wstring name = it.szSrc; - std::transform(name.begin(), name.end(), name.begin(), towlower); - excludes.insert(name); - } - } - else - { - std::wstring name = npath.c_str(); - std::transform(name.begin(), name.end(), name.begin(), towlower); - excludes.insert(name); - } - } - } - else if (wcspbrk(fname.c_str(), L"?*") != nullptr) - { - std::filesystem::path path(fname.c_str()); - SearchForFiles(path.make_preferred(), flist, false); - } - else - { - SConversion conv = {}; - std::filesystem::path path(fname.c_str()); - conv.szSrc = path.make_preferred().native(); - flist.push_back(conv); - } - } - - inFile.close(); - - if (!excludes.empty()) - { - // Remove any excluded files - for (auto it = flist.begin(); it != flist.end();) - { - std::wstring name = it->szSrc; - std::transform(name.begin(), name.end(), name.begin(), towlower); - auto item = it; - ++it; - if (excludes.find(name) != excludes.end()) - { - flist.erase(item); - } - } - } - - if (flist.empty()) - { - wprintf(L"WARNING: No file names found in -flist\n"); - } - else - { - files.splice(files.end(), flist); - } - } - - void PrintList(size_t cch, const SValue* pValue) - { - while (pValue->name) - { - const size_t cchName = wcslen(pValue->name); - - if (cch + cchName + 2 >= 80) - { - wprintf(L"\n "); - cch = 6; - } - - wprintf(L"%ls ", pValue->name); - cch += cchName + 2; - pValue++; - } - - wprintf(L"\n"); - } - - void PrintLogo(bool versionOnly) - { - wchar_t version[32] = {}; - - wchar_t appName[_MAX_PATH] = {}; - if (GetModuleFileNameW(nullptr, appName, _MAX_PATH)) - { - const DWORD size = GetFileVersionInfoSizeW(appName, nullptr); - if (size > 0) - { - auto verInfo = std::make_unique(size); - if (GetFileVersionInfoW(appName, 0, size, verInfo.get())) - { - LPVOID lpstr = nullptr; - UINT strLen = 0; - if (VerQueryValueW(verInfo.get(), L"\\StringFileInfo\\040904B0\\ProductVersion", &lpstr, &strLen)) - { - wcsncpy_s(version, reinterpret_cast(lpstr), strLen); - } - } - } - } - - if (!*version || wcscmp(version, L"1.0.0.0") == 0) - { - swprintf_s(version, L"%03d (library)", DIRECTX_MESH_VERSION); - } - - if (versionOnly) - { - wprintf(L"meshconvert version %ls\n", version); - } - else - { - wprintf(L"Microsoft (R) MeshConvert Command-line Tool Version %ls\n", version); - wprintf(L"Copyright (C) Microsoft Corp.\n"); - #ifdef _DEBUG - wprintf(L"*** Debug build ***\n"); - #endif - wprintf(L"\n"); - } - } - void PrintUsage() { - PrintLogo(false); + PrintLogo(false, g_ToolName, g_Description); static const wchar_t* const s_usage = L"Usage: meshconvert [--] \n" @@ -464,43 +232,6 @@ namespace wprintf(L"\n : "); PrintList(13, g_vertexColorFormats); } - - const wchar_t* GetErrorDesc(HRESULT hr) - { - static wchar_t desc[1024] = {}; - - LPWSTR errorText = nullptr; - - const DWORD result = FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_ALLOCATE_BUFFER, - nullptr, static_cast(hr), - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), reinterpret_cast(&errorText), 0, nullptr); - - *desc = 0; - - if (result > 0 && errorText) - { - swprintf_s(desc, L": %ls", errorText); - - size_t len = wcslen(desc); - if (len >= 1) - { - desc[len - 1] = 0; - } - - if (errorText) - LocalFree(errorText); - - for (wchar_t* ptr = desc; *ptr != 0; ++ptr) - { - if (*ptr == L'\r' || *ptr == L'\n') - { - *ptr = L' '; - } - } - } - - return desc; - } } //-------------------------------------------------------------------------------------- @@ -541,7 +272,7 @@ int __cdecl wmain(_In_ int argc, _In_z_count_(argc) wchar_t* argv[]) } else if (!_wcsicmp(pArg, L"--version")) { - PrintLogo(true); + PrintLogo(true, g_ToolName, g_Description); return 0; } else if (!_wcsicmp(pArg, L"--help")) @@ -738,7 +469,7 @@ int __cdecl wmain(_In_ int argc, _In_z_count_(argc) wchar_t* argv[]) { const size_t count = conversion.size(); std::filesystem::path path(pArg); - SearchForFiles(path.make_preferred(), conversion, (dwOptions & (1 << OPT_RECURSIVE)) != 0); + SearchForFiles(path.make_preferred(), conversion, (dwOptions & (1 << OPT_RECURSIVE)) != 0, nullptr); if (conversion.size() <= count) { wprintf(L"No matching files found for %ls\n", pArg); @@ -767,7 +498,7 @@ int __cdecl wmain(_In_ int argc, _In_z_count_(argc) wchar_t* argv[]) } if (~dwOptions & (1 << OPT_NOLOGO)) - PrintLogo(false); + PrintLogo(false, g_ToolName, g_Description); // Process files for (auto pConv = conversion.begin(); pConv != conversion.end(); ++pConv) diff --git a/Meshconvert/Meshconvert_Desktop_2019.vcxproj b/Meshconvert/Meshconvert_Desktop_2019.vcxproj index 90f3c0d..6169f80 100644 --- a/Meshconvert/Meshconvert_Desktop_2019.vcxproj +++ b/Meshconvert/Meshconvert_Desktop_2019.vcxproj @@ -295,6 +295,7 @@ + diff --git a/Meshconvert/Meshconvert_Desktop_2019.vcxproj.filters b/Meshconvert/Meshconvert_Desktop_2019.vcxproj.filters index 5fb4723..140af0d 100644 --- a/Meshconvert/Meshconvert_Desktop_2019.vcxproj.filters +++ b/Meshconvert/Meshconvert_Desktop_2019.vcxproj.filters @@ -22,6 +22,7 @@ Wavefront OBJ + diff --git a/Meshconvert/Meshconvert_Desktop_2019_Win10.vcxproj b/Meshconvert/Meshconvert_Desktop_2019_Win10.vcxproj index 61b2196..f87b1cf 100644 --- a/Meshconvert/Meshconvert_Desktop_2019_Win10.vcxproj +++ b/Meshconvert/Meshconvert_Desktop_2019_Win10.vcxproj @@ -420,6 +420,7 @@ + diff --git a/Meshconvert/Meshconvert_Desktop_2019_Win10.vcxproj.filters b/Meshconvert/Meshconvert_Desktop_2019_Win10.vcxproj.filters index 5fb4723..140af0d 100644 --- a/Meshconvert/Meshconvert_Desktop_2019_Win10.vcxproj.filters +++ b/Meshconvert/Meshconvert_Desktop_2019_Win10.vcxproj.filters @@ -22,6 +22,7 @@ Wavefront OBJ + diff --git a/Meshconvert/Meshconvert_Desktop_2022.vcxproj b/Meshconvert/Meshconvert_Desktop_2022.vcxproj index 57d5b00..b74fb1e 100644 --- a/Meshconvert/Meshconvert_Desktop_2022.vcxproj +++ b/Meshconvert/Meshconvert_Desktop_2022.vcxproj @@ -295,6 +295,7 @@ + diff --git a/Meshconvert/Meshconvert_Desktop_2022.vcxproj.filters b/Meshconvert/Meshconvert_Desktop_2022.vcxproj.filters index 5fb4723..140af0d 100644 --- a/Meshconvert/Meshconvert_Desktop_2022.vcxproj.filters +++ b/Meshconvert/Meshconvert_Desktop_2022.vcxproj.filters @@ -22,6 +22,7 @@ Wavefront OBJ + diff --git a/Meshconvert/Meshconvert_Desktop_2022_Win10.vcxproj b/Meshconvert/Meshconvert_Desktop_2022_Win10.vcxproj index 436a888..b97ff38 100644 --- a/Meshconvert/Meshconvert_Desktop_2022_Win10.vcxproj +++ b/Meshconvert/Meshconvert_Desktop_2022_Win10.vcxproj @@ -420,6 +420,7 @@ + diff --git a/Meshconvert/Meshconvert_Desktop_2022_Win10.vcxproj.filters b/Meshconvert/Meshconvert_Desktop_2022_Win10.vcxproj.filters index 5fb4723..140af0d 100644 --- a/Meshconvert/Meshconvert_Desktop_2022_Win10.vcxproj.filters +++ b/Meshconvert/Meshconvert_Desktop_2022_Win10.vcxproj.filters @@ -22,6 +22,7 @@ Wavefront OBJ +