Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
[submodule "extern/quickjs"]
path = extern/quickjs
url = https://github.com/quickjs-ng/quickjs.git
[submodule "extern/imgui"]
path = extern/imgui
url = https://github.com/ocornut/imgui.git
[submodule "extern/QtDockingSystem"]
path = extern/QtDockingSystem
url = https://github.com/githubuser0xFFFF/Qt-Advanced-Docking-System.git
[submodule "extern/imgui"]
path = extern/imgui
url = https://github.com/ocornut/imgui.git
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ It is built with C++ and uses OpenGL, Vulkan and Metal for rendering. It also ha
an environment system and a debugging system with more to come.

![Atlas Screenshot](example.png)
![Editor Screenshot](editorExample.png)

## Features

Expand Down
16 changes: 13 additions & 3 deletions atlas/application/window.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1917,9 +1917,6 @@ bool Window::stepFrame() {
}

if (!editorControlsRenderedInScenePass) {
if (this->usePathTracing) {
renderEditorGrid(commandBuffer);
}
renderEditorOverlays(commandBuffer);
}

Expand Down Expand Up @@ -3862,6 +3859,13 @@ void Window::addPreferencedObject(Renderable *obj) {
}
}

void Window::removePreferencedObject(Renderable *obj) {
this->preferenceRenderables.erase(
std::remove(this->preferenceRenderables.begin(),
this->preferenceRenderables.end(), obj),
this->preferenceRenderables.end());
}

void Window::addPreludeObject(Renderable *obj) {
if (obj == nullptr) {
return;
Expand Down Expand Up @@ -5688,6 +5692,12 @@ void Window::configurePathTracing(int samplesPerPixel, int bounceLimit,
accumulationFrames);
}

void Window::resetPathTracingAccumulation() {
if (pathTracer != nullptr) {
pathTracer->resetAccumulation();
}
}

bool Window::setEditorPathTracingPreview(bool enabled) {
if (pathTracer == nullptr) {
return false;
Expand Down
3 changes: 3 additions & 0 deletions atlas/graphics/deferred.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1012,6 +1012,9 @@ void Window::deferredRendering(
Window::mainWindow->currentScene->environment.rimLight.color.r,
Window::mainWindow->currentScene->environment.rimLight.color.g,
Window::mainWindow->currentScene->environment.rimLight.color.b);
lightPipeline->setUniform1f(
"environment.bloomThreshold",
Window::mainWindow->currentScene->environment.lightBloom.threshold);

commandBuffer->bindDrawingState(quadState);
commandBuffer->bindPipeline(lightPipeline);
Expand Down
7 changes: 6 additions & 1 deletion cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,12 @@ use atlas_cli::*;
use clap::Parser;

#[derive(Parser)]
#[command(name = "atlas")]
#[command(
name = "atlas",
version = "Release Candidate for Beta 1",
about = "Atlas (Release Candidate for Beta 1)",
arg_required_else_help = true
)]
pub struct Cli {
#[command(subcommand)]
command: Commands,
Expand Down
1 change: 1 addition & 0 deletions editor/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ qt_add_resources(AtlasEditor "atlas_editor_assets"
FILES
"${CMAKE_SOURCE_DIR}/editor/assets/Icon-iOS-Default-1024x1024@1x.png"
"${CMAKE_SOURCE_DIR}/editor/assets/iconFile-iOS-Dark-1024x1024@1x.png"
"${CMAKE_SOURCE_DIR}/editor/assets/atlas_ball_bright.png"
"${CMAKE_SOURCE_DIR}/editor/assets/Manrope-VariableFont_wght.ttf"
"${CMAKE_SOURCE_DIR}/editor/assets/Phosphor.ttf"
"${CMAKE_SOURCE_DIR}/editor/assets/PHOSPHOR_LICENSE.txt"
Expand Down
59 changes: 56 additions & 3 deletions editor/views/editor/editor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
#include <QPlainTextEdit>
#include <QProcess>
#include <QProgressBar>
#include <QPixmap>
#include <QPushButton>
#include <QRegularExpression>
#include <QSaveFile>
Expand Down Expand Up @@ -646,6 +647,12 @@ void EditorWindow::setupDocks() {
materialEditorPanel = new MaterialEditorPanel(viewportPanel);
postProcessingPanel = new PostProcessingPanel(viewportPanel);
graphiteEditorPanel = new GraphiteEditorPanel(viewportPanel, projectFile);
connect(materialEditorPanel, &MaterialEditorPanel::materialSaved, this,
[this](const QString &) { workspaceChangesPending = true; });
connect(postProcessingPanel, &PostProcessingPanel::settingsChanged, this,
[this] { workspaceChangesPending = true; });
connect(graphiteEditorPanel, &GraphiteEditorPanel::documentSaved, this,
[this] { workspaceChangesPending = true; });
workspaceStack = new QStackedWidget(this);
workspaceStack->setObjectName("editorWorkspaceStack");
workspaceStack->addWidget(viewportTools);
Expand Down Expand Up @@ -689,11 +696,44 @@ void EditorWindow::setupDocks() {
.area = EditorDockArea::Bottom,
.icon = styling::icon(styling::Icon::FolderOpen, "#7E929C")});

runtimeErrors = new QPlainTextEdit(this);
runtimeErrors->setObjectName("runtimeErrors");
runtimeErrors->setReadOnly(true);
runtimeErrors->setLineWrapMode(QPlainTextEdit::NoWrap);
runtimeErrors->setPlaceholderText(
"Runtime and script errors will appear here.");
auto *errorDock = dockManager->addPanel(
{.id = "errors",
.title = "Errors",
.widget = runtimeErrors,
.area = EditorDockArea::Bottom,
.icon = styling::icon(styling::Icon::Warning, "#A17F7F")});
if (contentDock->dockAreaWidget() != nullptr) {
coreManager->addDockWidget(ads::CenterDockWidgetArea, errorDock,
contentDock->dockAreaWidget());
}
errorDock->toggleView(false);
connect(viewportPanel, &ViewportPanel::runtimeErrorOccurred, this,
[this, errorDock](const QString &message) {
const QString normalized = message.trimmed();
if (normalized.isEmpty())
return;
runtimeErrors->appendPlainText(
QStringLiteral("[%1] %2")
.arg(QDateTime::currentDateTime().toString("HH:mm:ss"),
normalized));
errorDock->toggleView(true);
errorDock->setAsCurrentTab();
errorDock->raise();
statusBar()->showMessage("Runtime error reported", 5000);
});

workspaceDock->setAsCurrentTab();
defaultDockState = coreManager->saveState(DockStateVersion);

const QList<ads::CDockWidget *> managedDocks{workspaceDock, hierarchyDock,
inspectorDock, contentDock};
inspectorDock, contentDock,
errorDock};
for (ads::CDockWidget *dock : managedDocks) {
connect(dock, &ads::CDockWidget::topLevelChanged, this,
[this](bool) { scheduleLayoutSave(); });
Expand All @@ -718,7 +758,8 @@ void EditorWindow::setupDocks() {
{"Workspace", workspaceDock},
{"Hierarchy", hierarchyDock},
{"Inspector", inspectorDock},
{"Content Browser", contentDock}};
{"Content Browser", contentDock},
{"Errors", errorDock}};
for (int index = 0; index < panels.size(); ++index) {
const auto &[name, dock] = panels.at(index);
auto *action = windowMenu->addAction(
Expand Down Expand Up @@ -829,7 +870,9 @@ void EditorWindow::setupWorkspaceBar() {
identityLayout->setSpacing(9);
auto *mark = new QLabel(identity);
mark->setObjectName("workspaceMark");
mark->setPixmap(windowIcon().pixmap(24, 24));
mark->setPixmap(QPixmap(":/editor/assets/atlas_ball_bright.png")
.scaled(24, 24, Qt::KeepAspectRatio,
Qt::SmoothTransformation));
auto *identityText = new QWidget(identity);
identityText->setObjectName("workspaceIdentityText");
auto *identityTextLayout = new QVBoxLayout(identityText);
Expand Down Expand Up @@ -1034,7 +1077,17 @@ void EditorWindow::activateWorkspace(int index) {
if (workspaceStack == nullptr || index < 0 ||
index >= workspaceStack->count())
return;
const int previousIndex = workspaceStack->currentIndex();
if (index == 0 && previousIndex == 1 && materialEditorPanel != nullptr)
materialEditorPanel->flushPendingSave();
if (index == 0 && previousIndex == 3 && graphiteEditorPanel != nullptr)
graphiteEditorPanel->flushPendingSave();
workspaceStack->setCurrentIndex(index);
if (index == 0 && previousIndex != 0 && workspaceChangesPending &&
viewportPanel != nullptr) {
workspaceChangesPending = false;
viewportPanel->reloadRuntime();
}
if (workspaceModeGroup != nullptr) {
if (auto *button = workspaceModeGroup->button(index)) {
button->setChecked(true);
Expand Down
11 changes: 10 additions & 1 deletion editor/views/editor/graphiteEditor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -573,6 +573,7 @@ GraphiteEditorPanel::~GraphiteEditorPanel() {
}

void GraphiteEditorPanel::showEmptyState() {
documentDirty = false;
document = QJsonObject{{"format", "atlas.graphite.ui"},
{"version", 1},
{"canvas", QJsonObject{{"width", 1280},
Expand Down Expand Up @@ -635,6 +636,7 @@ void GraphiteEditorPanel::openUI(const QString &path) {
}
}
document = next;
documentDirty = repaired;
undoStack->clear();
titleLabel->setText(QFileInfo(uiPath).completeBaseName());
statusLabel->setText("Ready");
Expand All @@ -655,7 +657,14 @@ void GraphiteEditorPanel::saveUI() {
QMessageBox::warning(this, "Graphite", "The UI asset could not be saved.");
return;
}
documentDirty = false;
statusLabel->setText("Saved");
emit documentSaved();
}

void GraphiteEditorPanel::flushPendingSave() {
if (documentDirty)
saveUI();
}

void GraphiteEditorPanel::undo() { undoStack->undo(); }
Expand Down Expand Up @@ -720,6 +729,7 @@ void GraphiteEditorPanel::setDocument(const QJsonObject &next, bool recordUndo)
canvas->setDocument(document, uiPath);
canvas->setSelectedPath(path);
rebuildInspector();
documentDirty = true;
statusLabel->setText("Modified");
};
if (recordUndo)
Expand Down Expand Up @@ -1387,5 +1397,4 @@ void GraphiteEditorPanel::attachToScene(bool preview) {
});
emit previewRequested();
}
viewport->reloadRuntime();
}
14 changes: 6 additions & 8 deletions editor/views/editor/inspector.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,8 @@ QJsonObject environmentSchema() {
{"weight", 0.02},
{"decay", 0.95},
{"exposure", 0.7}}},
{"lightBloom", QJsonObject{{"radius", 0.01}, {"maxSamples", 6}}},
{"lightBloom",
QJsonObject{{"threshold", 0.8}, {"radius", 0.01}, {"maxSamples", 6}}},
{"rimLight", QJsonObject{{"intensity", 0.0},
{"color", QJsonArray{1.0, 0.96, 0.86}}}},
{"atmosphere",
Expand Down Expand Up @@ -1621,13 +1622,10 @@ void InspectorPanel::showObject(const QJsonObject &object) {
auto update = [runtime, objectId](const QString &component, int index,
const QString &path,
const QJsonValue &value) {
QTimer::singleShot(0,
[runtime, objectId, component, index, path, value] {
if (runtime != nullptr) {
runtime->setRuntimeObjectProperty(
objectId, component, index, path, value);
}
});
if (runtime != nullptr) {
runtime->setRuntimeObjectProperty(objectId, component, index, path,
value);
}
};
QJsonObject transform{{"position", object.value("position")},
{"rotation", object.value("rotation")},
Expand Down
70 changes: 65 additions & 5 deletions editor/views/editor/materialEditor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#include <QLineEdit>
#include <QList>
#include <QMessageBox>
#include <QMouseEvent>
#include <QPaintEngine>
#include <QPixmap>
#include <QPushButton>
Expand All @@ -47,6 +48,9 @@
#include <utility>

namespace {
constexpr int MaterialPreviewPathTracingFrames = 24;
constexpr int MaterialPreviewPathTracingIntervalMs = 50;

QColor jsonColor(const QJsonValue &value, const QColor &fallback) {
const QJsonArray array = value.toArray();
if (array.size() < 3) {
Expand Down Expand Up @@ -174,10 +178,51 @@ class MaterialPreviewWidget : public QWidget {
scheduleFrame();
}

void mousePressEvent(QMouseEvent *event) override {
if (event->button() == Qt::LeftButton) {
rotating = true;
lastPointer = event->position();
setCursor(Qt::ClosedHandCursor);
event->accept();
return;
}
QWidget::mousePressEvent(event);
}

void mouseMoveEvent(QMouseEvent *event) override {
if (rotating && runtimeContext != nullptr) {
const QPointF delta = event->position() - lastPointer;
lastPointer = event->position();
if (runtimeContext->rotateMaterialPreview(
static_cast<float>(delta.x() * 0.55),
static_cast<float>(delta.y() * 0.55))) {
scheduleFrame();
}
event->accept();
return;
}
QWidget::mouseMoveEvent(event);
}

void mouseReleaseEvent(QMouseEvent *event) override {
if (event->button() == Qt::LeftButton && rotating) {
rotating = false;
unsetCursor();
event->accept();
return;
}
QWidget::mouseReleaseEvent(event);
}

private:
void scheduleFrame() {
pendingFrames = std::max(pendingFrames, 2);
if (isVisible() && frameTimer != nullptr) {
const int requestedFrames =
runtimeContext != nullptr &&
runtimeContext->materialPreviewUsesPathTracing()
? MaterialPreviewPathTracingFrames
: 2;
pendingFrames = std::max(pendingFrames, requestedFrames);
if (isVisible() && frameTimer != nullptr && !frameTimer->isActive()) {
frameTimer->start(0);
}
}
Expand All @@ -200,6 +245,10 @@ class MaterialPreviewWidget : public QWidget {
return;
}
resizeRuntime();
if (runtimeContext->materialPreviewUsesPathTracing()) {
pendingFrames =
std::max(pendingFrames, MaterialPreviewPathTracingFrames);
}
} catch (const std::exception &error) {
qWarning().noquote()
<< QStringLiteral("Failed to start runtime material preview: %1")
Expand Down Expand Up @@ -241,8 +290,12 @@ class MaterialPreviewWidget : public QWidget {
return;
}
pendingFrames = std::max(0, pendingFrames - 1);
if (pendingFrames > 0 && isVisible())
frameTimer->start(1);
if (pendingFrames > 0 && isVisible()) {
frameTimer->start(
runtimeContext->materialPreviewUsesPathTracing()
? MaterialPreviewPathTracingIntervalMs
: 1);
}
} catch (const std::exception &error) {
qWarning().noquote()
<< QStringLiteral("Runtime material preview frame failed: %1")
Expand Down Expand Up @@ -277,6 +330,8 @@ class MaterialPreviewWidget : public QWidget {
int runtimeHeight = 0;
int environmentMode = 0;
int pendingFrames = 0;
QPointF lastPointer;
bool rotating = false;
};

MaterialEditorPanel::MaterialEditorPanel(ViewportPanel *viewport,
Expand Down Expand Up @@ -337,6 +392,11 @@ MaterialEditorPanel::~MaterialEditorPanel() {
}
}

void MaterialEditorPanel::flushPendingSave() {
if (saveTimer->isActive())
saveMaterial();
}

QJsonObject
MaterialEditorPanel::normalizedMaterial(const QJsonObject &source) const {
QJsonObject result = source;
Expand Down Expand Up @@ -445,7 +505,7 @@ void MaterialEditorPanel::showMaterial() {
previewOptionsLayout->setContentsMargins(0, 0, 0, 0);
auto *previewLabel = new QLabel("Preview Environment", previewOptions);
auto *environment = new QComboBox(previewOptions);
environment->addItems({"Studio", "Sunset", "Open Sky"});
environment->addItems({"Studio", "Sky", "Empty"});
previewOptionsLayout->addWidget(previewLabel);
previewOptionsLayout->addStretch();
previewOptionsLayout->addWidget(environment);
Expand Down
Loading
Loading