diff --git a/CMakeLists.txt b/CMakeLists.txt index 83fb92a6..d361e601 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,7 +16,7 @@ message(STATUS "Using Compiler: ${CMAKE_CXX_COMPILER_ID} ${CMAKE_CXX_COMPILER_VE message(STATUS "Compiler at path:" ${CMAKE_CXX_COMPILER}) add_compile_definitions( - ATLAS_VERSION="Alpha 9" + ATLAS_VERSION="Release Candidate for Beta 1" OPAL_VERSION="3 Tetrahedron" ) diff --git a/editor/views/editor/editor.cpp b/editor/views/editor/editor.cpp index f90a3971..838363de 100644 --- a/editor/views/editor/editor.cpp +++ b/editor/views/editor/editor.cpp @@ -138,8 +138,8 @@ bool copyExportPath(const QString &sourcePath, const QString &destinationPath, if (source.isDir()) { if (!QDir().mkpath(destinationPath)) { if (error != nullptr) - *error = QStringLiteral("Could not create %1") - .arg(destinationPath); + *error = + QStringLiteral("Could not create %1").arg(destinationPath); return false; } QDir sourceDirectory(sourcePath); @@ -217,10 +217,10 @@ void setTomlValue(QStringList *lines, const QString §ion, } lines->insert(end, QStringLiteral("%1 = %2").arg(key, value)); } -} +} // namespace #ifndef ATLAS_VERSION -#define ATLAS_VERSION "Alpha 9" +#define ATLAS_VERSION "No version" #endif #ifndef ATLAS_BUILD_STRING @@ -241,7 +241,8 @@ EditorWindow::EditorWindow(const QString &projectFile, QWidget *parent) void EditorWindow::setupWindow() { const auto project = ProjectStore::projectInfo(projectFile); - projectName = project.has_value() ? project->name : QStringLiteral("Project"); + projectName = + project.has_value() ? project->name : QStringLiteral("Project"); updateWindowTitle(false); setMinimumSize(1100, 700); resize(1440, 900); @@ -290,10 +291,9 @@ void EditorWindow::setupMenus() { QAction *action = menu->addAction(name, this, handler); action->setIcon(commandIcon(name)); if (!shortcut.isEmpty()) { - const bool plainShift = shortcut.startsWith("Shift+") && - !shortcut.contains("Meta+") && - !shortcut.contains("Alt+") && - !shortcut.contains("Ctrl+"); + const bool plainShift = + shortcut.startsWith("Shift+") && !shortcut.contains("Meta+") && + !shortcut.contains("Alt+") && !shortcut.contains("Ctrl+"); if (plainShift) { action->setProperty("atlasShortcut", shortcut); } else { @@ -308,8 +308,7 @@ void EditorWindow::setupMenus() { addCommand(fileMenu, "New Scene", "Meta+N", [this] { createScene(); }); addCommand(fileMenu, "Open Scene…", "Meta+O", [this] { openScene(); }); auto *saveAction = fileMenu->addAction("Save Scene"); - saveAction->setIcon( - styling::icon(styling::Icon::FloppyDisk, "#A1957D")); + saveAction->setIcon(styling::icon(styling::Icon::FloppyDisk, "#A1957D")); saveAction->setShortcut(QKeySequence::Save); saveAction->setShortcutContext(Qt::ApplicationShortcut); connect(saveAction, &QAction::triggered, this, [this] { @@ -346,7 +345,8 @@ void EditorWindow::setupMenus() { undoAction->setShortcut(QKeySequence::Undo); undoAction->setShortcutContext(Qt::ApplicationShortcut); connect(undoAction, &QAction::triggered, this, [this] { - if (auto *field = qobject_cast(QApplication::focusWidget())) { + if (auto *field = + qobject_cast(QApplication::focusWidget())) { field->undo(); } else if (materialEditorPanel != nullptr && materialEditorPanel->isAncestorOf( @@ -362,7 +362,8 @@ void EditorWindow::setupMenus() { redoAction->setShortcut(QKeySequence::Redo); redoAction->setShortcutContext(Qt::ApplicationShortcut); connect(redoAction, &QAction::triggered, this, [this] { - if (auto *field = qobject_cast(QApplication::focusWidget())) { + if (auto *field = + qobject_cast(QApplication::focusWidget())) { field->redo(); } else if (materialEditorPanel != nullptr && materialEditorPanel->isAncestorOf( @@ -373,11 +374,11 @@ void EditorWindow::setupMenus() { } }); editMenu->addSeparator(); - addCommand(editMenu, "Find…", "Meta+F", - [this] { showGlobalSearch(); }); + addCommand(editMenu, "Find…", "Meta+F", [this] { showGlobalSearch(); }); editMenu->addSeparator(); addCommand(editMenu, "Cut", "Meta+X", [this] { - if (auto *field = qobject_cast(QApplication::focusWidget())) + if (auto *field = + qobject_cast(QApplication::focusWidget())) field->cut(); else if (contentBrowserHasFocus()) contentBrowser->cutSelection(); @@ -385,7 +386,8 @@ void EditorWindow::setupMenus() { viewportPanel->cutSelectedRuntimeObject(); }); addCommand(editMenu, "Copy", "Meta+C", [this] { - if (auto *field = qobject_cast(QApplication::focusWidget())) + if (auto *field = + qobject_cast(QApplication::focusWidget())) field->copy(); else if (contentBrowserHasFocus()) contentBrowser->copySelection(); @@ -393,7 +395,8 @@ void EditorWindow::setupMenus() { viewportPanel->copySelectedRuntimeObject(); }); addCommand(editMenu, "Paste", "Meta+V", [this] { - if (auto *field = qobject_cast(QApplication::focusWidget())) + if (auto *field = + qobject_cast(QApplication::focusWidget())) field->paste(); else if (contentBrowserHasFocus()) contentBrowser->pasteSelection(); @@ -407,7 +410,8 @@ void EditorWindow::setupMenus() { viewportPanel->duplicateSelectedRuntimeObject(); }); addCommand(editMenu, "Delete", "Backspace", [this] { - if (auto *field = qobject_cast(QApplication::focusWidget())) + if (auto *field = + qobject_cast(QApplication::focusWidget())) field->backspace(); else if (contentBrowserHasFocus()) contentBrowser->deleteSelection(); @@ -415,7 +419,8 @@ void EditorWindow::setupMenus() { hierarchyPanel->deleteSelectedObject(); }); addCommand(editMenu, "Select All Objects", "Meta+A", [this] { - if (auto *field = qobject_cast(QApplication::focusWidget())) + if (auto *field = + qobject_cast(QApplication::focusWidget())) field->selectAll(); else if (contentBrowserHasFocus() && contentBrowser != nullptr) contentBrowser->selectAllAssets(); @@ -427,9 +432,8 @@ void EditorWindow::setupMenus() { hierarchyPanel->deselectAllObjects(); }); editMenu->addSeparator(); - auto *settingsAction = - addCommand(editMenu, "Project Settings…", "Meta+,", - [this] { showProjectSettings(); }); + auto *settingsAction = addCommand(editMenu, "Project Settings…", "Meta+,", + [this] { showProjectSettings(); }); settingsAction->setMenuRole(QAction::PreferencesRole); auto *objectMenu = menuBar()->addMenu("Object"); @@ -482,8 +486,7 @@ void EditorWindow::setupMenus() { viewMenu = menuBar()->addMenu("View"); auto *resetLayoutAction = viewMenu->addAction("Reset Layout"); - resetLayoutAction->setIcon( - styling::icon(styling::Icon::Layout, "#8498A8")); + resetLayoutAction->setIcon(styling::icon(styling::Icon::Layout, "#8498A8")); connect(resetLayoutAction, &QAction::triggered, this, [this] { if (coreManager != nullptr && !defaultDockState.isEmpty()) { restoringLayout = true; @@ -547,9 +550,8 @@ void EditorWindow::setupMenus() { [this] { takeViewportScreenshot(); }); auto *toolsMenu = menuBar()->addMenu("Tools"); - auto *toolsSettings = - addCommand(toolsMenu, "Project Settings…", QString(), - [this] { showProjectSettings(); }); + auto *toolsSettings = addCommand(toolsMenu, "Project Settings…", QString(), + [this] { showProjectSettings(); }); toolsSettings->setMenuRole(QAction::NoRole); addCommand(toolsMenu, "Input Actions…", QString(), [this] { showInputActions(); }); @@ -560,8 +562,9 @@ void EditorWindow::setupMenus() { windowMenu = menuBar()->addMenu("Window"); windowMenu->addAction("Minimize", this, &QWidget::showMinimized); - windowMenu->addAction("Zoom", this, - [this] { isMaximized() ? showNormal() : showMaximized(); }); + windowMenu->addAction("Zoom", this, [this] { + isMaximized() ? showNormal() : showMaximized(); + }); auto *helpMenu = menuBar()->addMenu("Help"); auto *aboutAction = helpMenu->addAction("About Atlas Engine", this, [this] { @@ -637,8 +640,8 @@ void EditorWindow::setupDocks() { workspaceDock->setAsCurrentTab(); defaultDockState = coreManager->saveState(DockStateVersion); - const QList managedDocks{ - workspaceDock, hierarchyDock, inspectorDock, contentDock}; + const QList managedDocks{workspaceDock, hierarchyDock, + inspectorDock, contentDock}; for (ads::CDockWidget *dock : managedDocks) { connect(dock, &ads::CDockWidget::topLevelChanged, this, [this](bool) { scheduleLayoutSave(); }); @@ -660,7 +663,8 @@ void EditorWindow::setupDocks() { } windowMenu->addSeparator(); const QList> panels{ - {"Workspace", workspaceDock}, {"Hierarchy", hierarchyDock}, + {"Workspace", workspaceDock}, + {"Hierarchy", hierarchyDock}, {"Inspector", inspectorDock}, {"Content Browser", contentDock}}; for (int index = 0; index < panels.size(); ++index) { @@ -672,8 +676,8 @@ void EditorWindow::setupDocks() { dock->raise(); }); action->setIcon(dock->icon()); - action->setShortcut(QKeySequence( - QStringLiteral("Ctrl+%1").arg(index + 1))); + action->setShortcut( + QKeySequence(QStringLiteral("Ctrl+%1").arg(index + 1))); action->setShortcutContext(Qt::ApplicationShortcut); } windowMenu->addSeparator(); @@ -688,14 +692,11 @@ void EditorWindow::setupDocks() { workspaceDock->setAsCurrentTab(); workspaceDock->raise(); }); - action->setIcon(index == 0 - ? styling::icon(styling::Icon::CubeFocus, - "#7E929C") - : index == 1 - ? styling::icon(styling::Icon::Material, - "#A1957D") - : styling::icon(styling::Icon::FilmStrip, - "#849589")); + action->setIcon( + index == 0 ? styling::icon(styling::Icon::CubeFocus, "#7E929C") + : index == 1 + ? styling::icon(styling::Icon::Material, "#A1957D") + : styling::icon(styling::Icon::FilmStrip, "#849589")); } } @@ -824,22 +825,23 @@ void EditorWindow::setupWorkspaceBar() { renderer->setObjectName("statusRenderer"); const auto projectInfo = ProjectStore::projectInfo(projectFile); renderer->setText(projectInfo.has_value() ? projectInfo->renderer - : QStringLiteral("ATLAS")); + : QStringLiteral("ATLAS")); auto *version = new QLabel(QStringLiteral(ATLAS_VERSION), statusBar()); version->setObjectName("statusVersion"); statusBar()->addPermanentWidget(runtimeIcon); statusBar()->addPermanentWidget(renderer); statusBar()->addPermanentWidget(version); - connect(viewportPanel, &ViewportPanel::runtimeAvailabilityChanged, this, - [this, runtimeIcon](bool available) { - runtimeIcon->setPixmap(styling::icon( - available ? styling::Icon::Check : styling::Icon::Warning, - available ? QColor("#849589") : QColor("#A1957D")) - .pixmap(14, 14)); - statusBar()->showMessage(available ? "Runtime ready" - : "Runtime unavailable", - 3000); - }); + connect( + viewportPanel, &ViewportPanel::runtimeAvailabilityChanged, this, + [this, runtimeIcon](bool available) { + runtimeIcon->setPixmap( + styling::icon(available ? styling::Icon::Check + : styling::Icon::Warning, + available ? QColor("#849589") : QColor("#A1957D")) + .pixmap(14, 14)); + statusBar()->showMessage( + available ? "Runtime ready" : "Runtime unavailable", 3000); + }); } void EditorWindow::activateWorkspace(int index) { @@ -864,22 +866,23 @@ void EditorWindow::createScene() { path += ".ascene"; const QString name = QFileInfo(path).completeBaseName(); const QByteArray contents = - QJsonDocument(QJsonObject{ - {"name", name}, - {"id", name.toLower().replace(' ', '_')}, - {"objects", QJsonArray{}}, - {"lights", QJsonArray{}}, - {"camera", QJsonObject{{"position", QJsonArray{0.0, 1.5, -5.0}}, - {"target", QJsonArray{0.0, 0.0, 0.0}}, - {"fov", 60.0}}}, - {"targets", QJsonArray{QJsonObject{{"name", "Main Target"}, - {"type", "scene"}, - {"render", true}, - {"display", true}}}}}) + QJsonDocument( + QJsonObject{ + {"name", name}, + {"id", name.toLower().replace(' ', '_')}, + {"objects", QJsonArray{}}, + {"lights", QJsonArray{}}, + {"camera", QJsonObject{{"position", QJsonArray{0.0, 1.5, -5.0}}, + {"target", QJsonArray{0.0, 0.0, 0.0}}, + {"fov", 60.0}}}, + {"targets", QJsonArray{QJsonObject{{"name", "Main Target"}, + {"type", "scene"}, + {"render", true}, + {"display", true}}}}}) .toJson(QJsonDocument::Indented); QSaveFile file(path); - if (!file.open(QIODevice::WriteOnly) || file.write(contents) != contents.size() || - !file.commit()) { + if (!file.open(QIODevice::WriteOnly) || + file.write(contents) != contents.size() || !file.commit()) { QMessageBox::warning(this, "Create Scene", "The scene could not be created."); return; @@ -933,9 +936,9 @@ void EditorWindow::showProjectSettings() { auto *headerCopy = new QVBoxLayout(); auto *headerTitle = new QLabel("Project Settings", header); headerTitle->setObjectName("dialogHeroTitle"); - auto *headerSubtitle = new QLabel( - "Configure runtime, rendering, controls, and packaging for this project.", - header); + auto *headerSubtitle = new QLabel("Configure runtime, rendering, controls, " + "and packaging for this project.", + header); headerSubtitle->setObjectName("dialogHeroSubtitle"); headerCopy->addWidget(headerTitle); headerCopy->addWidget(headerSubtitle); @@ -993,46 +996,56 @@ void EditorWindow::showProjectSettings() { general->addRow("Window width", windowWidth); general->addRow("Window height", windowHeight); general->addRow(QString(), fullscreen); - auto *rendering = - addPage("Rendering", styling::Icon::Aperture, "#9E897D"); + auto *rendering = addPage("Rendering", styling::Icon::Aperture, "#9E897D"); auto *renderer = new QComboBox(&dialog); renderer->addItems({"PBR", "PBR + DDGI", "Path Tracing"}); - renderer->setCurrentText(settings.value("project/renderer", "PBR").toString()); + renderer->setCurrentText( + settings.value("project/renderer", "PBR").toString()); auto *frameLimit = new QSpinBox(&dialog); frameLimit->setRange(0, 1000); frameLimit->setValue(settings.value("project/frameLimit", 0).toInt()); rendering->addRow("Renderer", renderer); rendering->addRow("Frame limit (0 = unlimited)", frameLimit); auto *physics = addPage("Physics", styling::Icon::Wrench, "#A1957D"); - auto *gravity = new QLineEdit(settings.value("project/gravity", "0, -9.81, 0").toString(), &dialog); - auto *fixedStep = new QLineEdit(settings.value("project/fixedStep", "0.0166667").toString(), &dialog); + auto *gravity = new QLineEdit( + settings.value("project/gravity", "0, -9.81, 0").toString(), &dialog); + auto *fixedStep = new QLineEdit( + settings.value("project/fixedStep", "0.0166667").toString(), &dialog); physics->addRow("Gravity", gravity); physics->addRow("Fixed timestep", fixedStep); - auto *input = - addPage("Input", styling::Icon::GameController, "#849589"); - auto *inputMap = new QLineEdit(settings.value("project/inputMap", "input.json").toString(), &dialog); + auto *input = addPage("Input", styling::Icon::GameController, "#849589"); + auto *inputMap = new QLineEdit( + settings.value("project/inputMap", "input.json").toString(), &dialog); auto *controller = new QComboBox(&dialog); controller->addItems({"Automatic", "Keyboard + Mouse", "Gamepad"}); - controller->setCurrentText(settings.value("project/controller", "Automatic").toString()); + controller->setCurrentText( + settings.value("project/controller", "Automatic").toString()); input->addRow("Input map", inputMap); input->addRow("Primary controller", controller); auto *build = addPage("Build & Run", styling::Icon::Package, "#A1957D"); - auto *buildCommand = new QLineEdit(settings.value("project/buildCommand", "atlas pack --backend METAL").toString(), &dialog); - auto *runCommand = new QLineEdit(settings.value("project/runCommand", "atlas run project.atlas").toString(), &dialog); + auto *buildCommand = new QLineEdit( + settings.value("project/buildCommand", "atlas pack --backend METAL") + .toString(), + &dialog); + auto *runCommand = new QLineEdit( + settings.value("project/runCommand", "atlas run project.atlas") + .toString(), + &dialog); build->addRow("Build command", buildCommand); build->addRow("Run command", runCommand); auto *editor = addPage("Editor", styling::Icon::Layout, "#7E929C"); auto *autosave = new QSpinBox(&dialog); autosave->setRange(0, 120); autosave->setValue(settings.value("project/autosaveMinutes", 5).toInt()); - auto *snap = new QLineEdit(settings.value("project/snapIncrement", "0.5").toString(), &dialog); + auto *snap = new QLineEdit( + settings.value("project/snapIncrement", "0.5").toString(), &dialog); editor->addRow("Autosave interval (minutes)", autosave); editor->addRow("Transform snapping", snap); - auto *packaging = - addPage("Packaging", styling::Icon::Export, "#8498A8"); + auto *packaging = addPage("Packaging", styling::Icon::Export, "#8498A8"); auto *identifier = new QLineEdit( - settings.value("project/bundleIdentifier", - "org.atlasengine." + projectName.toLower().replace(' ', '-')) + settings + .value("project/bundleIdentifier", + "org.atlasengine." + projectName.toLower().replace(' ', '-')) .toString(), &dialog); auto *iconPath = new QLineEdit( @@ -1076,8 +1089,7 @@ void EditorWindow::showProjectSettings() { settings.sync(); QFile manifest(projectFile); if (manifest.open(QIODevice::ReadOnly | QIODevice::Text)) { - QStringList lines = - QString::fromUtf8(manifest.readAll()).split('\n'); + QStringList lines = QString::fromUtf8(manifest.readAll()).split('\n'); manifest.close(); setTomlValue(&lines, QString(), "backend", tomlQuoted(backend->currentText())); @@ -1087,23 +1099,20 @@ void EditorWindow::showProjectSettings() { tomlQuoted(identifier->text())); setTomlValue(&lines, "pack", "version", tomlQuoted(gameVersion->text())); - setTomlValue(&lines, "pack", "icon", - tomlQuoted(iconPath->text())); - setTomlValue( - &lines, "window", "dimensions", - QStringLiteral("[%1, %2]") - .arg(windowWidth->value()) - .arg(windowHeight->value())); + setTomlValue(&lines, "pack", "icon", tomlQuoted(iconPath->text())); + setTomlValue(&lines, "window", "dimensions", + QStringLiteral("[%1, %2]") + .arg(windowWidth->value()) + .arg(windowHeight->value())); setTomlValue(&lines, "window", "fullscreen", fullscreen->isChecked() ? "true" : "false"); - const QString rendererName = - renderer->currentText() == "Path Tracing" ? "pathtracing" - : "deferred"; - setTomlValue(&lines, "renderer", "default", - tomlQuoted(rendererName)); + const QString rendererName = renderer->currentText() == "Path Tracing" + ? "pathtracing" + : "deferred"; + setTomlValue(&lines, "renderer", "default", tomlQuoted(rendererName)); setTomlValue(&lines, "renderer", "global_illumination", renderer->currentText() == "PBR + DDGI" ? "true" - : "false"); + : "false"); QSaveFile outputFile(projectFile); const QByteArray contents = lines.join('\n').toUtf8(); if (!outputFile.open(QIODevice::WriteOnly) || @@ -1155,23 +1164,21 @@ void EditorWindow::showExportDialog() { const QString settingsDirectory = QDir(QFileInfo(projectFile).absolutePath()).filePath(".atlas"); QDir().mkpath(settingsDirectory); - QSettings settings( - QDir(settingsDirectory).filePath("project-settings.ini"), - QSettings::IniFormat); + QSettings settings(QDir(settingsDirectory).filePath("project-settings.ini"), + QSettings::IniFormat); auto *backend = new QComboBox(&dialog); backend->addItems({"METAL", "VULKAN", "OPENGL"}); backend->setCurrentText( settings.value("project/exportBackend", "METAL").toString()); auto *output = new QLineEdit( settings - .value("project/exportDirectory", - QDir(QFileInfo(projectFile).absolutePath()) - .filePath("Exports")) + .value( + "project/exportDirectory", + QDir(QFileInfo(projectFile).absolutePath()).filePath("Exports")) .toString(), &dialog); auto *browse = new QPushButton("Choose…", &dialog); - browse->setIcon( - styling::icon(styling::Icon::FolderOpen, "#7E929C")); + browse->setIcon(styling::icon(styling::Icon::FolderOpen, "#7E929C")); auto *outputRow = new QWidget(&dialog); auto *outputLayout = new QHBoxLayout(outputRow); outputLayout->setContentsMargins(0, 0, 0, 0); @@ -1182,9 +1189,10 @@ void EditorWindow::showExportDialog() { form->addRow("Backend", backend); form->addRow("Destination", outputRow); layout->addLayout(form); - auto *summary = new QLabel( - "Atlas will save the current scene and package the configured runtime with the project resources.", - &dialog); + auto *summary = + new QLabel("Atlas will save the current scene and package the " + "configured runtime with the project resources.", + &dialog); summary->setObjectName("exportSummary"); summary->setWordWrap(true); layout->addWidget(summary); @@ -1198,12 +1206,13 @@ void EditorWindow::showExportDialog() { log->setPlaceholderText("Packaging output will appear here."); layout->addWidget(log, 1); auto *buttons = new QDialogButtonBox(QDialogButtonBox::Cancel, &dialog); - auto *exportButton = buttons->addButton("Export", QDialogButtonBox::AcceptRole); + auto *exportButton = + buttons->addButton("Export", QDialogButtonBox::AcceptRole); exportButton->setIcon( styling::icon(styling::Icon::RocketLaunch, "#849589")); - auto *revealButton = buttons->addButton("Reveal Export", QDialogButtonBox::ActionRole); - revealButton->setIcon( - styling::icon(styling::Icon::FolderOpen, "#7E929C")); + auto *revealButton = + buttons->addButton("Reveal Export", QDialogButtonBox::ActionRole); + revealButton->setIcon(styling::icon(styling::Icon::FolderOpen, "#7E929C")); revealButton->setEnabled(false); layout->addWidget(buttons); connect(browse, &QPushButton::clicked, &dialog, [&dialog, output] { @@ -1217,95 +1226,99 @@ void EditorWindow::showExportDialog() { QDesktopServices::openUrl(QUrl::fromLocalFile(output->text())); }); auto *process = new QProcess(&dialog); - connect(process, &QProcess::readyReadStandardOutput, &dialog, - [process, log] { - log->appendPlainText( - QString::fromUtf8(process->readAllStandardOutput()).trimmed()); - }); - connect(process, &QProcess::readyReadStandardError, &dialog, - [process, log] { - log->appendPlainText( - QString::fromUtf8(process->readAllStandardError()).trimmed()); - }); + connect( + process, &QProcess::readyReadStandardOutput, &dialog, [process, log] { + log->appendPlainText( + QString::fromUtf8(process->readAllStandardOutput()).trimmed()); + }); + connect( + process, &QProcess::readyReadStandardError, &dialog, [process, log] { + log->appendPlainText( + QString::fromUtf8(process->readAllStandardError()).trimmed()); + }); connect(process, &QProcess::errorOccurred, &dialog, [process, progress, exportButton, log](QProcess::ProcessError) { progress->setVisible(false); exportButton->setEnabled(true); log->appendPlainText(process->errorString()); }); - connect(process, &QProcess::finished, &dialog, - [this, process, progress, exportButton, revealButton, output, - log](int exitCode, QProcess::ExitStatus status) { - progress->setVisible(false); - exportButton->setEnabled(true); - if (status != QProcess::NormalExit || exitCode != 0) { - log->appendPlainText("Export failed."); - return; - } - const QString dist = - QDir(QFileInfo(projectFile).absolutePath()).filePath("dist"); - QDir destination(output->text()); - if (!destination.exists() && !QDir().mkpath(destination.path())) { - log->appendPlainText("Could not create the export destination."); - return; - } - const QFileInfoList packages = QDir(dist).entryInfoList( - QDir::NoDotAndDotDot | QDir::AllEntries | QDir::Hidden); - if (packages.isEmpty()) { - log->appendPlainText("Atlas Pack produced no distributable files."); + connect( + process, &QProcess::finished, &dialog, + [this, process, progress, exportButton, revealButton, output, + log](int exitCode, QProcess::ExitStatus status) { + progress->setVisible(false); + exportButton->setEnabled(true); + if (status != QProcess::NormalExit || exitCode != 0) { + log->appendPlainText("Export failed."); + return; + } + const QString dist = + QDir(QFileInfo(projectFile).absolutePath()).filePath("dist"); + QDir destination(output->text()); + if (!destination.exists() && !QDir().mkpath(destination.path())) { + log->appendPlainText( + "Could not create the export destination."); + return; + } + const QFileInfoList packages = QDir(dist).entryInfoList( + QDir::NoDotAndDotDot | QDir::AllEntries | QDir::Hidden); + if (packages.isEmpty()) { + log->appendPlainText( + "Atlas Pack produced no distributable files."); + return; + } + QString error; + for (const QFileInfo &package : packages) { + const QString target = destination.filePath(package.fileName()); + if (QFileInfo(target).isDir()) + QDir(target).removeRecursively(); + else + QFile::remove(target); + if (!copyExportPath(package.absoluteFilePath(), target, + &error)) { + log->appendPlainText(error); return; } - QString error; - for (const QFileInfo &package : packages) { - const QString target = destination.filePath(package.fileName()); - if (QFileInfo(target).isDir()) - QDir(target).removeRecursively(); - else - QFile::remove(target); - if (!copyExportPath(package.absoluteFilePath(), target, - &error)) { - log->appendPlainText(error); - return; - } - } - log->appendPlainText( - QStringLiteral("Export complete: %1").arg(destination.path())); - revealButton->setEnabled(true); - }); - connect(exportButton, &QPushButton::clicked, &dialog, - [this, process, output, platform, configuration, backend, progress, - exportButton, revealButton, log] { - const QString program = atlasCliPath(); - QStringList arguments; - if (program.isEmpty()) { - QMessageBox::warning( - this, "Export Project", - "Atlas CLI was not found. Install it or place it beside Atlas Editor."); - return; - } - QSettings settings( - QDir(QFileInfo(projectFile).absolutePath()) - .filePath(".atlas/project-settings.ini"), - QSettings::IniFormat); - settings.setValue("project/exportDirectory", output->text()); - settings.setValue("project/exportPlatform", platform->currentText()); - settings.setValue("project/exportConfiguration", - configuration->currentText()); - settings.setValue("project/exportBackend", backend->currentText()); - settings.sync(); - if (viewportPanel != nullptr) - viewportPanel->saveRuntimeScene(); - log->clear(); - log->appendPlainText("Starting Atlas Pack…"); - progress->setVisible(true); - exportButton->setEnabled(false); - revealButton->setEnabled(false); - arguments << "pack" << "--backend" << backend->currentText(); - if (configuration->currentText() == "Release") - arguments << "--release" << "1"; - process->setWorkingDirectory(QFileInfo(projectFile).absolutePath()); - process->start(program, arguments); - }); + } + log->appendPlainText( + QStringLiteral("Export complete: %1").arg(destination.path())); + revealButton->setEnabled(true); + }); + connect( + exportButton, &QPushButton::clicked, &dialog, + [this, process, output, platform, configuration, backend, progress, + exportButton, revealButton, log] { + const QString program = atlasCliPath(); + QStringList arguments; + if (program.isEmpty()) { + QMessageBox::warning(this, "Export Project", + "Atlas CLI was not found. Install it or " + "place it beside Atlas Editor."); + return; + } + QSettings settings(QDir(QFileInfo(projectFile).absolutePath()) + .filePath(".atlas/project-settings.ini"), + QSettings::IniFormat); + settings.setValue("project/exportDirectory", output->text()); + settings.setValue("project/exportPlatform", + platform->currentText()); + settings.setValue("project/exportConfiguration", + configuration->currentText()); + settings.setValue("project/exportBackend", backend->currentText()); + settings.sync(); + if (viewportPanel != nullptr) + viewportPanel->saveRuntimeScene(); + log->clear(); + log->appendPlainText("Starting Atlas Pack…"); + progress->setVisible(true); + exportButton->setEnabled(false); + revealButton->setEnabled(false); + arguments << "pack" << "--backend" << backend->currentText(); + if (configuration->currentText() == "Release") + arguments << "--release" << "1"; + process->setWorkingDirectory(QFileInfo(projectFile).absolutePath()); + process->start(program, arguments); + }); connect(&dialog, &QDialog::finished, process, [process] { if (process->state() == QProcess::NotRunning) return; @@ -1341,16 +1354,14 @@ void EditorWindow::showCommandPalette() { auto *item = new QListWidgetItem(commands); item->setText(action->text().remove('&')); item->setIcon(action->icon()); - item->setData(Qt::UserRole, - QVariant::fromValue( - reinterpret_cast(action))); + item->setData(Qt::UserRole, QVariant::fromValue( + reinterpret_cast(action))); const QString shortcut = !action->shortcut().isEmpty() ? action->shortcut().toString(QKeySequence::NativeText) : action->property("atlasShortcut").toString(); if (!shortcut.isEmpty()) - item->setText(item->text() + "\t" + - shortcut); + item->setText(item->text() + "\t" + shortcut); } if (commands->count() > 0) commands->setCurrentRow(0); @@ -1365,8 +1376,8 @@ void EditorWindow::showCommandPalette() { QListWidgetItem *item = commands->item(index); if (item == noCommands) continue; - if (firstMatch < 0 && item->text().contains( - text, Qt::CaseInsensitive)) + if (firstMatch < 0 && + item->text().contains(text, Qt::CaseInsensitive)) firstMatch = index; } if (firstMatch >= 0) { @@ -1379,8 +1390,8 @@ void EditorWindow::showCommandPalette() { for (int index = 0; index < commands->count(); ++index) { QListWidgetItem *item = commands->item(index); if (item != noCommands) - item->setHidden(!item->text().contains( - text, Qt::CaseInsensitive)); + item->setHidden( + !item->text().contains(text, Qt::CaseInsensitive)); } }); connect(commands, &QListWidget::itemActivated, &dialog, @@ -1451,11 +1462,11 @@ void EditorWindow::showGlobalSearch() { item->setData(SearchKindRole, 0); item->setData(SearchValueRole, path); } - const QJsonDocument snapshot = viewportPanel != nullptr - ? QJsonDocument::fromJson( - viewportPanel->currentSceneSnapshot() - .toUtf8()) - : QJsonDocument(); + const QJsonDocument snapshot = + viewportPanel != nullptr + ? QJsonDocument::fromJson( + viewportPanel->currentSceneSnapshot().toUtf8()) + : QJsonDocument(); std::function addObjects; addObjects = [&addObjects, results](const QJsonArray &objects) { for (const QJsonValue &value : objects) { @@ -1465,8 +1476,7 @@ void EditorWindow::showGlobalSearch() { if (!name.isEmpty() && id >= 0) { auto *item = new QListWidgetItem( QStringLiteral("Object %1").arg(name), results); - item->setIcon( - styling::icon(styling::Icon::Cube, "#8498A8")); + item->setIcon(styling::icon(styling::Icon::Cube, "#8498A8")); item->setToolTip(object.value("type").toString()); item->setData(SearchKindRole, 1); item->setData(SearchValueRole, id); @@ -1485,44 +1495,43 @@ void EditorWindow::showGlobalSearch() { results); item->setIcon(action->icon()); item->setData(SearchKindRole, 2); - item->setData( - SearchValueRole, - QVariant::fromValue(reinterpret_cast(action))); + item->setData(SearchValueRole, QVariant::fromValue( + reinterpret_cast(action))); } if (results->count() > 0) results->setCurrentRow(0); auto *noResults = new QListWidgetItem("No matching results", results); noResults->setData(SearchKindRole, 3); noResults->setHidden(true); - connect(search, &QLineEdit::textChanged, &dialog, - [results, noResults](const QString &text) { - int firstMatch = -1; - for (int index = 0; index < results->count(); ++index) { - QListWidgetItem *item = results->item(index); - if (item == noResults) - continue; - const bool matches = - item->text().contains(text, Qt::CaseInsensitive) || - item->toolTip().contains(text, Qt::CaseInsensitive); - if (firstMatch < 0 && matches) - firstMatch = index; - } - if (firstMatch >= 0) { - results->setCurrentRow(firstMatch); - noResults->setHidden(true); - } else { - noResults->setHidden(false); - results->setCurrentItem(noResults); - } - for (int index = 0; index < results->count(); ++index) { - QListWidgetItem *item = results->item(index); - if (item != noResults) - item->setHidden( - !item->text().contains(text, Qt::CaseInsensitive) && - !item->toolTip().contains(text, - Qt::CaseInsensitive)); - } - }); + connect( + search, &QLineEdit::textChanged, &dialog, + [results, noResults](const QString &text) { + int firstMatch = -1; + for (int index = 0; index < results->count(); ++index) { + QListWidgetItem *item = results->item(index); + if (item == noResults) + continue; + const bool matches = + item->text().contains(text, Qt::CaseInsensitive) || + item->toolTip().contains(text, Qt::CaseInsensitive); + if (firstMatch < 0 && matches) + firstMatch = index; + } + if (firstMatch >= 0) { + results->setCurrentRow(firstMatch); + noResults->setHidden(true); + } else { + noResults->setHidden(false); + results->setCurrentItem(noResults); + } + for (int index = 0; index < results->count(); ++index) { + QListWidgetItem *item = results->item(index); + if (item != noResults) + item->setHidden( + !item->text().contains(text, Qt::CaseInsensitive) && + !item->toolTip().contains(text, Qt::CaseInsensitive)); + } + }); connect(results, &QListWidget::itemActivated, &dialog, [this, &dialog](QListWidgetItem *item) { const int kind = item->data(Qt::UserRole + 1).toInt(); @@ -1542,8 +1551,7 @@ void EditorWindow::showGlobalSearch() { action->trigger(); return; } - const QString path = - item->data(Qt::UserRole + 2).toString(); + const QString path = item->data(Qt::UserRole + 2).toString(); if (path.endsWith(".ascene", Qt::CaseInsensitive) && viewportPanel != nullptr) { if (viewportPanel->openRuntimeScene(path) && @@ -1585,27 +1593,25 @@ void EditorWindow::runProjectCommand(bool buildOnly) { QDir().mkpath(settingsDirectory); QSettings settings(QDir(settingsDirectory).filePath("project-settings.ini"), QSettings::IniFormat); - const QString settingsKey = buildOnly ? "project/buildCommand" - : "project/runCommand"; - const QString defaultCommand = buildOnly ? "atlas pack --backend METAL" - : "atlas run project.atlas"; - const QString command = settings.value(settingsKey, defaultCommand) - .toString() - .trimmed(); + const QString settingsKey = + buildOnly ? "project/buildCommand" : "project/runCommand"; + const QString defaultCommand = + buildOnly ? "atlas pack --backend METAL" : "atlas run project.atlas"; + const QString command = + settings.value(settingsKey, defaultCommand).toString().trimmed(); if (command.isEmpty()) return; if (viewportPanel != nullptr) viewportPanel->saveRuntimeScene(); if (!buildOnly) { QString error; - if (!ToolchainInstaller::run( - {"script", "compile"}, - QFileInfo(projectFile).absolutePath(), &error)) { + if (!ToolchainInstaller::run({"script", "compile"}, + QFileInfo(projectFile).absolutePath(), + &error)) { QMessageBox::warning( this, "Script Compilation Failed", - error.isEmpty() - ? "Atlas could not compile the project scripts." - : error); + error.isEmpty() ? "Atlas could not compile the project scripts." + : error); return; } } @@ -1613,14 +1619,15 @@ void EditorWindow::runProjectCommand(bool buildOnly) { if (!settings.contains(settingsKey) || command == defaultCommand) { const QString executable = ToolchainInstaller::executablePath(); if (executable.isEmpty()) { - QMessageBox::warning( - this, buildOnly ? "Build Project" : "Run Project", - "Atlas CLI was not found. Install the Atlas toolchain from the Tools menu."); + QMessageBox::warning(this, + buildOnly ? "Build Project" : "Run Project", + "Atlas CLI was not found. Install the Atlas " + "toolchain from the Tools menu."); return; } - const QStringList arguments = buildOnly - ? QStringList{"pack", "--backend", "METAL"} - : QStringList{"run", "project.atlas"}; + const QStringList arguments = + buildOnly ? QStringList{"pack", "--backend", "METAL"} + : QStringList{"run", "project.atlas"}; QProcess::startDetached(executable, arguments, workingDirectory); return; } @@ -1710,8 +1717,7 @@ void EditorWindow::saveLayout() { QSettings settings("Neutral Software", "Atlas Engine"); settings.setValue("window/geometry", saveGeometry()); - settings.setValue(DockStateKey, - coreManager->saveState(DockStateVersion)); + settings.setValue(DockStateKey, coreManager->saveState(DockStateVersion)); settings.sync(); } @@ -1724,9 +1730,9 @@ void EditorWindow::restoreLayout() { const QByteArray dockState = settings.value(DockStateKey).toByteArray(); restoringLayout = true; - const bool restored = !dockState.isEmpty() && - coreManager->restoreState(dockState, - DockStateVersion); + const bool restored = + !dockState.isEmpty() && + coreManager->restoreState(dockState, DockStateVersion); if (!restored && !defaultDockState.isEmpty()) coreManager->restoreState(defaultDockState, DockStateVersion); restoringLayout = false; @@ -1757,11 +1763,11 @@ void EditorWindow::updateWindowTitle(bool dirty) { const QString name = projectName + (dirty ? "*" : ""); #ifdef ATLAS_DEBUG_BUILD const QString build = QStringLiteral(ATLAS_BUILD_STRING); - setWindowTitle(build.isEmpty() - ? QStringLiteral("%1 - Atlas Engine (Development)") - .arg(name) - : QStringLiteral("%1 - Atlas Engine (Development) + %2") - .arg(name, build)); + setWindowTitle( + build.isEmpty() + ? QStringLiteral("%1 - Atlas Engine (Development)").arg(name) + : QStringLiteral("%1 - Atlas Engine (Development) + %2") + .arg(name, build)); #else setWindowTitle(QStringLiteral("%1 - Atlas Engine %2") .arg(name, QStringLiteral(ATLAS_VERSION))); diff --git a/editor/views/editor/viewport.cpp b/editor/views/editor/viewport.cpp index e9f2af14..ebb3d7a9 100644 --- a/editor/views/editor/viewport.cpp +++ b/editor/views/editor/viewport.cpp @@ -1110,6 +1110,7 @@ bool ViewportPanel::applyRuntimeMaterialDirect(int id, const QString &path) { runtimeContext->saveCurrentScene(); refreshSceneSnapshot(); setSceneDirty(true); + reloadRuntime(); return true; } diff --git a/editor/views/general/contentBrowser.cpp b/editor/views/general/contentBrowser.cpp index fbbe96ea..aa2b3e18 100644 --- a/editor/views/general/contentBrowser.cpp +++ b/editor/views/general/contentBrowser.cpp @@ -79,6 +79,34 @@ bool isValidEntryName(const QString &name) { !name.contains('/') && !name.contains('\\'); } +QString requestFilePath(QWidget *parent, const QString &directory, + const QString &title, const QString &label, + const QString &defaultName, const QString &extension) { + bool accepted = false; + QString name = QInputDialog::getText(parent, title, label, QLineEdit::Normal, + defaultName, &accepted) + .trimmed(); + if (!accepted) + return {}; + + const QString suffix = "." + extension; + if (name.endsWith(suffix, Qt::CaseInsensitive)) + name.chop(suffix.size()); + name = name.trimmed(); + if (!isValidEntryName(name)) { + QMessageBox::warning(parent, title, "Enter a valid file name."); + return {}; + } + + const QString path = QDir(directory).filePath(name + suffix); + if (QFileInfo::exists(path)) { + QMessageBox::warning(parent, title, + "A file with that name already exists."); + return {}; + } + return path; +} + bool copyEntry(const QString &source, const QString &destination) { const QFileInfo info(source); if (info.isDir()) { @@ -474,14 +502,20 @@ void ContentBrowserPanel::createFolder() { } void ContentBrowserPanel::createScene() { - const QString path = uniquePath("New Scene.ascene"); + const QString path = requestFilePath(this, currentPath, "New Scene", + "Scene name", "New Scene", "ascene"); + if (path.isEmpty()) + return; if (writeNewFile(path, EmptyScene)) { gridView->setCurrentIndex(model->index(path)); } } void ContentBrowserPanel::createScript() { - const QString path = uniquePath("NewScript.ts"); + const QString path = requestFilePath(this, currentPath, "New Script", + "Script name", "NewScript", "ts"); + if (path.isEmpty()) + return; const QString relativePath = QDir(projectRoot).relativeFilePath(path); QString componentName = QFileInfo(path).completeBaseName(); componentName.remove(QRegularExpression("[^A-Za-z0-9_$]")); @@ -502,7 +536,11 @@ void ContentBrowserPanel::createScript() { } void ContentBrowserPanel::createMaterial() { - const QString path = uniquePath("New Material.amat"); + const QString path = requestFilePath(this, currentPath, "New Material", + "Material name", "New Material", + "amat"); + if (path.isEmpty()) + return; const QByteArray material = "{\n" " \"material\": {\n" diff --git a/editor/views/general/projectBrowser.cpp b/editor/views/general/projectBrowser.cpp index 0c5924f0..66286bfa 100644 --- a/editor/views/general/projectBrowser.cpp +++ b/editor/views/general/projectBrowser.cpp @@ -33,7 +33,7 @@ #include #ifndef ATLAS_VERSION -#define ATLAS_VERSION "Alpha 9" +#define ATLAS_VERSION "No Version" #endif namespace { @@ -41,10 +41,10 @@ constexpr int ProjectPathRole = Qt::UserRole; constexpr int ProjectAvailableRole = Qt::UserRole + 1; class TemplateCard : public QFrame { -public: - TemplateCard(const QString& title, const QString& description, - styling::Icon icon, const QColor& color, - QWidget* parent = nullptr) + public: + TemplateCard(const QString &title, const QString &description, + styling::Icon icon, const QColor &color, + QWidget *parent = nullptr) : QFrame(parent) { setProperty("templateCard", true); setProperty("selected", false); @@ -52,12 +52,12 @@ class TemplateCard : public QFrame { setMinimumHeight(112); setCursor(Qt::PointingHandCursor); - auto* layout = new QVBoxLayout(this); + auto *layout = new QVBoxLayout(this); layout->setContentsMargins(16, 14, 16, 14); layout->setSpacing(8); - auto* heading = new QHBoxLayout(); + auto *heading = new QHBoxLayout(); heading->setSpacing(8); - auto* iconLabel = new QLabel(this); + auto *iconLabel = new QLabel(this); iconLabel->setObjectName("templateIcon"); iconLabel->setPixmap(styling::icon(icon, color).pixmap(24, 24)); iconLabel->setFixedSize(28, 28); @@ -68,7 +68,7 @@ class TemplateCard : public QFrame { heading->addWidget(iconLabel); heading->addWidget(option, 1); layout->addLayout(heading); - auto* descriptionLabel = new QLabel(description, this); + auto *descriptionLabel = new QLabel(description, this); descriptionLabel->setObjectName("templateDescription"); descriptionLabel->setWordWrap(true); descriptionLabel->setAttribute(Qt::WA_TransparentForMouseEvents); @@ -82,56 +82,53 @@ class TemplateCard : public QFrame { }); } - QRadioButton* button() const { - return option; - } + QRadioButton *button() const { return option; } -protected: - void mousePressEvent(QMouseEvent* event) override { + protected: + void mousePressEvent(QMouseEvent *event) override { if (event->button() == Qt::LeftButton) { option->setChecked(true); } QFrame::mousePressEvent(event); } -private: - QRadioButton* option = nullptr; + private: + QRadioButton *option = nullptr; }; class CreateProjectDialog : public QDialog { -public: - explicit CreateProjectDialog(QWidget* parent = nullptr) - : QDialog(parent) { + public: + explicit CreateProjectDialog(QWidget *parent = nullptr) : QDialog(parent) { setWindowTitle("Create an Atlas project"); setModal(true); setMinimumWidth(760); setObjectName("createProjectDialog"); - auto* root = new QVBoxLayout(this); + auto *root = new QVBoxLayout(this); root->setContentsMargins(28, 26, 28, 24); root->setSpacing(18); - auto* title = new QLabel("Create a new project", this); + auto *title = new QLabel("Create a new project", this); title->setObjectName("dialogTitle"); root->addWidget(title); - auto* subtitle = new QLabel( + auto *subtitle = new QLabel( "Choose a renderer template. You can change these settings later.", this); subtitle->setObjectName("dialogSubtitle"); root->addWidget(subtitle); - auto* templateLayout = new QHBoxLayout(); + auto *templateLayout = new QHBoxLayout(); templateLayout->setSpacing(12); templateGroup = new QButtonGroup(this); templateGroup->setExclusive(true); - auto* pbr = new TemplateCard( + auto *pbr = new TemplateCard( "PBR", "Deferred physically based rendering for most 3D projects.", styling::Icon::Cube, "#8498A8", this); - auto* ddgi = new TemplateCard( + auto *ddgi = new TemplateCard( "PBR + DDGI", "PBR with dynamic diffuse global illumination enabled.", styling::Icon::Sun, "#A1957D", this); - auto* pathTracing = new TemplateCard( + auto *pathTracing = new TemplateCard( "Path Tracing", "Progressive ray-traced lighting for high-fidelity scenes.", styling::Icon::Aperture, "#9E897D", this); @@ -148,9 +145,9 @@ class CreateProjectDialog : public QDialog { templateLayout->addWidget(pathTracing); root->addLayout(templateLayout); - auto* fields = new QVBoxLayout(); + auto *fields = new QVBoxLayout(); fields->setSpacing(8); - auto* nameLabel = new QLabel("Project name", this); + auto *nameLabel = new QLabel("Project name", this); nameLabel->setObjectName("fieldLabel"); fields->addWidget(nameLabel); nameField = new QLineEdit(this); @@ -158,22 +155,21 @@ class CreateProjectDialog : public QDialog { nameField->setClearButtonEnabled(true); fields->addWidget(nameField); - auto* locationLabel = new QLabel("Location", this); + auto *locationLabel = new QLabel("Location", this); locationLabel->setObjectName("fieldLabel"); fields->addWidget(locationLabel); - auto* locationLayout = new QHBoxLayout(); + auto *locationLayout = new QHBoxLayout(); locationField = new QLineEdit(this); - QString defaultLocation = QStandardPaths::writableLocation( - QStandardPaths::DocumentsLocation); + QString defaultLocation = + QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation); if (defaultLocation.isEmpty()) { defaultLocation = QDir::homePath(); } locationField->setText(defaultLocation); locationLayout->addWidget(locationField, 1); - auto* browse = new QPushButton("Browse…", this); + auto *browse = new QPushButton("Browse…", this); browse->setProperty("secondary", true); - browse->setIcon( - styling::icon(styling::Icon::FolderOpen, "#7E929C")); + browse->setIcon(styling::icon(styling::Icon::FolderOpen, "#7E929C")); locationLayout->addWidget(browse); fields->addLayout(locationLayout); root->addLayout(fields); @@ -184,9 +180,9 @@ class CreateProjectDialog : public QDialog { errorLabel->hide(); root->addWidget(errorLabel); - auto* actions = new QHBoxLayout(); + auto *actions = new QHBoxLayout(); actions->addStretch(); - auto* cancel = new QPushButton("Cancel", this); + auto *cancel = new QPushButton("Cancel", this); cancel->setProperty("secondary", true); actions->addWidget(cancel); createButton = new QPushButton("Create project", this); @@ -211,14 +207,13 @@ class CreateProjectDialog : public QDialog { QDir(locationField->text()).exists()); errorLabel->hide(); }; - connect(nameField, &QLineEdit::textChanged, this, - updateAvailability); + connect(nameField, &QLineEdit::textChanged, this, updateAvailability); connect(locationField, &QLineEdit::textChanged, this, updateAvailability); connect(createButton, &QPushButton::clicked, this, [this] { QString error; - const auto projectTemplate = static_cast( - templateGroup->checkedId()); + const auto projectTemplate = + static_cast(templateGroup->checkedId()); createdProjectFile = ProjectStore::createProject( nameField->text(), locationField->text(), projectTemplate, &error); @@ -232,31 +227,29 @@ class CreateProjectDialog : public QDialog { nameField->setFocus(); } - QString projectFile() const { - return createdProjectFile; - } + QString projectFile() const { return createdProjectFile; } -private: - QButtonGroup* templateGroup = nullptr; - QLineEdit* nameField = nullptr; - QLineEdit* locationField = nullptr; - QLabel* errorLabel = nullptr; - QPushButton* createButton = nullptr; + private: + QButtonGroup *templateGroup = nullptr; + QLineEdit *nameField = nullptr; + QLineEdit *locationField = nullptr; + QLabel *errorLabel = nullptr; + QPushButton *createButton = nullptr; QString createdProjectFile; }; class ProjectRow : public QFrame { -public: - explicit ProjectRow(const AtlasProjectInfo& project, - QWidget* parent = nullptr) + public: + explicit ProjectRow(const AtlasProjectInfo &project, + QWidget *parent = nullptr) : QFrame(parent) { setObjectName("projectRow"); setProperty("available", project.available); - auto* layout = new QHBoxLayout(this); + auto *layout = new QHBoxLayout(this); layout->setContentsMargins(16, 12, 12, 12); layout->setSpacing(14); - auto* projectIcon = new QLabel(this); + auto *projectIcon = new QLabel(this); projectIcon->setObjectName("projectIcon"); projectIcon->setPixmap( styling::icon(styling::Icon::GameController, "#8498A8") @@ -265,26 +258,26 @@ class ProjectRow : public QFrame { projectIcon->setAlignment(Qt::AlignCenter); layout->addWidget(projectIcon); - auto* copy = new QVBoxLayout(); + auto *copy = new QVBoxLayout(); copy->setSpacing(3); - auto* title = new QLabel(project.name, this); + auto *title = new QLabel(project.name, this); title->setObjectName("projectName"); copy->addWidget(title); - auto* path = new QLabel(project.directory, this); + auto *path = new QLabel(project.directory, this); path->setObjectName("projectPath"); path->setTextInteractionFlags(Qt::TextSelectableByMouse); copy->addWidget(path); layout->addLayout(copy, 1); - auto* renderer = new QLabel(project.renderer, this); + auto *renderer = new QLabel(project.renderer, this); renderer->setObjectName("rendererBadge"); layout->addWidget(renderer); - auto* date = new QLabel( - project.lastModified.isValid() - ? project.lastModified.toString("d MMM yyyy") - : QStringLiteral("Unavailable"), - this); + auto *date = + new QLabel(project.lastModified.isValid() + ? project.lastModified.toString("d MMM yyyy") + : QStringLiteral("Unavailable"), + this); date->setObjectName("projectDate"); date->setMinimumWidth(90); layout->addWidget(date); @@ -297,17 +290,14 @@ class ProjectRow : public QFrame { layout->addWidget(moreButton); } - QToolButton* optionsButton() const { - return moreButton; - } + QToolButton *optionsButton() const { return moreButton; } -private: - QToolButton* moreButton = nullptr; + private: + QToolButton *moreButton = nullptr; }; -} +} // namespace -ProjectBrowser::ProjectBrowser(QWidget* parent) - : QMainWindow(parent) { +ProjectBrowser::ProjectBrowser(QWidget *parent) : QMainWindow(parent) { setWindowTitle("Atlas Engine — Projects"); setMinimumSize(900, 580); resize(1120, 720); @@ -316,78 +306,75 @@ ProjectBrowser::ProjectBrowser(QWidget* parent) } void ProjectBrowser::setupUi() { - auto* root = new QWidget(this); + auto *root = new QWidget(this); root->setObjectName("projectBrowserRoot"); setCentralWidget(root); - auto* rootLayout = new QHBoxLayout(root); + auto *rootLayout = new QHBoxLayout(root); rootLayout->setContentsMargins(0, 0, 0, 0); rootLayout->setSpacing(0); - auto* sidebar = new QFrame(root); + auto *sidebar = new QFrame(root); sidebar->setObjectName("projectSidebar"); sidebar->setFixedWidth(224); - auto* sidebarLayout = new QVBoxLayout(sidebar); + auto *sidebarLayout = new QVBoxLayout(sidebar); sidebarLayout->setContentsMargins(22, 26, 22, 22); sidebarLayout->setSpacing(18); - auto* brandLayout = new QHBoxLayout(); + auto *brandLayout = new QHBoxLayout(); brandLayout->setSpacing(11); - auto* brandIcon = new QLabel(sidebar); + auto *brandIcon = new QLabel(sidebar); brandIcon->setFixedSize(38, 38); brandIcon->setPixmap( QPixmap(":/editor/assets/Icon-iOS-Default-1024x1024@1x.png") .scaled(brandIcon->size(), Qt::KeepAspectRatio, Qt::SmoothTransformation)); brandLayout->addWidget(brandIcon); - auto* brandCopy = new QVBoxLayout(); + auto *brandCopy = new QVBoxLayout(); brandCopy->setSpacing(0); - auto* brand = new QLabel("Atlas Engine", sidebar); + auto *brand = new QLabel("Atlas Engine", sidebar); brand->setObjectName("projectBrand"); brandCopy->addWidget(brand); - auto* brandVersion = new QLabel(QStringLiteral(ATLAS_VERSION), sidebar); + auto *brandVersion = new QLabel(QStringLiteral(ATLAS_VERSION), sidebar); brandVersion->setObjectName("projectSidebarVersion"); brandCopy->addWidget(brandVersion); brandLayout->addLayout(brandCopy); brandLayout->addStretch(); sidebarLayout->addLayout(brandLayout); - auto* projectsNav = new QPushButton("Projects", sidebar); + auto *projectsNav = new QPushButton("Projects", sidebar); projectsNav->setObjectName("projectNavSelected"); - projectsNav->setIcon( - styling::icon(styling::Icon::SquaresFour, "#8498A8")); + projectsNav->setIcon(styling::icon(styling::Icon::SquaresFour, "#8498A8")); projectsNav->setEnabled(false); sidebarLayout->addWidget(projectsNav); sidebarLayout->addStretch(); rootLayout->addWidget(sidebar); - auto* content = new QWidget(root); + auto *content = new QWidget(root); content->setObjectName("projectBrowserContent"); - auto* contentLayout = new QVBoxLayout(content); + auto *contentLayout = new QVBoxLayout(content); contentLayout->setContentsMargins(34, 30, 34, 30); contentLayout->setSpacing(20); - auto* headingLayout = new QHBoxLayout(); - auto* headingCopy = new QVBoxLayout(); + auto *headingLayout = new QHBoxLayout(); + auto *headingCopy = new QVBoxLayout(); headingCopy->setSpacing(4); - auto* title = new QLabel("Projects", content); + auto *title = new QLabel("Projects", content); title->setObjectName("projectBrowserTitle"); headingCopy->addWidget(title); - auto* subtitle = new QLabel( - "Create a project or continue where you left off.", content); + auto *subtitle = + new QLabel("Create a project or continue where you left off.", content); subtitle->setObjectName("projectBrowserSubtitle"); headingCopy->addWidget(subtitle); headingLayout->addLayout(headingCopy, 1); - auto* openButton = new QPushButton("Open existing", content); + auto *openButton = new QPushButton("Open existing", content); openButton->setProperty("secondary", true); - openButton->setIcon( - styling::icon(styling::Icon::FolderOpen, "#7E929C")); + openButton->setIcon(styling::icon(styling::Icon::FolderOpen, "#7E929C")); headingLayout->addWidget(openButton); - auto* createButton = new QPushButton("New project", content); + auto *createButton = new QPushButton("New project", content); createButton->setObjectName("primaryAction"); - createButton->setIcon( - styling::icon(styling::Icon::Plus, "#FFFFFF")); + createButton->setIcon(styling::icon(styling::Icon::Plus, "#FFFFFF")); headingLayout->addWidget(createButton); contentLayout->addLayout(headingLayout); @@ -406,16 +393,16 @@ void ProjectBrowser::setupUi() { projectList->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); projectStack->addWidget(projectList); - auto* empty = new QWidget(projectStack); + auto *empty = new QWidget(projectStack); empty->setObjectName("projectEmptyState"); - auto* emptyLayout = new QVBoxLayout(empty); + auto *emptyLayout = new QVBoxLayout(empty); emptyLayout->setContentsMargins(40, 40, 40, 40); emptyLayout->addStretch(); emptyTitle = new QLabel("No projects yet", empty); emptyTitle->setObjectName("emptyStateTitle"); emptyTitle->setAlignment(Qt::AlignCenter); emptyLayout->addWidget(emptyTitle); - auto* emptySubtitle = new QLabel( + auto *emptySubtitle = new QLabel( "Create your first Atlas project or open one from disk.", empty); emptySubtitle->setObjectName("emptyStateSubtitle"); emptySubtitle->setAlignment(Qt::AlignCenter); @@ -440,37 +427,39 @@ void ProjectBrowser::setupUi() { void ProjectBrowser::reloadProjects() { projectList->clear(); const QList projects = ProjectStore::recentProjects(); - for (const AtlasProjectInfo& project : projects) { - auto* item = new QListWidgetItem(projectList); + for (const AtlasProjectInfo &project : projects) { + auto *item = new QListWidgetItem(projectList); item->setData(ProjectPathRole, project.projectFile); item->setData(ProjectAvailableRole, project.available); item->setSizeHint(QSize(0, 76)); - auto* row = new ProjectRow(project, projectList); + auto *row = new ProjectRow(project, projectList); projectList->setItemWidget(item, row); connect(row->optionsButton(), &QToolButton::clicked, this, [this, item, row] { projectList->setCurrentItem(item); - const QPoint menuPosition = projectList->viewport()->mapFromGlobal( - row->optionsButton()->mapToGlobal( - QPoint(0, row->optionsButton()->height()))); + const QPoint menuPosition = + projectList->viewport()->mapFromGlobal( + row->optionsButton()->mapToGlobal( + QPoint(0, row->optionsButton()->height()))); showProjectMenu(menuPosition); }); } filterProjects(searchField->text()); } -void ProjectBrowser::filterProjects(const QString& query) { +void ProjectBrowser::filterProjects(const QString &query) { const QString normalized = query.trimmed(); int visibleCount = 0; for (int index = 0; index < projectList->count(); ++index) { - QListWidgetItem* item = projectList->item(index); - QWidget* row = projectList->itemWidget(item); + QListWidgetItem *item = projectList->item(index); + QWidget *row = projectList->itemWidget(item); const QString path = item->data(ProjectPathRole).toString(); const bool matches = - normalized.isEmpty() || path.contains(normalized, Qt::CaseInsensitive) || + normalized.isEmpty() || + path.contains(normalized, Qt::CaseInsensitive) || (row != nullptr && - row->findChild("projectName") != nullptr && - row->findChild("projectName") + row->findChild("projectName") != nullptr && + row->findChild("projectName") ->text() .contains(normalized, Qt::CaseInsensitive)); item->setHidden(!matches); @@ -510,7 +499,7 @@ void ProjectBrowser::openExistingProject() { } void ProjectBrowser::openSelectedProject() { - QListWidgetItem* item = projectList->currentItem(); + QListWidgetItem *item = projectList->currentItem(); if (item == nullptr) { return; } @@ -526,8 +515,8 @@ void ProjectBrowser::openSelectedProject() { emit openProjectRequested(projectFile); } -void ProjectBrowser::showProjectMenu(const QPoint& position) { - QListWidgetItem* item = projectList->itemAt(position); +void ProjectBrowser::showProjectMenu(const QPoint &position) { + QListWidgetItem *item = projectList->itemAt(position); if (item == nullptr) { item = projectList->currentItem(); } @@ -539,19 +528,19 @@ void ProjectBrowser::showProjectMenu(const QPoint& position) { const bool available = ProjectStore::isProjectFile(projectFile); QMenu menu(this); - QAction* open = menu.addAction( - styling::icon(styling::Icon::GameController, "#8498A8"), - "Open project"); + QAction *open = + menu.addAction(styling::icon(styling::Icon::GameController, "#8498A8"), + "Open project"); open->setEnabled(available); - QAction* reveal = menu.addAction( - styling::icon(styling::Icon::FolderOpen, "#7E929C"), - "Show in Finder"); - reveal->setEnabled(QFileInfo::exists(QFileInfo(projectFile).absolutePath())); + QAction *reveal = menu.addAction( + styling::icon(styling::Icon::FolderOpen, "#7E929C"), "Show in Finder"); + reveal->setEnabled( + QFileInfo::exists(QFileInfo(projectFile).absolutePath())); menu.addSeparator(); - QAction* remove = menu.addAction( - styling::icon(styling::Icon::Trash, "#A17F7F"), - "Remove from list"); - QAction* selected = menu.exec(projectList->viewport()->mapToGlobal(position)); + QAction *remove = menu.addAction( + styling::icon(styling::Icon::Trash, "#A17F7F"), "Remove from list"); + QAction *selected = + menu.exec(projectList->viewport()->mapToGlobal(position)); if (selected == open) { openSelectedProject(); } else if (selected == reveal) { diff --git a/editor/views/general/splashScreen.cpp b/editor/views/general/splashScreen.cpp index 54e7b4e7..a9e513d0 100644 --- a/editor/views/general/splashScreen.cpp +++ b/editor/views/general/splashScreen.cpp @@ -11,7 +11,7 @@ #include #ifndef ATLAS_VERSION -#define ATLAS_VERSION "Alpha 9" +#define ATLAS_VERSION "No version" #endif #ifndef ATLAS_BUILD_STRING @@ -102,7 +102,7 @@ SplashScreen::SplashScreen(QWidget *parent) "appropriate runtime in your system that works with this version.", card); warning->setObjectName("splashWarning"); - warning->setGeometry(40, 199, 628, 44); + warning->setGeometry(40, 199, 628, 44); warning->setWordWrap(true); warning->setAlignment(Qt::AlignLeft | Qt::AlignTop); #endif diff --git a/tests/path-tracing/assets/materials/New Material.amat b/tests/path-tracing/assets/materials/New Material.amat new file mode 100644 index 00000000..fa680c40 --- /dev/null +++ b/tests/path-tracing/assets/materials/New Material.amat @@ -0,0 +1,25 @@ +{ + "material": { + "albedo": [ + 0.8374150991439819, + 0.2547340989112854, + 0.2064850777387619, + 1 + ], + "ao": 1, + "emissiveColor": [ + 0, + 0, + 0, + 1 + ], + "emissiveIntensity": 0, + "ior": 1.45, + "metallic": 1, + "normalMapStrength": 1, + "reflectivity": 0.5, + "roughness": 0.5, + "transmittance": 0, + "useNormalMap": true + } +} diff --git a/tests/path-tracing/lib/atlas.d.ts b/tests/path-tracing/lib/atlas.d.ts new file mode 100644 index 00000000..ab2b119e --- /dev/null +++ b/tests/path-tracing/lib/atlas.d.ts @@ -0,0 +1,2535 @@ +// +// atlas.d.ts +// As part of the Atlas project +// Created by Max Van den Eynde in 2026 +// -------------------------------------------------- +// Description: Declarations for the Atlas Library for scripting +// Copyright (c) 2026 Max Van den Eynde +// + +declare module "atlas/log" { + export const Debug: { + print(message: string): void; + warning(message: string): void; + error(message: string): void; + }; +} + +declare module "atlas" { + import { + Position3d, + Color, + Position2d, + Size2d, + Quaternion, + Size3d, + Point3d, + Normal3d, + Rotation3d, + Scale3d, + } from "atlas/units"; + import { + Skybox, + Light, + SpotLight, + DirectionalLight, + AreaLight, + RenderTarget, + Texture, + Cubemap, + } from "atlas/graphics"; + import { + AxisTrigger, + Trigger, + Key, + MouseButton, + InputAction, + AxisPacket, + } from "atlas/input"; + import { QueryResult } from "bezel"; + import { AudioEngine } from "finewave"; + import { Atmosphere } from "hydra"; + + export type Fog = { + color: Color; + intensity: number; + }; + + export type VolumetricLighting = { + enabled: boolean; + density: number; + weight: number; + decay: number; + exposure: number; + }; + + export type LightBloomConfiguration = { + radius: number; + maxSamples: number; + }; + + export type RimLightingConfiguration = { + color: Color; + intensity: number; + }; + + export type Environment = { + fog: Fog; + volumetricLighting: VolumetricLighting; + lightBloom: LightBloomConfiguration; + rimLighting: RimLightingConfiguration; + lookupTexture: Texture; + }; + + export class Scene { + name: string; + + setAmbientIntensity(intensity: number): void; + setAutomaticAmbient(enabled: boolean): void; + + setSkybox(skybox: Skybox): void; + useAtmosphereSkybox(enabled: boolean): void; + + setEnvironment(environment: Environment): void; + + setAmbientColor(color: Color): void; + setAmbientIntensity(intensity: number): void; + addDirectionalLight(light: DirectionalLight): void; + addLight(light: Light): void; + addSpotLight(light: SpotLight): void; + addAreaLight(light: AreaLight): void; + + getCamera(): Camera; + getWindow(): Window; + + atmosphere: Atmosphere; + } + + export abstract class Component { + parentId: number; + + abstract init(): void; + abstract update(deltaTime: number): void; + beforePhysics(): void; + atAttach(): void; + + onCollisionEnter(other: GameObject): void; + onCollisionStay(other: GameObject): void; + onCollisionExit(other: GameObject): void; + onSignalReceive(signal: string, sender: GameObject): void; + onSignalEnd(signal: string, sender: GameObject): void; + onQueryReceive(query: QueryResult, sender: GameObject): void; + + getParent(): GameObject; + getParent( + type: new (...args: any[]) => T, + ): T | null; + getObject(identifier: number | string): CoreObject; + getScene(): Scene; + getWindow(): Window; + } + + export class Material { + constructor(); + + albedo: Color; + metallic: number; + roughness: number; + ao: number; + reflectivity: number; + emissiveColor: Color; + emissiveIntensity: number; + normalMapStrength: number; + useNormalMap: boolean; + transmittance: number; + ior: number; + } + + export class CoreVertex { + constructor( + position?: Position3d, + color?: Color, + textureCoord?: Position2d, + normal?: Normal3d, + tangent?: Normal3d, + bitangent?: Normal3d, + ); + + position: Position3d; + color: Color; + textureCoord: Position2d; + normal: Normal3d; + tangent: Normal3d; + bitangent: Normal3d; + } + + export class Instance { + position: Position3d; + rotation: Rotation3d; + scale: Scale3d; + + move(position: Position3d): void; + setPosition(position: Position3d): void; + setRotation(rotation: Rotation3d): void; + rotate(rotation: Rotation3d): void; + setScale(scale: Scale3d): void; + scaleBy(scale: Scale3d): void; + + equals(other: Instance): boolean; + } + + export abstract class GameObject { + id: number; + components: Component[]; + position: Position3d; + rotation: Rotation3d; + scale: Scale3d; + name: string; + + constructor(); + + attachTexture(texture: Texture): void; + setPosition(position: Position3d): void; + move(position: Position3d): void; + lookAt(target: Position3d, up?: Normal3d): void; + setRotation(rotation: Rotation3d): void; + rotate(rotation: Rotation3d): void; + setScale(scale: Scale3d): void; + scaleBy(scale: Scale3d): void; + show(): void; + hide(): void; + + as(type: new (...args: any[]) => T): T | null; + + addComponent(component: T): void; + } + + export abstract class UIObject extends GameObject { + getSize(): Size2d; + getScreenPosition(): Position2d; + abstract setScreenPosition(position: Position2d): void; + } + + export class CoreObject extends GameObject { + vertices: CoreVertex[]; + indices: number[]; + textures: Texture[]; + material: Material; + instances: Instance[]; + position: Position3d; + rotation: Rotation3d; + scale: Scale3d; + castsShadows: boolean; + name: string; + + constructor(); + + makeEmissive(color: Color, intensity: number): void; + attachVertices(vertices: CoreVertex[]): void; + attachIndices(indices: number[]): void; + attachTexture(texture: Texture): void; + + setPosition(position: Position3d): void; + move(position: Position3d): void; + setRotation(rotation: Rotation3d): void; + setRotationQuaternion(rotation: Quaternion): void; + rotate(rotation: Rotation3d): void; + lookAt(target: Position3d, up?: Normal3d): void; + setScale(scale: Scale3d): void; + scaleBy(scale: Scale3d): void; + + clone(): CoreObject; + + show(): void; + hide(): void; + + addComponent(component: T): void; + + enableDeferredRendering(): void; + disableDeferredRendering(): void; + + createInstance(): Instance; + + getComponent( + type: new (...args: any[]) => T, + ): T | null; + + static box(size: Size3d): CoreObject; + static plane(size: Size2d): CoreObject; + static pyramid(size: Size3d): CoreObject; + static sphere( + radius: number, + sectorCount: number, + stackCount: number, + ): CoreObject; + } + + export class Model extends GameObject { + static fromResource(path: string): Model; + + getObjects(): CoreObject[]; + + override move(position: Position3d): void; + override setPosition(position: Position3d): void; + override setRotation(rotation: Rotation3d): void; + override lookAt(target: Position3d, up?: Normal3d): void; + override rotate(rotation: Rotation3d): void; + override setScale(scale: Scale3d): void; + override scaleBy(scale: Scale3d): void; + + override show(): void; + override hide(): void; + override attachTexture(texture: Texture): void; + } + + export enum ResourceType { + File, + Texture, + SpecularMap, + Audio, + Font, + Model, + } + + export class Resource { + type: ResourceType; + path: string; + name: string; + + constructor(type: ResourceType, path: string, name: string); + + static fromAssetPath( + path: string, + type: ResourceType, + name?: string, + ): Resource; + + static fromName(name: string, type: ResourceType): Resource | null; + } + + export class ResourceGroup { + resources: Resource[]; + name: string; + + constructor(resources: Resource[], name: string); + + addResource(resource: Resource): void; + getResourceByName(name: string): Resource | null; + } + + export class Camera { + position: Position3d; + target: Point3d; + fov: number; + nearClip: number; + farClip: number; + orthographicSize: number; + movementSpeed: number; + mouseSensitivity: number; + controllerLookSensitivity: number; + lookSmoothness: number; + useOrthographic: boolean; + focusDepth: number; + focusRange: number; + + constructor(); + + move(offset: Position3d): void; + setPosition(position: Position3d): void; + setPositionKeepingOrientation(position: Position3d): void; + lookAt(target: Point3d, up?: Normal3d): void; + moveTo(target: Point3d, speed: number): void; + getDirection(): Normal3d; + } + + export type ViewInformation = { + position: Position3d; + target: Point3d; + time: number; + deltaTime: number; + }; + + export type WindowConfiguration = { + title: string; + width: number; + height: number; + renderScale: number; + mouseCaptured: boolean; + posX: number; + posY: number; + multisampling: boolean; + editorControls: boolean; + decorations: boolean; + resizable: boolean; + transparent: boolean; + alwaysOnTop: boolean; + opacity: number; + aspectRatioX: number; + aspectRatioY: number; + ssaoScale: number; + }; + + export type VideoMode = { + width: number; + height: number; + refreshRate: number; + }; + + export class Monitor { + monitorId: number; + primary: boolean; + + queryVideoModes(): VideoMode[]; + getCurrentVideoMode(): VideoMode; + getPhysicalSize(): Size2d; + getPosition(): Position2d; + getContentScale(): number; + getName(): string; + } + + export enum ControllerAxis { + LeftStick, + LeftStickX, + LeftStickY, + RightStick, + RightStickX, + RightStickY, + Trigger, + TriggerLeft, + TriggerRight, + } + + export enum ControllerButton { + A = 0, + B, + X, + Y, + LeftBumper, + RightBumper, + Back, + Start, + Guide, + LeftThumb, + RightThumb, + DPadUp, + DPadRight, + DPadDown, + DPadLeft, + ButtonCount, + } + + export enum NintendoControllerButton { + B = 0, + A, + Y, + X, + L, + R, + ZL, + ZR, + Minus, + Plus, + LeftStick, + RightStick, + DPadUp, + DPadRight, + DPadDown, + DPadLeft, + ButtonCount, + } + + export enum SonyControllerButton { + Cross = 0, + Circle, + Square, + Triangle, + L1, + R1, + L2, + R2, + Share, + Options, + LeftStick, + RightStick, + DPadUp, + DPadRight, + DPadDown, + DPadLeft, + ButtonCount, + } + + export const CONTROLLER_UNDEFINED = -2; + + export class Gamepad { + controllerId: number; + name: string; + connected: boolean; + + getAxisTrigger(axis: ControllerAxis): AxisTrigger; + static getGlobalAxisTrigger(axis: ControllerAxis): AxisTrigger; + getButtonTrigger(button: ControllerButton): Trigger; + static getGlobalButtonTrigger(button: ControllerButton): Trigger; + + runble(strength: number, duration: number): void; + } + + export type Controller = Gamepad; + + export class Joystick { + joystickId: number; + name: string; + connected: boolean; + + getSingleAxisTrigger(axisIndex: number): AxisTrigger; + getDualAxisTrigger(axisIndexX: number, axisIndexY: number): AxisTrigger; + getButtonTrigger(buttonIndex: number): Trigger; + + getAxisCount(): number; + getButtonCount(): number; + } + + export type ControllerID = { + id: number; + name: string; + isJoystick: boolean; + }; + + export class Window { + title: string; + width: number; + height: number; + currentFrame: number; + + audioEngine: AudioEngine; + + setClearColor(color: Color): void; + close(): void; + setFullscreen(enabled: boolean): void; + setFullscreen(monitor: Monitor): void; + setWindowed(config: WindowConfiguration): void; + + enumerateMonitors(): Monitor[]; + getControllers(): ControllerID[]; + getController(id: ControllerID): Controller | null; + getJoystick(id: ControllerID): Joystick | null; + + instantiate(object: GameObject): void; + destroy(object: GameObject): void; + + addUIObject(object: UIObject): void; + setCamera(camera: Camera): void; + setScene(scene: Scene): void; + getTime(): number; + isKeyActive(key: Key): boolean; + + isMouseButtonActive(button: MouseButton): boolean; + isMouseButtonPressed(button: MouseButton): boolean; + getTextInput(): string; + startTextInput(): void; + stopTextInput(): void; + isTextInputActive(): boolean; + + isControllerButtonPressed( + controllerID: number, + buttonIndex: number, + ): boolean; + getControllerAxisValue(controllerID: number, axisIndex: number): number; + getControllerAxisPairValue( + controllerID: number, + axisIndexX: number, + axisIndexY: number, + ): Position2d; + + releaseMouse(): void; + captureMouse(): void; + getCursorPosition(): Position2d; + + main: Window; + + getCurrentScene(): Scene; + getCamera(): Camera; + addRenderTarget(): RenderTarget; + getSize(): Size2d; + activateDebug(): void; + desactivateDebug(): void; + + getDeltaTime(): number; + getFramesPerSecond(): number; + gravity: number; + + useAtlasTracer(enabled: boolean): void; + setLogOutput( + showLogs: boolean, + showWarnings: boolean, + showErrors: boolean, + ): void; + + usesDeferred: boolean; + + getRenderScale(): number; + useMetalUpscaling(ratio: number): void; + isMetalUpscalingEnabled(): boolean; + getMetalUpscalingRatio(): number; + + getSSAORenderScale(): number; + + addInputAction(action: InputAction): void; + resetInputActions(): void; + getInputAction(name: string): InputAction | null; + + isActionTriggered(name: string): boolean; + isActionCurrentlyActive(name: string): boolean; + getActionAxisValue(name: string): AxisPacket; + } +} + +declare module "atlas/graphics" { + import { Resource, ResourceGroup } from "atlas"; + import { + Color, + Magnitude2d, + Position3d, + Magnitude3d, + Rotation3d, + Size2d, + } from "atlas/units"; + + export enum TextureType { + Color, + Specular, + Cubemap, + Depth, + DepthCube, + Normal, + Parallax, + SSAONoise, + SSAO, + Metallic, + Roughness, + AO, + Opacity, + HDR, + } + + export class Texture { + type: TextureType; + resource: Resource; + width: number; + height: number; + channels: number; + id: number; + borderColor: Color; + + static fromResource( + resource: Resource | string, + type: TextureType, + ): Texture; + + static createEmpty( + width: number, + height: number, + type: TextureType, + borderColor?: Color, + ): Texture; + + static createColor( + color: Color, + type: TextureType, + width: number, + height: number, + ): Texture; + + createCheckerboard( + width: number, + height: number, + checkSize: number, + color1: Color, + color2: Color, + ): void; + + createDoubleCheckerboard( + width: number, + height: number, + checkSizeBig: number, + checkSizeSmall: number, + color1: Color, + color2: Color, + color3: Color, + ): void; + + displayToWindow(): void; + } + + export class Cubemap { + resources: Resource[]; + id: number; + + constructor(resources: Resource[]); + + getAverageColor(): Color; + static fromResourceGroup(resourceGroup: ResourceGroup): Cubemap | null; + updateWithColors(colors: Color[]): void; + } + + export enum RenderTargetType { + Scene, + Multisampled, + Shadow, + CubeShadow, + GBuffer, + SSAO, + SSAOBlur, + } + + export enum RenderPassType { + Deferred, + Forward, + PathTracing, + } + + export const Effects: { + Inversion: { type: "Inversion" }; + Grayscale: { type: "Grayscale" }; + Sharpen: { type: "Sharpen" }; + Blur: { type: "Blur"; magnitude: number }; + EdgeDetection: { type: "EdgeDetection" }; + ColorCorrection: { + type: "ColorCorrection"; + exposure: number; + contrast: number; + saturation: number; + gamma: number; + temperature: number; + tint: number; + }; + MotionBlur: { type: "MotionBlur"; size: number; separation: number }; + ChromaticAberration: { + type: "ChromaticAberration"; + red: number; + green: number; + blue: number; + direction: Magnitude2d; + }; + Posterization: { type: "Posterization"; levels: number }; + Pixelation: { type: "Pixelation"; pixelSize: number }; + Dialation: { type: "Dilation"; size: number; separation: number }; + Dilation: { type: "Dilation"; size: number; separation: number }; + FilmGrain: { type: "FilmGrain"; amount: number }; + }; + + export type Effect = + | keyof typeof Effects + | (typeof Effects)[keyof typeof Effects]; + + export class RenderTarget { + type: RenderTargetType; + resolution: number; + outTextures: Texture[]; + depthTexture: Texture | null; + + constructor(type: RenderTargetType, resolution: number); + + addEffect(effect: Effect): void; + + addToPassQueue(type: RenderPassType): void; + display(): void; + } + + export class Skybox { + cubemap: Cubemap; + + constructor(cubemap: Cubemap); + } + + export class AmbientLight { + color: Color; + intensity: number; + + constructor(color?: Color, intensity?: number); + } + + export class Light { + position: Position3d; + color: Color; + shineColor: Color; + intensity: number; + distance: number; + + constructor( + position?: Position3d, + color?: Color, + distance?: number, + shineColor?: Color, + intensity?: number, + ); + + setColor(color: Color): void; + // Also calls addDebugObject(Window&); + createDebugObject(): void; + castShadows(resolution: number): void; + } + + export class DirectionalLight { + direction: Magnitude3d; + color: Color; + shineColor: Color; + intensity: number; + + constructor( + direction?: Magnitude3d, + color?: Color, + shineColor?: Color, + intensity?: number, + ); + + setColor(color: Color): void; + castShadows(resolution: number): void; + } + + export class SpotLight { + position: Position3d; + direction: Magnitude3d; + color: Color; + shineColor: Color; + range: number; + cutOff: number; + outerCutOff: number; + intensity: number; + + constructor( + position?: Position3d, + direction?: Magnitude3d, + color?: Color, + cutOff?: number, + outerCutOff?: number, + shineColor?: Color, + intensity?: number, + range?: number, + ); + + setColor(color: Color): void; + // Also calls addDebugObject(Window&); + createDebugObject(): void; + lookAt(target: Position3d): void; + castShadows(resolution: number): void; + } + + export class AreaLight { + position: Position3d; + right: Magnitude3d; + up: Magnitude3d; + size: Size2d; + color: Color; + shineColor: Color; + intensity: number; + range: number; + angle: number; + castsBothSides: boolean; + rotation: Rotation3d; + + constructor( + position?: Position3d, + right?: Magnitude3d, + up?: Magnitude3d, + size?: Size2d, + color?: Color, + shineColor?: Color, + intensity?: number, + range?: number, + angle?: number, + castsBothSides?: boolean, + rotation?: Rotation3d, + ); + + getNormal(): Magnitude3d; + setColor(color: Color): void; + setRotation(rotation: Rotation3d): void; + rotate(delta: Rotation3d): void; + // Also calls addDebugObject(Window&); + createDebugObject(): void; + castShadows(resolution: number): void; + } +} + +declare module "atlas/units" { + export class Position3d { + x: number; + y: number; + z: number; + + constructor(x: number, y: number, z: number); + + static zero(): Position3d; + static down(): Position3d; + static up(): Position3d; + static forward(): Position3d; + static back(): Position3d; + static right(): Position3d; + static left(): Position3d; + static invalid(): Position3d; + + add(other: Position3d | number): Position3d; + subtract(other: Position3d | number): Position3d; + multiply(other: Position3d | number): Position3d; + divide(other: Position3d | number): Position3d; + is(other: Position3d): boolean; + + normalized(): Position3d; + toString(): string; + } + + export type Scale3d = Position3d; + export type Size3d = Position3d; + export type Point3d = Position3d; + export type Normal3d = Position3d; + export type Magnitude3d = Position3d; + export type Impulse3d = Position3d; + export type Force3d = Position3d; + export type Vector3d = Position3d; + export type Velocity3d = Position3d; + export type Rotation3d = Position3d; + + export class BoundingBox { + min: Position3d; + max: Position3d; + + constructor(min: Position3d, max: Position3d); + + toString(): string; + contains(point: Position3d): boolean; + intersects(other: BoundingBox): boolean; + } + + export class Quaternion { + x: number; + y: number; + z: number; + w: number; + + constructor(x: number, y: number, z: number, w: number); + constructor(rotation: Rotation3d); + + toEuler(): Rotation3d; + static fromEuler(rotation: Rotation3d): Quaternion; + } + + export class Color { + r: number; + g: number; + b: number; + a: number; + + constructor(r: number, g: number, b: number, a?: number); + + add(other: Color | number): Color; + subtract(other: Color | number): Color; + multiply(other: Color | number): Color; + divide(other: Color | number): Color; + is(other: Color): boolean; + + static white(): Color; + static black(): Color; + static red(): Color; + static green(): Color; + static blue(): Color; + static transparent(): Color; + static yellow(): Color; + static cyan(): Color; + static magenta(): Color; + static gray(): Color; + static orange(): Color; + static purple(): Color; + static brown(): Color; + static pink(): Color; + static lime(): Color; + static navy(): Color; + static teal(): Color; + static olive(): Color; + static maroon(): Color; + + static fromHex(hex: string): Color; + static mix(color1: Color, color2: Color, t: number): Color; + } + + export enum Direction3d { + Up, + Down, + Left, + Right, + Forward, + Backward, + } + + export class Position2d { + x: number; + y: number; + + constructor(x: number, y: number); + + static zero(): Position2d; + static up(): Position2d; + static down(): Position2d; + static left(): Position2d; + static right(): Position2d; + static invalid(): Position2d; + + add(other: Position2d | number): Position2d; + subtract(other: Position2d | number): Position2d; + multiply(other: Position2d | number): Position2d; + divide(other: Position2d | number): Position2d; + + is(other: Position2d): boolean; + } + + export type Scale2d = Position2d; + export type Point2d = Position2d; + export type Movement2d = Position2d; + export type Magnitude2d = Position2d; + + export class Radians { + value: number; + + constructor(value: number); + + add(other: Radians): Radians; + subtract(other: Radians): Radians; + multiply(other: Radians | number): Radians; + divide(other: Radians | number): Radians; + + toNumber(): number; + static fromDegrees(degrees: number): Radians; + toDegrees(): number; + } + + export class Size2d { + width: number; + height: number; + + constructor(width: number, height: number); + + static zero(): Size2d; + + toString(): string; + + add(other: Size2d | number): Size2d; + subtract(other: Size2d | number): Size2d; + multiply(other: Size2d | number): Size2d; + divide(other: Size2d | number): Size2d; + + is(other: Size2d): boolean; + } +} + +declare module "atlas/audio" { + import { Component, Resource } from "atlas"; + import { Color, Position3d } from "atlas/units"; + import { AudioSource } from "finewave"; + + export class AudioPlayer extends Component { + constructor(); + + override init(): void; + play(): void; + pause(): void; + stop(): void; + setVolume(volume: number): void; + setLoop(loop: boolean): void; + + setSource(resource: Resource): void; + + override update(dt: number): void; + + setPosition(position: Position3d): void; + useSpatialAudio(enabled: boolean): void; + + source: AudioSource; + } +} + +declare module "atlas/input" { + import { Position2d } from "atlas/units"; + + export enum Key { + Unknown, + Space, + Apostrophe, + Comma, + Minus, + Period, + Slash, + Key0, + Key1, + Key2, + Key3, + Key4, + Key5, + Key6, + Key7, + Key8, + Key9, + Semicolon, + Equal, + A, + B, + C, + D, + E, + F, + G, + H, + I, + J, + K, + L, + M, + N, + O, + P, + Q, + R, + S, + T, + U, + V, + W, + X, + Y, + Z, + LeftBracket, + Backslash, + RightBracket, + GraveAccent, + Escape, + Enter, + Tab, + Backspace, + Insert, + Delete, + Right, + Left, + Down, + Up, + PageUp, + PageDown, + Home, + End, + CapsLock, + ScrollLock, + NumLock, + PrintScreen, + Pause, + F1, + F2, + F3, + F4, + F5, + F6, + F7, + F8, + F9, + F10, + F11, + F12, + F13, + F14, + F15, + F16, + F17, + F18, + F19, + F20, + F21, + F22, + F23, + F24, + F25, + KP0, + KP1, + KP2, + KP3, + KP4, + KP5, + KP6, + KP7, + KP8, + KP9, + KPDecimal, + KPDivide, + KPMultiply, + KPSubtract, + KPAdd, + KPEnter, + KPEqual, + LeftShift, + LeftControl, + LeftAlt, + LeftSuper, + RightShift, + RightControl, + RightAlt, + RightSuper, + Menu, + } + + export enum MouseButton { + Left, + Right, + Middle, + X1, + X2, + Button6, + Button7, + Button8, + Last, + } + + export enum TriggerType { + MouseButton, + Key, + ControllerButton, + } + + export type ControllerButtonTrigger = { + controllerID: number; + buttonIndex: number; + }; + + export class Trigger { + type: TriggerType; + mouseButton?: MouseButton; + key?: Key; + controllerButton?: ControllerButtonTrigger; + + static fromKey(key: Key): Trigger; + static fromMouseButton(mouseButton: MouseButton): Trigger; + static fromControllerButton( + controllerID: number, + buttonIndex: number, + ): Trigger; + } + + export enum AxisTriggerType { + MouseAxis, + KeyCustom, + ControllerAxis, + } + + export class AxisTrigger { + type: AxisTriggerType; + + positiveX: Trigger; + negativeX: Trigger; + positiveY: Trigger; + negativeY: Trigger; + + controllerId?: number; + controllerAxisSingle: boolean; + axisIndex?: number; + axisIndexY: number; + + isJoystick: boolean; + + static fromMouse(): AxisTrigger; + static fromKeys( + positiveX: Key, + negativeX: Key, + positiveY: Key, + negativeY: Key, + ): AxisTrigger; + static fromControllerAxis( + controllerId: number, + axisIndex: number, + single: boolean, + axisIndexY?: number, + ): AxisTrigger; + } + + export type AxisPacket = { + deltaX: number; + deltaY: number; + x: number; + y: number; + valueX: number; + valueY: number; + inputDeltaX: number; + inputDeltaY: number; + hasValueInput: boolean; + hasDeltaInput: boolean; + }; + + export type MousePacket = { + xpos: number; + ypos: number; + xoffset: number; + yoffset: number; + constrainPitch: boolean; + firstMouse: boolean; + }; + + export type MouseScrollPacket = { + xoffset: number; + yoffset: number; + }; + + export class InputAction { + triggers: Trigger[]; + axisTriggers: AxisTrigger[]; + name: string; + isAxis: boolean; + isAxisSingle: boolean; + normalized: boolean; + invertY: boolean; + + static createButtonAction( + name: string, + triggers: Trigger[], + ): InputAction; + static createAxisAction( + name: string, + axisTriggers: AxisTrigger[], + ): InputAction; + static createSingleAxisAction( + name: string, + positiveTrigger: Trigger, + negativeTrigger: Trigger, + ): InputAction; + } + + export const Input: { + addAction(action: InputAction): InputAction; + resetActions(): void; + + isKeyActive(key: Key): boolean; + isKeyPressed(key: Key): boolean; + isMouseButtonActive(button: MouseButton): boolean; + isMouseButtonPressed(button: MouseButton): boolean; + + getTextInput(): string; + startTextInput(): void; + stopTextInput(): void; + isTextInputActive(): boolean; + + isControllerButtonPressed( + controllerID: number, + buttonIndex: number, + ): boolean; + getControllerAxisValue(controllerID: number, axisIndex: number): number; + getControllerAxisPairValue( + controllerID: number, + axisIndexX: number, + axisIndexY: number, + ): Position2d; + + captureMouse(): void; + releaseMouse(): void; + getMousePosition(): Position2d; + + isActionTriggered(name: string): boolean; + isActionCurrentlyActive(name: string): boolean; + getAxisActionValue(name: string): AxisPacket; + }; + + export abstract class Interactive { + abstract onKeyPress(key: Key, dt: number): void; + abstract onKeyRelease(key: Key, dt: number): void; + abstract onMouseMove(packet: MousePacket, dt: number): void; + abstract onMouseButtonPress(button: MouseButton, dt: number): void; + abstract onMouseScroll(packet: MouseScrollPacket, dt: number): void; + abstract onEachFrame(dt: number): void; + } +} + +declare module "atlas/particle" { + import { + Position3d, + Color, + Magnitude3d, + Rotation3d, + Scale3d, + Normal3d, + } from "atlas/units"; + import { GameObject } from "atlas"; + import { Texture } from "atlas/graphics"; + + export enum ParticleEmissionType { + Fountain, + Ambient, + } + + export type ParticleSettings = { + minLifetime: number; + maxLifetime: number; + minSize: number; + maxSize: number; + fadeSpeed: number; + gravity: number; + spread: number; + speedVariation: number; + }; + + export type Particle = { + position: Position3d; + velocity: Magnitude3d; + color: Color; + lifetime: number; + maxLifetime: number; + size: number; + active: boolean; + }; + + export class ParticleEmitter extends GameObject { + settings: ParticleSettings; + constructor(maxParticles: number); + + override attachTexture(texture: Texture): void; + setColor(color: Color): void; + enableTexture(): void; + disableTexture(): void; + override setPosition(position: Position3d): void; + override move(position: Position3d): void; + getPosition(): Position3d; + + setEmissionType(type: ParticleEmissionType): void; + setDirection(direction: Magnitude3d): void; + setSpawnRadius(radius: number): void; + setSpawnRate(rate: number): void; + setParticleSettings(settings: ParticleSettings): void; + + emitOnce(): void; + emitContinuous(): void; + startEmission(): void; + stopEmission(): void; + emitBurst(count: number): void; + + // Disabled + override setRotation(rotation: Rotation3d): void; + override setScale(scale: Scale3d): void; + override lookAt(target: Position3d, up?: Normal3d): void; + override rotate(rotation: Rotation3d): void; + override scaleBy(scale: Scale3d): void; + override show(): void; + override hide(): void; + } +} + +declare module "bezel" { + import { + Position3d, + Normal3d, + Point3d, + Size3d, + Force3d, + Impulse3d, + Velocity3d, + } from "atlas/units"; + import { GameObject, Component } from "atlas"; + + export type RaycastHit = { + position: Position3d; + normal: Normal3d; + distance: number; + object: GameObject; + didHit: boolean; + }; + + export type RaycastResult = { + hits: RaycastHit[]; + hit: RaycastHit | null; + closestDistance: number; + }; + + export type OverlapHit = { + contactPoint: Position3d; + penerationAxis: Point3d; + penetrationDepth: number; + object: GameObject; + }; + + export type OverlapResult = { + hits: OverlapHit[]; + hitAny: boolean; + }; + + export type SweepHit = { + position: Position3d; + normal: Normal3d; + distance: number; + percentage: number; + object: GameObject; + }; + + export type SweepResult = { + hits: SweepHit[]; + closest: SweepHit | null; + hitAny: boolean; + endPosition: Position3d; + }; + + export enum QueryOperation { + RaycastAll, + Raycast, + RasycastWorld, + RaycastWorldAll, + RaycastTagged, + RaycastTaggedAll, + Movement, + Overlap, + MovementAll, + } + + export type QueryResult = { + operation: QueryOperation; + raycastResult?: RaycastResult; + overlapResult?: OverlapResult; + sweepResult?: SweepResult; + }; + + export type WorldBody = {}; + + export type JointMember = GameObject | WorldBody; + + export enum SpringMode { + FrequencyAndDamping, + StiffnessAndDamping, + } + + export enum Space { + Local, + Global, + } + + export type Spring = { + enabled: boolean; + mode: SpringMode; + frequency: number; + dampingRatio: number; + stiffness: number; + damping: number; + }; + + export type AngleLimits = { + enabled: boolean; + minAngle: number; + maxAngle: number; + }; + + export type Motor = { + enabled: boolean; + maxForce: number; + maxTorque: number; + }; + + export abstract class Joint extends Component { + parent: JointMember; + child: JointMember; + space: Space; + anchor: Position3d; + breakForce: number; + breakTorque: number; + + override init(): void; + override update(deltaTime: number): void; + + abstract override beforePhysics(): void; + abstract breakJoint(): void; + } + + export class FixedJoint extends Joint { + override beforePhysics(): void; + override breakJoint(): void; + } + + export class HingeJoint extends Joint { + axis1: Normal3d; + axis2: Normal3d; + angleLimits: AngleLimits; + motor: Motor; + + override beforePhysics(): void; + override breakJoint(): void; + } + + export class SpringJoint extends Joint { + anchorB: Position3d; + restLength: number; + useLimits: boolean; + minLength: number; + maxLength: number; + + spring: Spring; + + override beforePhysics(): void; + override breakJoint(): void; + } + + export type VehicleWheelSettings = { + position: Position3d; + enableSuspensionForcePoint: boolean; + suspensionForcePoint: Position3d; + + suspensionDirection: Normal3d; + steeringAxis: Normal3d; + wheelUp: Normal3d; + wheelForward: Normal3d; + + suspensionMinLength: number; + suspensionMaxLength: number; + suspensionPreloadLength: number; + suspensionFrequencyHz: number; + suspensionDampingRatio: number; + + radius: number; + width: number; + + inertia: number; + angularDamping: number; + maxSteerAngleDegrees: number; + maxBrakeTorque: number; + maxHandBrakeTorque: number; + }; + + export type VehicleDifferential = { + leftWheel: number; + rightWheel: number; + differentialRatio: number; + leftRightSplit: number; + limitedSlipRatio: number; + engineTorqueRatio: number; + }; + + export type VehicleEngine = { + maxTorque: number; + minRPM: number; + maxRPM: number; + inertia: number; + angularDamping: number; + }; + + export enum VehicleTransmissionMode { + Auto, + Manual, + } + + export type VehicleTransmission = { + mode: VehicleTransmissionMode; + gearRatios: number[]; + reverseGearRatios: number[]; + switchTime: number; + clutchReleaseTime: number; + switchLatency: number; + shiftUpRPM: number; + shiftDownRPM: number; + clutchStrength: number; + }; + + export type VehicleControllerSettings = { + engine: VehicleEngine; + transmission: VehicleTransmission; + differentials: VehicleDifferential[]; + differentialLimitedSlipRatio: number; + }; + + export type VehicleSettings = { + up: Normal3d; + forward: Normal3d; + + maxPitchRollAngleDeg: number; + + wheels: VehicleWheelSettings[]; + controller: VehicleControllerSettings; + + maxSlopAngleDeg: number; + }; + + export class Vehicle extends Component { + settings: VehicleSettings; + forward: number; + right: number; + brake: number; + handBrake: number; + + override atAttach(): void; + override beforePhysics(): void; + + requestRecreate(): void; + + override init(): void; + override update(deltaTime: number): void; + } + + export type CapsuleCollider = { + radius: number; + height: number; + }; + + export type BoxCollider = { + size: Size3d; + }; + + export type SphereCollider = { + radius: number; + }; + + export type MeshCollider = {}; + + export type Collider = + | CapsuleCollider + | BoxCollider + | SphereCollider + | MeshCollider; + + export class Rigidbody extends Component { + sendSignal: string; + isSensor: boolean; + + override atAttach(): void; + override init(): void; + override beforePhysics(): void; + override update(deltaTime: number): void; + + clone(): Rigidbody; + + addCollider(collider: Collider): void; + + setFriction(friction: number): void; + applyForce(force: Force3d): void; + applyForceAtPoint(force: Force3d, point: Position3d): void; + applyImpulse(impulse: Impulse3d): void; + + setLinearVelocity(velocity: Velocity3d): void; + addLinearVelocity(velocity: Velocity3d): void; + setAngularVelocity(velocity: Velocity3d): void; + addAngularVelocity(velocity: Velocity3d): void; + + setMaxLinearVelocity(velocity: number): void; + setMaxAngularVelocity(velocity: number): void; + + getLinearVelocity(): Velocity3d; + getAngularVelocity(): Velocity3d; + getVelocity(): Velocity3d; + + raycast(direction: Normal3d, maxDistance: number): RaycastResult; + raycastAll(direction: Normal3d, maxDistance: number): RaycastResult; + raycastWorld( + origin: Position3d, + direction: Normal3d, + maxDistance: number, + ): RaycastResult; + raycastWorldAll( + origin: Position3d, + direction: Normal3d, + maxDistance: number, + ): RaycastResult; + raycastTagged( + tags: string[], + direction: Normal3d, + maxDistance: number, + ): RaycastResult; + raycastTaggedAll( + tags: string[], + direction: Normal3d, + maxDistance: number, + ): RaycastResult; + + overlap(): OverlapResult; + overlapWithCollider(collider: Collider): OverlapResult; + overlapWithColliderWorld( + collider: Collider, + position: Position3d, + ): OverlapResult; + + predictMovementWithCollider( + endPosition: Position3d, + collider: Collider, + ): SweepResult; + predictMovement(endPosition: Position3d): SweepResult; + predictMovementWithColliderWorld( + startPosition: Position3d, + endPosition: Position3d, + collider: Collider, + ): SweepResult; + predictMovementWorld( + startPosition: Position3d, + endPosition: Position3d, + ): SweepResult; + predictMovementWithColliderAll( + endPosition: Position3d, + collider: Collider, + ): SweepResult; + predictMovementAll(endPosition: Position3d): SweepResult; + predictMovementWithColliderAllWorld( + startPosition: Position3d, + endPosition: Position3d, + collider: Collider, + ): SweepResult; + predictMovementAllWorld( + startPosition: Position3d, + endPosition: Position3d, + ): SweepResult; + + hasTag(tag: string): boolean; + addTag(tag: string): void; + removeTag(tag: string): void; + + setDamping(linearDamping: number, angularDamping: number): void; + setMass(mass: number): void; + setRestituition(restitution: number): void; + setMotionType(motionType: "Static" | "Dynamic" | "Kinematic"): void; + } + + export class Sensor extends Rigidbody { + constructor(); // sets isSensor to true + + setSignal(signal: string): void; + } +} + +declare module "aurora" { + import { GameObject, Resource } from "atlas"; + import { Texture } from "atlas/graphics"; + import { + Color, + Position3d, + Rotation3d, + Normal3d, + Scale3d, + } from "atlas/units"; + + export class PerlinNoise { + constructor(seed?: number); + noise(x: number, y: number): number; + } + + export class SimplexNoise { + static noise(xin: number, yin: number): number; + } + + export class WorleyNoise { + constructor(numPoints: number, seed?: number); + noise(x: number, y: number): number; + } + + export class FractalNoise { + constructor(o: number, p: number); + noise(x: number, y: number): number; + } + + export class Noise { + static perlin(x: number, y: number): number; + static simplex(x: number, y: number): number; + static worley(x: number, y: number): number; + static fractal( + x: number, + y: number, + octaves: number, + persistence: number, + ): number; + static seed: number; + static initializedSeed: boolean; + } + + export class Biome { + name: string; + texture: Texture; + color: Color; + useTexture: boolean; + + attachTexture(texture: Texture): void; + + minHeight: number; + maxHeight: number; + minMoisture: number; + maxMoisture: number; + minTemperature: number; + maxTemperature: number; + + constructor( + name: string, + texture: Texture, + color: Color, + useTexture: boolean, + ); + + condition: BiomeFunction; + } + + export type BiomeFunction = (biome: Biome) => void; + + export class Terrain extends GameObject { + attachTexture(texture: Texture): void; + setPosition(position: Position3d): void; + move(position: Position3d): void; + setRotation(rotation: Rotation3d): void; + lookAt(target: Position3d, up?: Normal3d): void; + rotate(rotation: Rotation3d): void; + setScale(scale: Scale3d): void; + scaleBy(scale: Scale3d): void; + show(): void; + hide(): void; + + heightmap: Resource; + moistureTexture: Texture; + temperatureTexture: Texture; + generator: TerrainGenerator; + + createdWithMap: boolean; + width: number; + length: number; + height: number; + + addBiome(biome: Biome): void; + + static fromGenerator(generator: T): Terrain; + static fromHeightmap(heightmap: Resource): Terrain; + + maxPeak: number; + seaLevel: number; + } + + export abstract class TerrainGenerator { + abstract generateHeight(x: number, y: number): number; + applyTo(terrain: Terrain): void; + } + + export class HillGenerator extends TerrainGenerator { + constructor(scale: number, amplitude: number); + + override generateHeight(x: number, y: number): number; + } + + export class MountainGenerator extends TerrainGenerator { + constructor( + scale: number, + amplitude: number, + octaves: number, + persistance: number, + ); + + override generateHeight(x: number, y: number): number; + } + + export class PlainGenerator extends TerrainGenerator { + constructor(scale: number, amplitude: number); + + override generateHeight(x: number, y: number): number; + } + + export class IslandGenerator extends TerrainGenerator { + constructor(numFeatures: number, scale: number); + + override generateHeight(x: number, y: number): number; + } + + export class CompoundGenerator extends TerrainGenerator { + addGenerator(generator: T): void; + override generateHeight(x: number, y: number): number; + } +} + +declare module "finewave" { + import { Position3d } from "atlas/units"; + import { Resource } from "atlas"; + + export class AudioEngine { + setListenerPosition(position: Position3d): void; + setListenerOrientation(forward: Position3d, up: Position3d): void; + setListenerVelocity(velocity: Position3d): void; + setMasterVolume(volume: number): void; + deviceName: string; + } + + export class AudioData { + static fromResource(resource: Resource): AudioData; + isMono: boolean; + resource: Resource; + } + + export class AudioSource { + setData(data: AudioData): void; + fromFile(resource: Resource): void; + play(): void; + pause(): void; + stop(): void; + setLoop(loop: boolean): void; + setVolume(volume: number): void; + setPitch(pitch: number): void; + setPosition(position: Position3d): void; + setVelocity(velocity: Position3d): void; + + isPlaying(): boolean; + playFrom(position: number): void; + disableSpatialization(): void; + applyEffect(effect: AudioEffect): void; + getPosition(): Position3d; + getListenerPosition(): Position3d; + useSpatialization(): void; + } + + export abstract class AudioEffect {} + + export class Reverb extends AudioEffect { + setRoomSize(size: number): void; + setDamping(damping: number): void; + setWetLevel(level: number): void; + setDryLevel(level: number): void; + setWidth(width: number): void; + } + + export class Echo extends AudioEffect { + setDelay(delay: number): void; + setDecay(decay: number): void; + setWetLevel(level: number): void; + setDryLevel(level: number): void; + } + + export class Distortion extends AudioEffect { + setEdge(edge: number): void; + setGain(gain: number): void; + setLowpassCutoff(cutoff: number): void; + } +} + +declare module "graphite" { + import { UIObject, Resource } from "atlas"; + import { Texture } from "atlas/graphics"; + import { Position2d, Position3d, Color, Size2d, Size3d } from "atlas/units"; + + export class Image extends UIObject { + texture: Texture; + position: Position3d; + size: Size2d; + tint: Color; + + constructor(); + constructor( + texture: Texture, + size: Size2d, + position: Position2d, + tint: Color, + ); + + override getSize(): Size2d; + override getScreenPosition(): Position2d; + override setScreenPosition(position: Position2d): void; + + style(): UIStyle; + setStyle(style: UIStyle): Image; + + setTexture(texture: Texture): void; + setSize(size: Size2d): void; + } + + export type TextFieldChangeEvent = { + text: string; + cursorPosition: number; + focused: boolean; + }; + + export type ButtonClickEvent = { + label: string; + }; + + export type CheckboxToggleEvent = { + label: string; + checked: boolean; + }; + + export namespace TextField { + export type ChangeCallback = (event: TextFieldChangeEvent) => void; + } + + export class TextField extends UIObject { + text: string; + placeholder: string; + font: Font; + position: Position3d; + fontSize: number; + padding: Size2d; + maximumWidth: number; + textColor: Color; + placeholderColor: Color; + backgroundColor: Color; + borderColor: Color; + focusedBorderColor: Color; + cursorColor: Color; + + constructor(); + + constructor( + font: Font, + maximumWidth: number, + position: Position2d, + text: string, + placeholder: string, + ); + + override getSize(): Size2d; + override getScreenPosition(): Position2d; + override setScreenPosition(position: Position2d): void; + + getText(): string; + isFocused(): boolean; + getCursorIndex(): number; + style(): UIStyle; + + setText(text: string): TextField; + setPlaceholder(placeholder: string): TextField; + setPadding(padding: Size2d): TextField; + setMaximumWidth(width: number): TextField; + setFontSize(size: number): TextField; + setStyle(style: UIStyle): TextField; + setOnChange(callback: TextField.ChangeCallback): TextField; + + focus(): void; + blur(): void; + } + + export namespace Button { + export type ClickCallback = (event: ButtonClickEvent) => void; + } + + export class Button extends UIObject { + label: string; + font: Font; + position: Position3d; + fontSize: number; + padding: Size2d; + minimumSize: Size2d; + textColor: Color; + backgroundColor: Color; + hoverBackgroundColor: Color; + pressedBackgroundColor: Color; + borderColor: Color; + hoverBorderColor: Color; + enabled: boolean; + + constructor(); + + constructor(font: Font, label: string, position: Position2d); + + override getSize(): Size2d; + override getScreenPosition(): Position2d; + override setScreenPosition(position: Position2d): void; + + getLabel(): string; + isHovered(): boolean; + isEnabled(): boolean; + + style(): UIStyle; + + setLabel(label: string): Button; + setPadding(padding: Size2d): Button; + setMinimumSize(size: Size2d): Button; + setFontSize(size: number): Button; + setStyle(style: UIStyle): Button; + setOnClick(callback: Button.ClickCallback): Button; + setEnabled(enabled: boolean): void; + } + + export namespace Checkbox { + export type ToggleCallback = (event: CheckboxToggleEvent) => void; + } + + export class Checkbox extends UIObject { + label: string; + font: Font; + position: Position3d; + fontSize: number; + padding: Size2d; + boxSize: number; + spacing: number; + checked: boolean; + enabled: boolean; + textColor: Color; + boxBackgroundColor: Color; + hoverBoxBackgroundColor: Color; + borderColor: Color; + activeBorderColor: Color; + checkColor: Color; + + constructor(); + + constructor(font: Font, label: string, position: Position2d); + + override getSize(): Size2d; + override getScreenPosition(): Position2d; + override setScreenPosition(position: Position2d): void; + + getLabel(): string; + isChecked(): boolean; + isHovered(): boolean; + isEnabled(): boolean; + + style(): UIStyle; + + setLabel(label: string): Checkbox; + setPadding(padding: Size2d): Checkbox; + setFontSize(size: number): Checkbox; + setBoxSize(size: number): Checkbox; + setSpacing(spacing: number): Checkbox; + setStyle(style: UIStyle): Checkbox; + setOnToggle(callback: Checkbox.ToggleCallback): Checkbox; + setChecked(checked: boolean): void; + setEnabled(enabled: boolean): void; + toggle(): void; + } + + export enum ElementAlignment { + Top, + Center, + Bottom, + } + + export enum LayoutAnchor { + TopLeft, + TopCenter, + TopRight, + CenterLeft, + Center, + CenterRight, + BottomLeft, + BottomCenter, + BottomRight, + } + + export class Column extends UIObject { + constructor(position: Position2d); + constructor( + children: UIObject[], + spacing: number, + padding: Size2d, + position: Position2d, + ); + + spacing: number; + maxSize: Size2d; + padding: Size2d; + children: UIObject[]; + position: Position3d; + alignment: ElementAlignment; + anchor: LayoutAnchor; + + addChild(child: UIObject): void; + setChildren(children: UIObject[]): void; + + override getSize(): Size2d; + + override getScreenPosition(): Position2d; + override setScreenPosition(position: Position2d): void; + + style: UIStyle; + setStyle(style: UIStyle): Column; + } + + export class Row extends UIObject { + constructor(position: Position2d); + constructor( + children: UIObject[], + spacing: number, + padding: Size2d, + position: Position2d, + ); + + spacing: number; + maxSize: Size2d; + padding: Size2d; + children: UIObject[]; + position: Position3d; + alignment: ElementAlignment; + anchor: LayoutAnchor; + + addChild(child: UIObject): void; + setChildren(children: UIObject[]): void; + + override getSize(): Size2d; + + override getScreenPosition(): Position2d; + override setScreenPosition(position: Position2d): void; + + style: UIStyle; + setStyle(style: UIStyle): Column; + } + + export class Stack extends UIObject { + constructor(position: Position2d); + constructor( + children: UIObject[], + padding: Size2d, + position: Position2d, + ); + + maxSize: Size2d; + padding: Size2d; + children: UIObject[]; + position: Position3d; + horizontalAlignment: ElementAlignment; + verticalAlignment: ElementAlignment; + anchor: LayoutAnchor; + + addChild(child: UIObject): void; + setChildren(children: UIObject[]): void; + + override getSize(): Size2d; + + override getScreenPosition(): Position2d; + override setScreenPosition(position: Position2d): void; + + style: UIStyle; + setStyle(style: UIStyle): Column; + } + + export enum UIStyleState { + Normal, + Hovered, + Pressed, + Disabled, + Focused, + Checked, + } + + export type UIStyleStateSnapshot = { + hovered: boolean; + pressed: boolean; + disabled: boolean; + focused: boolean; + checked: boolean; + }; + + export class UIStyleVariant { + paddingValue?: number; + cornerRadiusValue?: number; + borderWidthValue?: number; + backgroundColorValue?: Color; + borderColorValue?: Color; + foregroundColorValue?: Color; + tintColorValue?: Color; + fontValue?: Font; + fontSizeValue?: number; + + padding(value: Size2d): UIStyleVariant; + cornerRadius(value: number): UIStyleVariant; + borderWidth(value: number): UIStyleVariant; + backgroundColor(value: Color): UIStyleVariant; + borderColor(value: Color): UIStyleVariant; + foregroundColor(value: Color): UIStyleVariant; + tintColor(value: Color): UIStyleVariant; + font(value: Font): UIStyleVariant; + fontSize(value: number): UIStyleVariant; + } + + export type UIResolvedStyle = { + padding: Size2d; + cornerRadius: number; + borderWidth: number; + backgroundColor: Color; + borderColor: Color; + foregroundColor: Color; + tintColor: Color; + font: Font; + fontSize: number; + }; + + export class UIStyle { + normal(): UIStyleVariant; + hovered(): UIStyleVariant; + pressed(): UIStyleVariant; + disabled(): UIStyleVariant; + focused(): UIStyleVariant; + checked(): UIStyleVariant; + variant(state: UIStyleState): UIStyleVariant; + } + + export class Theme { + text: UIStyle; + image: UIStyle; + textField: UIStyle; + button: UIStyle; + checkbox: UIStyle; + row: UIStyle; + column: UIStyle; + stack: UIStyle; + + static current(): Theme; + static set(theme: Theme): void; + static reset(): void; + } + + export type Character = { + size: Size2d; + bearing: Position2d; + advance: number; + uvMin: Position2d; + uvMax: Position2d; + }; + + export type FontAtlas = Map; + + export class Font { + name: string; + atlas: Texture; + size: number; + resource: Resource; + texture: Texture; + + static fromResource(resource: Resource): Font; + static getFont(name: string): Font; + + changeSize(size: number): Font; + } + + export class Text extends UIObject { + content: string; + font: Font; + position: Position3d; + fontSize: number; + color: Color; + + constructor(); + constructor( + text: string, + font: Font, + color: Color, + position: Position2d, + ); + + override getSize(): Size2d; + override getScreenPosition(): Position2d; + override setScreenPosition(position: Position2d): void; + + style(): UIStyle; + setStyle(style: UIStyle): Text; + setFontSize(size: number): Text; + } +} + +declare module "hydra" { + import { + Position3d, + Size3d, + Force3d, + Magnitude3d, + Color, + Scale3d, + Rotation3d, + Size2d, + } from "atlas/units"; + import { Cubemap } from "atlas/graphics"; + import { ViewInformation, GameObject } from "atlas"; + import { Texture } from "atlas/graphics"; + + export class WorleyNoise3D { + constructor(frequency: number, numDivisions: number); + + getValue(x: number, y: number, z: number): number; + + get3dTexture(size: number): number; + getDetailTexture(size: number): number; + get3dTextureAtAllChannels(size: number): number; + } + + export class Clouds { + constructor(frequency: number, numDivisions: number); + + getCloudTexture(size: number): number; + + position: Position3d; + size: Size3d; + scale: number; + offset: Position3d; + density: number; + densityMultiplier: number; + absorption: number; + scattering: number; + phase: number; + clusterStrength: number; + primaryStepCount: number; + lightStepCount: number; + lightStepMultiplier: number; + minStepLength: number; + wind: Force3d; + } + + export enum WeatherCondition { + Clear, + Rain, + Snow, + Storm, + } + + export type WeatherState = { + condition: WeatherCondition; + intensity: number; + wind: Force3d; + }; + + export type WeatherDelegate = ( + information: ViewInformation, + ) => WeatherState; + + export class Atmosphere { + timeOfDay: number; + secondsPerHour: number; + wind: Magnitude3d; + weatherDelegate: WeatherDelegate; + + enable(): void; + disable(): void; + isEnabled(): boolean; + enableWeather(): void; + disableWeather(): void; + + getNormalizedTime(): number; + getSunAngle(): Magnitude3d; + getMoonAngle(): Magnitude3d; + getLightIntensity(): number; + getLightColor(): Color; + + clouds?: Clouds; + + getSkyboxColors(): Color[]; + createSkyCubemap(size: number): Cubemap; + updateSkyCubemap(cubemap: Cubemap): void; + + castShadowsFromSunlight(resolution: number): void; + useGlobalLight(): void; + + sunColor: Color; + moonColor: Color; + + sunSize: number; + moonSize: number; + sunTintStrength: number; + moonTintStrength: number; + starIntensity: number; + + isDaytime(): boolean; + setTime(hours: number, minutes: number, seconds: number): void; + + addClouds(frequency: number, numDivisions: number): void; + + cycle: boolean; + resetRuntimeState(): void; + } + + export class Fluid extends GameObject { + waveVelocity: number; + + constructor(); + create(extent: Size2d, color: Color): void; + + override move(position: Position3d): void; + override setPosition(position: Position3d): void; + override setRotation(rotation: Rotation3d): void; + override rotate(rotation: Rotation3d): void; + override setScale(scale: Scale3d): void; + + setExtent(extent: Size2d): void; + setWaveVelocity(velocity: number): void; + setWaterColor(color: Color): void; + getPosition(): Position3d; + getScale(): Scale3d; + + normalTexture: Texture; + movementTexture: Texture; + } +} diff --git a/tests/path-tracing/main.ascene b/tests/path-tracing/main.ascene new file mode 100644 index 00000000..e048865d --- /dev/null +++ b/tests/path-tracing/main.ascene @@ -0,0 +1,117 @@ +{ + "camera": { + "actions": [], + "automaticMoving": false, + "controllerLookSensitivity": 180.0, + "farClip": 1000.0, + "focusDepth": 20.0, + "focusRange": 10.0, + "fov": 60.0, + "lookSmoothness": 0.15000000596046448, + "mouseSensitivity": 0.10000000149011612, + "movementSpeed": 2.0, + "nearClip": 0.5, + "orthoSize": 5.0, + "orthographic": false, + "position": [ + 2.4182076454162598, + 0.7414431571960449, + 2.6619606018066406 + ], + "target": [ + 1.25, + 0.0, + 0.0 + ] + }, + "environment": { + "atmosphere": { + "enabled": true, + "globalLight": { + "castsShadows": true, + "enabled": true, + "shadowResolution": 4096 + } + }, + "atmosphereSky": true, + "automaticAmbient": true + }, + "id": "main_scene", + "lights": [ + { + "color": [ + 1.0, + 1.0, + 1.0, + 1.0 + ], + "id": "ambientLight_0", + "intensity": 0.25, + "name": "ambientLight_0", + "position": [ + 0.0, + 0.0, + 0.0 + ], + "type": "ambientLight" + } + ], + "name": "Main Scene", + "objects": [ + { + "components": [], + "material": "", + "name": "Cube", + "position": [ + 0.0, + 0.0, + 0.0 + ], + "rotation": [ + 0.0, + 0.0, + 0.0 + ], + "scale": [ + 1.0, + 1.0, + 1.0 + ], + "solid_type": "cube", + "type": "solid" + }, + { + "components": [], + "id": 262366761, + "material": "assets/materials/New Material.amat", + "name": "Cube 2", + "parent": "Cube", + "position": [ + 1.25, + 0.0, + 0.0 + ], + "rotation": [ + 0.0, + 0.0, + 0.0 + ], + "scale": [ + 1.0, + 1.0, + 1.0 + ], + "solid_type": "cube", + "type": "solid" + } + ], + "property_syncs": [], + "targets": [ + { + "display": true, + "name": "Main Target", + "render": true, + "type": "scene" + } + ] +} diff --git a/tests/path-tracing/package-lock.json b/tests/path-tracing/package-lock.json new file mode 100644 index 00000000..a7ede3b4 --- /dev/null +++ b/tests/path-tracing/package-lock.json @@ -0,0 +1,512 @@ +{ + "name": "path-tracing", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "path-tracing", + "devDependencies": { + "esbuild": "^0.25.5", + "typescript": "^5.9.2" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", + "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz", + "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz", + "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz", + "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz", + "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz", + "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz", + "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz", + "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz", + "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz", + "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz", + "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz", + "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz", + "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz", + "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz", + "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz", + "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz", + "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz", + "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz", + "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz", + "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz", + "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz", + "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz", + "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz", + "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz", + "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz", + "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz", + "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.12", + "@esbuild/android-arm": "0.25.12", + "@esbuild/android-arm64": "0.25.12", + "@esbuild/android-x64": "0.25.12", + "@esbuild/darwin-arm64": "0.25.12", + "@esbuild/darwin-x64": "0.25.12", + "@esbuild/freebsd-arm64": "0.25.12", + "@esbuild/freebsd-x64": "0.25.12", + "@esbuild/linux-arm": "0.25.12", + "@esbuild/linux-arm64": "0.25.12", + "@esbuild/linux-ia32": "0.25.12", + "@esbuild/linux-loong64": "0.25.12", + "@esbuild/linux-mips64el": "0.25.12", + "@esbuild/linux-ppc64": "0.25.12", + "@esbuild/linux-riscv64": "0.25.12", + "@esbuild/linux-s390x": "0.25.12", + "@esbuild/linux-x64": "0.25.12", + "@esbuild/netbsd-arm64": "0.25.12", + "@esbuild/netbsd-x64": "0.25.12", + "@esbuild/openbsd-arm64": "0.25.12", + "@esbuild/openbsd-x64": "0.25.12", + "@esbuild/openharmony-arm64": "0.25.12", + "@esbuild/sunos-x64": "0.25.12", + "@esbuild/win32-arm64": "0.25.12", + "@esbuild/win32-ia32": "0.25.12", + "@esbuild/win32-x64": "0.25.12" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + } + } +} diff --git a/tests/path-tracing/package.json b/tests/path-tracing/package.json new file mode 100644 index 00000000..2f3db6b2 --- /dev/null +++ b/tests/path-tracing/package.json @@ -0,0 +1,13 @@ +{ + "devDependencies": { + "esbuild": "^0.25.5", + "typescript": "^5.9.2" + }, + "name": "path-tracing", + "private": true, + "scripts": { + "atlas:compile": "atlas script compile", + "typecheck": "tsc --noEmit" + }, + "type": "module" +} diff --git a/tests/path-tracing/project.atlas b/tests/path-tracing/project.atlas new file mode 100644 index 00000000..55738484 --- /dev/null +++ b/tests/path-tracing/project.atlas @@ -0,0 +1,23 @@ +app_name = "path-tracing" +atlas_version = "alpha9" +backend = "AUTO" +name = "path-tracing" +platform = "DESKTOP" + +[game] +assets = ["assets/"] +main_scene = "main.ascene" + +[pack] +icon = "none" +supported_platforms = "all" + +[renderer] +default = "pathtracing" +global_illumination = false + +[window] +dimensions = [1280, 720] +mouse_capture = false +multisampling = false +ssaoScale = 0.5 diff --git a/tests/path-tracing/tsconfig.json b/tests/path-tracing/tsconfig.json new file mode 100644 index 00000000..186f01e9 --- /dev/null +++ b/tests/path-tracing/tsconfig.json @@ -0,0 +1,49 @@ +{ + "compilerOptions": { + "baseUrl": ".", + "ignoreDeprecations": "6.0", + "module": "ESNext", + "moduleResolution": "Bundler", + "noEmit": true, + "paths": { + "atlas": [ + "lib/atlas.d.ts" + ], + "atlas/*": [ + "lib/*" + ] + }, + "skipLibCheck": true, + "strict": true, + "target": "ES2022", + "verbatimModuleSyntax": true + }, + "exclude": [ + ".git", + "node_modules", + "dist", + "build", + "target", + "extern", + "atlas", + "aurora", + "bezel", + "finewave", + "graphite", + "hydra", + "include", + "opal", + "photon", + "cli", + "docs", + "tests", + "runtime/lib", + "runtime/docs", + "runtime/executable" + ], + "include": [ + "**/*.ts", + "**/*.mts", + "**/*.cts" + ] +}