Init Repo

This commit is contained in:
2026-06-03 09:00:11 +08:00
commit 9be48d8b9e
155 changed files with 14827 additions and 0 deletions
+59
View File
@@ -0,0 +1,59 @@
#include "FileDialog.h"
#include "Logger.h"
#include <commdlg.h>
#include <vector>
namespace UsdLayerManager {
std::string FileDialog::OpenFile(const char* filter, const char* title, HWND owner) {
std::vector<char> filename(MAX_PATH_LENGTH, 0);
OPENFILENAMEA ofn = {};
ofn.lStructSize = sizeof(OPENFILENAMEA);
ofn.hwndOwner = owner;
ofn.lpstrFilter = filter;
ofn.lpstrFile = filename.data();
ofn.nMaxFile = MAX_PATH_LENGTH;
ofn.lpstrTitle = title;
ofn.Flags = OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST | OFN_NOCHANGEDIR;
if (GetOpenFileNameA(&ofn)) {
return std::string(filename.data());
}
// User cancelled or error occurred
DWORD error = CommDlgExtendedError();
if (error != 0) {
LOG_ERROR("File dialog error code: " + std::to_string(error));
}
return "";
}
std::string FileDialog::SaveFile(const char* filter, const char* title, const char* defaultExt, HWND owner) {
std::vector<char> filename(MAX_PATH_LENGTH, 0);
OPENFILENAMEA ofn = {};
ofn.lStructSize = sizeof(OPENFILENAMEA);
ofn.hwndOwner = owner;
ofn.lpstrFilter = filter;
ofn.lpstrFile = filename.data();
ofn.nMaxFile = MAX_PATH_LENGTH;
ofn.lpstrTitle = title;
ofn.lpstrDefExt = defaultExt;
ofn.Flags = OFN_OVERWRITEPROMPT | OFN_NOCHANGEDIR;
if (GetSaveFileNameA(&ofn)) {
return std::string(filename.data());
}
// User cancelled or error occurred
DWORD error = CommDlgExtendedError();
if (error != 0) {
LOG_ERROR("File dialog error code: " + std::to_string(error));
}
return "";
}
} // namespace UsdLayerManager
+29
View File
@@ -0,0 +1,29 @@
#pragma once
#include <string>
#include <Windows.h>
namespace UsdLayerManager {
class FileDialog {
public:
// Open file dialog
static std::string OpenFile(
const char* filter = "USD Files (*.usd;*.usda;*.usdc)\0*.usd;*.usda;*.usdc\0All Files (*.*)\0*.*\0",
const char* title = "Open USD File",
HWND owner = nullptr
);
// Save file dialog
static std::string SaveFile(
const char* filter = "USD Files (*.usd;*.usda;*.usdc)\0*.usd;*.usda;*.usdc\0All Files (*.*)\0*.*\0",
const char* title = "Save USD File",
const char* defaultExt = "usd",
HWND owner = nullptr
);
private:
static const int MAX_PATH_LENGTH = 4096;
};
} // namespace UsdLayerManager
+47
View File
@@ -0,0 +1,47 @@
#include "GLExt.h"
#include "Logger.h"
#ifdef _WIN32
# include <Windows.h>
#endif
namespace UsdLayerManager {
namespace GL {
#ifdef _WIN32
// wglGetProcAddress only resolves extension / ARB functions.
// Core functions (OpenGL 1.x) live in opengl32.dll and must be
// fetched via GetProcAddress. This two-stage loader covers both.
static GLADapiproc WinGLLoader(const char* name) {
GLADapiproc proc = reinterpret_cast<GLADapiproc>(wglGetProcAddress(name));
if (!proc) {
HMODULE hMod = GetModuleHandleA("opengl32.dll");
if (hMod) {
proc = reinterpret_cast<GLADapiproc>(GetProcAddress(hMod, name));
}
}
return proc;
}
#endif
bool InitExtensions() {
// Must be called after wglMakeCurrent so the context is current.
#ifdef _WIN32
int version = gladLoadGL(WinGLLoader);
#else
int version = 0; // supply a platform loader for non-Windows
#endif
if (version == 0) {
LOG_ERROR("gladLoadGL failed - could not load OpenGL functions");
return false;
}
LOG_INFO("OpenGL loaded via glad (GL "
+ std::to_string(GLAD_VERSION_MAJOR(version)) + "."
+ std::to_string(GLAD_VERSION_MINOR(version)) + ")");
return true;
}
} // namespace GL
} // namespace UsdLayerManager
+15
View File
@@ -0,0 +1,15 @@
#pragma once
// glad must be included before any other OpenGL headers.
// It provides all GL core 3.3 functions and constants.
#include <glad/gl.h>
namespace UsdLayerManager {
namespace GL {
// Initializes the glad OpenGL function loader.
// Must be called after a valid OpenGL context has been made current.
bool InitExtensions();
} // namespace GL
} // namespace UsdLayerManager
+96
View File
@@ -0,0 +1,96 @@
#include "Logger.h"
#include <iomanip>
namespace UsdLayerManager {
Logger& Logger::Instance() {
static Logger instance;
return instance;
}
Logger::Logger()
: m_logLevel(LogLevel::Info) {
}
Logger::~Logger() {
if (m_logFile.is_open()) {
m_logFile.close();
}
}
void Logger::SetLogLevel(LogLevel level) {
std::lock_guard<std::mutex> lock(m_mutex);
m_logLevel = level;
}
void Logger::SetLogFile(const std::string& filename) {
std::lock_guard<std::mutex> lock(m_mutex);
if (m_logFile.is_open()) {
m_logFile.close();
}
m_logFile.open(filename, std::ios::out | std::ios::app);
if (!m_logFile.is_open()) {
std::cerr << "Failed to open log file: " << filename << std::endl;
}
}
void Logger::Debug(const std::string& message) {
Log(LogLevel::Debug, message);
}
void Logger::Info(const std::string& message) {
Log(LogLevel::Info, message);
}
void Logger::Warning(const std::string& message) {
Log(LogLevel::Warning, message);
}
void Logger::Error(const std::string& message) {
Log(LogLevel::Error, message);
}
void Logger::Log(LogLevel level, const std::string& message) {
if (level < m_logLevel) {
return;
}
std::lock_guard<std::mutex> lock(m_mutex);
std::string timestamp = GetTimestamp();
std::string levelStr = LevelToString(level);
std::string logMessage = "[" + timestamp + "] [" + levelStr + "] " + message;
// Output to console
if (level == LogLevel::Error) {
std::cerr << logMessage << std::endl;
} else {
std::cout << logMessage << std::endl;
}
// Output to file if open
if (m_logFile.is_open()) {
m_logFile << logMessage << std::endl;
m_logFile.flush();
}
}
std::string Logger::GetTimestamp() {
auto now = std::time(nullptr);
auto tm = *std::localtime(&now);
std::ostringstream oss;
oss << std::put_time(&tm, "%Y-%m-%d %H:%M:%S");
return oss.str();
}
std::string Logger::LevelToString(LogLevel level) {
switch (level) {
case LogLevel::Debug: return "DEBUG";
case LogLevel::Info: return "INFO";
case LogLevel::Warning: return "WARNING";
case LogLevel::Error: return "ERROR";
default: return "UNKNOWN";
}
}
} // namespace UsdLayerManager
+87
View File
@@ -0,0 +1,87 @@
#pragma once
#include <string>
#include <iostream>
#include <sstream>
#include <fstream>
#include <mutex>
#include <ctime>
namespace UsdLayerManager {
enum class LogLevel {
Debug,
Info,
Warning,
Error
};
class Logger {
public:
static Logger& Instance();
void SetLogLevel(LogLevel level);
void SetLogFile(const std::string& filename);
void Debug(const std::string& message);
void Info(const std::string& message);
void Warning(const std::string& message);
void Error(const std::string& message);
template<typename... Args>
void Debug(const std::string& format, Args... args) {
Log(LogLevel::Debug, FormatString(format, args...));
}
template<typename... Args>
void Info(const std::string& format, Args... args) {
Log(LogLevel::Info, FormatString(format, args...));
}
template<typename... Args>
void Warning(const std::string& format, Args... args) {
Log(LogLevel::Warning, FormatString(format, args...));
}
template<typename... Args>
void Error(const std::string& format, Args... args) {
Log(LogLevel::Error, FormatString(format, args...));
}
private:
Logger();
~Logger();
Logger(const Logger&) = delete;
Logger& operator=(const Logger&) = delete;
void Log(LogLevel level, const std::string& message);
std::string GetTimestamp();
std::string LevelToString(LogLevel level);
template<typename T>
std::string FormatString(const std::string& format, T value) {
std::ostringstream oss;
oss << format << value;
return oss.str();
}
template<typename T, typename... Args>
std::string FormatString(const std::string& format, T value, Args... args) {
std::ostringstream oss;
oss << format << value;
return FormatString(oss.str(), args...);
}
LogLevel m_logLevel;
std::ofstream m_logFile;
std::mutex m_mutex;
};
// Convenience macros
#define LOG_DEBUG(msg) UsdLayerManager::Logger::Instance().Debug(msg)
#define LOG_INFO(msg) UsdLayerManager::Logger::Instance().Info(msg)
#define LOG_WARNING(msg) UsdLayerManager::Logger::Instance().Warning(msg)
#define LOG_ERROR(msg) UsdLayerManager::Logger::Instance().Error(msg)
} // namespace UsdLayerManager
+53
View File
@@ -0,0 +1,53 @@
#include "PathUtils.h"
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#else
#include <unistd.h>
#include <limits.h>
#endif
#include <algorithm>
namespace UsdLayerManager {
std::string ExeDir()
{
#ifdef _WIN32
wchar_t wpath[MAX_PATH] = {};
DWORD len = ::GetModuleFileNameW(nullptr, wpath, MAX_PATH);
if (len == 0 || len >= MAX_PATH)
return "./";
// Convert UTF-16 → UTF-8
int needed = ::WideCharToMultiByte(CP_UTF8, 0, wpath, static_cast<int>(len),
nullptr, 0, nullptr, nullptr);
std::string path(static_cast<size_t>(needed), '\0');
::WideCharToMultiByte(CP_UTF8, 0, wpath, static_cast<int>(len),
path.data(), needed, nullptr, nullptr);
#else
char buf[PATH_MAX] = {};
ssize_t len = ::readlink("/proc/self/exe", buf, sizeof(buf) - 1);
if (len <= 0)
return "./";
std::string path(buf, static_cast<size_t>(len));
#endif
// Normalise to forward slashes and strip the filename
std::replace(path.begin(), path.end(), '\\', '/');
auto slash = path.rfind('/');
if (slash != std::string::npos)
path = path.substr(0, slash + 1); // keep trailing '/'
else
path = "./";
return path;
}
std::string ResourcePath(const std::string& relativePath)
{
return ExeDir() + relativePath;
}
} // namespace UsdLayerManager
+18
View File
@@ -0,0 +1,18 @@
#pragma once
#include <string>
namespace UsdLayerManager {
/// Return the directory that contains the running executable, with a
/// trailing path separator. On Windows this uses GetModuleFileNameW.
/// Falls back to "./" if the path cannot be determined.
///
/// Usage:
/// std::string iconDir = ExeDir() + "resources/icons";
std::string ExeDir();
/// Concatenate the exe directory with a relative path.
/// ResourcePath("resources/icons") == ExeDir() + "resources/icons"
std::string ResourcePath(const std::string& relativePath);
} // namespace UsdLayerManager