增加摄像机
This commit is contained in:
@@ -15,6 +15,7 @@
|
||||
#include "props/BlackholePropertySection.h"
|
||||
#include "props/EntityPropertySection.h"
|
||||
#include "props/ToolPropertySection.h"
|
||||
#include "props/CameraPropertySection.h"
|
||||
#include "timeline/TimelineWidget.h"
|
||||
#include "dialogs/FrameAnimationDialog.h"
|
||||
#include "dialogs/EntityIntroPopup.h"
|
||||
@@ -559,7 +560,7 @@ void MainWindow::createTimelineDock() {
|
||||
// 合并后的关键帧按钮:一次写入位置 + userScale
|
||||
auto* btnKeyCombined = new QToolButton(bar);
|
||||
setToolButtonIconOrText(btnKeyCombined, QStringLiteral("media-record"), QStringLiteral("关键帧"));
|
||||
btnKeyCombined->setToolTip(QStringLiteral("在当前帧记录实体的位置与缩放关键帧"));
|
||||
btnKeyCombined->setToolTip(QStringLiteral("在当前帧记录实体或摄像机的位置与缩放关键帧"));
|
||||
polishCompactToolButton(btnKeyCombined, 34);
|
||||
layout->addWidget(btnKeyCombined);
|
||||
|
||||
@@ -611,7 +612,27 @@ void MainWindow::createTimelineDock() {
|
||||
toolOps.push_back(rt.opacity);
|
||||
}
|
||||
m_editorCanvas->setTools(tools, toolOps);
|
||||
QVector<core::Project::Camera> cams;
|
||||
cams.reserve(rf.cameras.size());
|
||||
for (const auto& rc : rf.cameras) {
|
||||
cams.push_back(rc.camera);
|
||||
}
|
||||
m_editorCanvas->setCameraOverlays(cams, m_selectedCameraId, m_tempHiddenCameraIds);
|
||||
m_editorCanvas->setTempHiddenIds(m_tempHiddenEntityIds, m_tempHiddenToolIds);
|
||||
const bool presentation = m_previewRequested && m_workspace.hasBackground();
|
||||
m_editorCanvas->setPreviewCameraViewLocked(false);
|
||||
if (presentation) {
|
||||
const QString acid = m_workspace.project().activeCameraId();
|
||||
if (!acid.isEmpty()) {
|
||||
for (const auto& rc : rf.cameras) {
|
||||
if (rc.camera.id == acid) {
|
||||
m_editorCanvas->setPreviewCameraViewLocked(true);
|
||||
m_editorCanvas->applyCameraViewport(rc.camera.centerWorld, rc.camera.viewScale);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
m_timelineScrubbing = false;
|
||||
} else if (m_editorCanvas) {
|
||||
m_editorCanvas->setCurrentFrame(m_currentFrame);
|
||||
@@ -710,7 +731,9 @@ void MainWindow::createTimelineDock() {
|
||||
|
||||
const bool entityKeyUi = (m_workspace.isOpen() && !m_selectedEntityId.isEmpty());
|
||||
const bool toolKeyUi = (m_workspace.isOpen() && m_hasSelectedTool && !m_selectedToolId.isEmpty());
|
||||
actDeleteKey->setEnabled(m_workspace.isOpen() && m_timeline->hasSelectedKeyframe() && (entityKeyUi || toolKeyUi));
|
||||
const bool cameraKeyUi = (m_workspace.isOpen() && m_hasSelectedCamera && !m_selectedCameraId.isEmpty());
|
||||
actDeleteKey->setEnabled(m_workspace.isOpen() && m_timeline->hasSelectedKeyframe() &&
|
||||
(entityKeyUi || toolKeyUi || cameraKeyUi));
|
||||
const int selA = m_timeline->selectionStart();
|
||||
const int selB = m_timeline->selectionEnd();
|
||||
const bool hasRange = (selA >= 0 && selB >= 0);
|
||||
@@ -734,10 +757,18 @@ void MainWindow::createTimelineDock() {
|
||||
bool ok = false;
|
||||
switch (m_timeline->selectedKeyKind()) {
|
||||
case TimelineWidget::KeyKind::Location:
|
||||
if (!m_selectedEntityId.isEmpty()) ok = m_workspace.removeEntityLocationKey(m_selectedEntityId, f);
|
||||
if (!m_selectedEntityId.isEmpty()) {
|
||||
ok = m_workspace.removeEntityLocationKey(m_selectedEntityId, f);
|
||||
} else if (m_hasSelectedCamera && !m_selectedCameraId.isEmpty()) {
|
||||
ok = m_workspace.removeCameraLocationKey(m_selectedCameraId, f);
|
||||
}
|
||||
break;
|
||||
case TimelineWidget::KeyKind::UserScale:
|
||||
if (!m_selectedEntityId.isEmpty()) ok = m_workspace.removeEntityUserScaleKey(m_selectedEntityId, f);
|
||||
if (!m_selectedEntityId.isEmpty()) {
|
||||
ok = m_workspace.removeEntityUserScaleKey(m_selectedEntityId, f);
|
||||
} else if (m_hasSelectedCamera && !m_selectedCameraId.isEmpty()) {
|
||||
ok = m_workspace.removeCameraScaleKey(m_selectedCameraId, f);
|
||||
}
|
||||
break;
|
||||
case TimelineWidget::KeyKind::Image:
|
||||
if (!m_selectedEntityId.isEmpty()) ok = m_workspace.removeEntityImageFrame(m_selectedEntityId, f);
|
||||
@@ -1007,16 +1038,33 @@ void MainWindow::syncPreviewPlaybackBar() {
|
||||
}
|
||||
|
||||
void MainWindow::onInsertCombinedKey() {
|
||||
if (m_selectedEntityId.isEmpty() || !m_editorCanvas) {
|
||||
if (!m_editorCanvas || !m_workspace.isOpen()) {
|
||||
return;
|
||||
}
|
||||
const int lf = m_currentFrame % core::Project::kClipFixedFrames;
|
||||
if (m_hasSelectedCamera && !m_selectedCameraId.isEmpty()) {
|
||||
const core::eval::ResolvedProjectFrame rf =
|
||||
core::eval::evaluateAtFrame(m_workspace.project(), m_currentFrame, 10);
|
||||
for (const auto& rc : rf.cameras) {
|
||||
if (rc.camera.id == m_selectedCameraId) {
|
||||
m_workspace.setCameraLocationKey(m_selectedCameraId, lf, rc.camera.centerWorld);
|
||||
m_workspace.setCameraScaleKey(m_selectedCameraId, lf, rc.camera.viewScale);
|
||||
refreshEditorPage();
|
||||
return;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (m_selectedEntityId.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
// 位置关键帧:使用当前帧下的动画原点
|
||||
const QPointF o = m_editorCanvas->selectedAnimatedOriginWorld();
|
||||
m_workspace.setEntityLocationKey(m_selectedEntityId, m_currentFrame % core::Project::kClipFixedFrames, o);
|
||||
m_workspace.setEntityLocationKey(m_selectedEntityId, lf, o);
|
||||
|
||||
// 缩放关键帧:使用当前帧下的 userScale(而非 depthScale01)
|
||||
const double s = m_editorCanvas->selectedUserScale();
|
||||
m_workspace.setEntityUserScaleKey(m_selectedEntityId, m_currentFrame % core::Project::kClipFixedFrames, s);
|
||||
m_workspace.setEntityUserScaleKey(m_selectedEntityId, lf, s);
|
||||
|
||||
refreshEditorPage();
|
||||
}
|
||||
@@ -1380,6 +1428,7 @@ void MainWindow::createProjectTreeDock() {
|
||||
m_bgPropertySection = new gui::BackgroundPropertySection();
|
||||
m_blackholePropertySection = new gui::BlackholePropertySection();
|
||||
m_entityPropertySection = new gui::EntityPropertySection();
|
||||
m_cameraPropertySection = new gui::CameraPropertySection();
|
||||
m_toolPropertySection = new gui::ToolPropertySection();
|
||||
m_propertyStack = new QStackedWidget();
|
||||
m_propertyStack->setContentsMargins(4, 4, 4, 4);
|
||||
@@ -1387,6 +1436,7 @@ void MainWindow::createProjectTreeDock() {
|
||||
m_propertyStack->addWidget(m_bgPropertySection);
|
||||
m_propertyStack->addWidget(m_blackholePropertySection);
|
||||
m_propertyStack->addWidget(m_entityPropertySection);
|
||||
m_propertyStack->addWidget(m_cameraPropertySection);
|
||||
m_propertyStack->addWidget(m_toolPropertySection);
|
||||
|
||||
connect(m_bgPropertySection, &gui::BackgroundPropertySection::backgroundVisibleToggled, this, [this](bool on) {
|
||||
@@ -1600,6 +1650,37 @@ void MainWindow::createProjectTreeDock() {
|
||||
refreshEditorPage();
|
||||
});
|
||||
|
||||
connect(m_cameraPropertySection, &gui::CameraPropertySection::displayNameCommitted, this, [this](const QString& text) {
|
||||
if (m_selectedCameraId.isEmpty() || !m_workspace.isOpen()) return;
|
||||
if (!m_workspace.setCameraDisplayName(m_selectedCameraId, text)) return;
|
||||
refreshProjectTree();
|
||||
refreshPropertyPanel();
|
||||
});
|
||||
connect(m_cameraPropertySection, &gui::CameraPropertySection::centerEdited, this, [this](double x, double y) {
|
||||
if (m_selectedCameraId.isEmpty() || !m_workspace.isOpen()) return;
|
||||
if (!m_workspace.setCameraCenterWorld(m_selectedCameraId, QPointF(x, y))) return;
|
||||
refreshEditorPage();
|
||||
});
|
||||
connect(m_cameraPropertySection, &gui::CameraPropertySection::viewScaleEdited, this, [this](double vs) {
|
||||
if (m_selectedCameraId.isEmpty() || !m_workspace.isOpen()) return;
|
||||
const int f = std::clamp(m_currentFrame, 0, core::Project::kClipFixedFrames - 1);
|
||||
if (!m_workspace.setCameraViewScaleValue(m_selectedCameraId, vs, f)) return;
|
||||
refreshEditorPage();
|
||||
refreshDopeSheet();
|
||||
});
|
||||
connect(m_cameraPropertySection, &gui::CameraPropertySection::activePreviewToggled, this, [this](bool on) {
|
||||
if (m_selectedCameraId.isEmpty() || !m_workspace.isOpen()) return;
|
||||
if (on) {
|
||||
if (!m_workspace.setActiveCameraId(m_selectedCameraId)) return;
|
||||
} else {
|
||||
if (m_workspace.project().activeCameraId() == m_selectedCameraId) {
|
||||
if (!m_workspace.setActiveCameraId(QString())) return;
|
||||
}
|
||||
}
|
||||
refreshEditorPage();
|
||||
refreshPropertyPanel();
|
||||
});
|
||||
|
||||
auto* propScroll = new SpinFriendlyScrollArea(m_dockProperties);
|
||||
propScroll->setWidgetResizable(true);
|
||||
propScroll->setFrameShape(QFrame::NoFrame);
|
||||
@@ -1652,7 +1733,7 @@ void MainWindow::createProjectTreeDock() {
|
||||
|
||||
void MainWindow::refreshPropertyPanel() {
|
||||
if (!m_bgPropertySection || !m_blackholePropertySection || !m_entityPropertySection ||
|
||||
!m_toolPropertySection || !m_propertyStack) {
|
||||
!m_cameraPropertySection || !m_toolPropertySection || !m_propertyStack) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1735,8 +1816,31 @@ void MainWindow::refreshPropertyPanel() {
|
||||
return cur;
|
||||
};
|
||||
|
||||
const bool cameraUi = m_hasSelectedCamera && m_workspace.isOpen() && !m_selectedCameraId.isEmpty();
|
||||
if (cameraUi) {
|
||||
m_entityPropertySection->clearDisconnected();
|
||||
m_toolPropertySection->clearDisconnected();
|
||||
gui::CameraPropertyUiState st;
|
||||
const auto rf = core::eval::evaluateAtFrame(m_workspace.project(), m_currentFrame, 10);
|
||||
for (const auto& rc : rf.cameras) {
|
||||
if (rc.camera.id != m_selectedCameraId) {
|
||||
continue;
|
||||
}
|
||||
st.displayName = rc.camera.displayName.isEmpty() ? rc.camera.id : rc.camera.displayName;
|
||||
st.centerWorld = rc.camera.centerWorld;
|
||||
st.viewScale = rc.camera.viewScale;
|
||||
st.isActivePreviewCamera = (m_workspace.project().activeCameraId() == m_selectedCameraId);
|
||||
break;
|
||||
}
|
||||
m_cameraPropertySection->applyState(st);
|
||||
m_propertyStack->setCurrentWidget(m_cameraPropertySection);
|
||||
m_dockProperties->setWindowTitle(QStringLiteral("属性 — 摄像机"));
|
||||
return;
|
||||
}
|
||||
|
||||
const bool toolUi = m_hasSelectedTool && m_workspace.isOpen() && !m_selectedToolId.isEmpty();
|
||||
if (toolUi) {
|
||||
m_cameraPropertySection->clearDisconnected();
|
||||
gui::ToolPropertyUiState st;
|
||||
const int f = std::clamp(m_currentFrame, 0, core::Project::kClipFixedFrames - 1);
|
||||
const auto* clip = activeClipForUi();
|
||||
@@ -1796,6 +1900,7 @@ void MainWindow::refreshPropertyPanel() {
|
||||
|
||||
const bool holeUi = m_workspace.isOpen() && !m_selectedBlackholeEntityId.isEmpty();
|
||||
if (holeUi) {
|
||||
m_cameraPropertySection->clearDisconnected();
|
||||
gui::BlackholePropertyUiState st;
|
||||
for (const auto& e : m_workspace.entities()) {
|
||||
if (e.id != m_selectedBlackholeEntityId) {
|
||||
@@ -1826,11 +1931,13 @@ void MainWindow::refreshPropertyPanel() {
|
||||
if (!entUi) {
|
||||
m_entityPropertySection->clearDisconnected();
|
||||
m_toolPropertySection->clearDisconnected();
|
||||
m_cameraPropertySection->clearDisconnected();
|
||||
m_propertyStack->setCurrentWidget(m_bgPropertySection);
|
||||
m_dockProperties->setWindowTitle(QStringLiteral("属性 — 背景"));
|
||||
return;
|
||||
}
|
||||
|
||||
m_cameraPropertySection->clearDisconnected();
|
||||
QString displayName;
|
||||
double userScale = 1.0;
|
||||
bool ignoreDist = false;
|
||||
@@ -2147,6 +2254,28 @@ void MainWindow::refreshProjectTree() {
|
||||
};
|
||||
|
||||
addSubtree(QString(), nullptr);
|
||||
|
||||
if (m_workspace.isOpen()) {
|
||||
for (const auto& c : m_workspace.cameras()) {
|
||||
auto* it = new QTreeWidgetItem(m_projectTree);
|
||||
it->setText(1, c.displayName.isEmpty() ? QStringLiteral("摄像机") : c.displayName);
|
||||
it->setTextAlignment(1, Qt::AlignRight | Qt::AlignVCenter);
|
||||
it->setData(0, Qt::UserRole, QStringLiteral("camera"));
|
||||
it->setData(0, Qt::UserRole + 1, c.id);
|
||||
it->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
|
||||
auto* eye = makeEye(!m_tempHiddenCameraIds.contains(c.id), true);
|
||||
m_projectTree->setItemWidget(it, 0, eye);
|
||||
connect(eye, &QToolButton::toggled, this, [this, id = c.id](bool on) {
|
||||
if (!m_workspace.isOpen()) return;
|
||||
if (on) m_tempHiddenCameraIds.remove(id);
|
||||
else m_tempHiddenCameraIds.insert(id);
|
||||
if (m_editorCanvas) {
|
||||
m_editorCanvas->setTempHiddenCameraIds(m_tempHiddenCameraIds);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
m_projectTree->expandAll();
|
||||
|
||||
if (m_projectTree->header()) {
|
||||
@@ -2165,6 +2294,7 @@ void MainWindow::syncProjectTreeFromCanvasSelection() {
|
||||
m_projectTree->blockSignals(true);
|
||||
if ((!m_hasSelectedEntity || m_selectedEntityId.isEmpty()) &&
|
||||
(!m_hasSelectedTool || m_selectedToolId.isEmpty()) &&
|
||||
(!m_hasSelectedCamera || m_selectedCameraId.isEmpty()) &&
|
||||
m_selectedBlackholeEntityId.isEmpty()) {
|
||||
m_projectTree->clearSelection();
|
||||
} else {
|
||||
@@ -2181,6 +2311,11 @@ void MainWindow::syncProjectTreeFromCanvasSelection() {
|
||||
found = node;
|
||||
break;
|
||||
}
|
||||
if (m_hasSelectedCamera && !m_selectedCameraId.isEmpty() && kind == QStringLiteral("camera") &&
|
||||
id == m_selectedCameraId) {
|
||||
found = node;
|
||||
break;
|
||||
}
|
||||
if (!m_selectedBlackholeEntityId.isEmpty() && kind == QStringLiteral("blackhole") &&
|
||||
id == m_selectedBlackholeEntityId) {
|
||||
found = node;
|
||||
@@ -2213,9 +2348,14 @@ void MainWindow::onProjectTreeItemClicked(QTreeWidgetItem* item, int column) {
|
||||
}
|
||||
m_hasSelectedTool = false;
|
||||
m_selectedToolId.clear();
|
||||
m_hasSelectedCamera = false;
|
||||
m_selectedCameraId.clear();
|
||||
if (m_timeline) {
|
||||
m_timeline->setToolKeyframeTracks({}, {});
|
||||
}
|
||||
if (m_editorCanvas) {
|
||||
m_editorCanvas->clearCameraSelection();
|
||||
}
|
||||
m_editorCanvas->selectEntityById(id);
|
||||
}
|
||||
} else if (kind == QStringLiteral("tool")) {
|
||||
@@ -2229,8 +2369,33 @@ void MainWindow::onProjectTreeItemClicked(QTreeWidgetItem* item, int column) {
|
||||
m_selectedToolId = id;
|
||||
m_hasSelectedEntity = false;
|
||||
m_selectedEntityId.clear();
|
||||
m_hasSelectedCamera = false;
|
||||
m_selectedCameraId.clear();
|
||||
if (m_editorCanvas) {
|
||||
m_editorCanvas->clearEntitySelection();
|
||||
m_editorCanvas->clearCameraSelection();
|
||||
}
|
||||
if (m_timeline) {
|
||||
updateTimelineTracks();
|
||||
}
|
||||
refreshPropertyPanel();
|
||||
}
|
||||
} else if (kind == QStringLiteral("camera")) {
|
||||
const QString id = item->data(0, Qt::UserRole + 1).toString();
|
||||
if (!id.isEmpty()) {
|
||||
m_selectedBlackholeEntityId.clear();
|
||||
if (m_editorCanvas) {
|
||||
m_editorCanvas->clearBlackholeSelection();
|
||||
}
|
||||
m_hasSelectedTool = false;
|
||||
m_selectedToolId.clear();
|
||||
m_hasSelectedEntity = false;
|
||||
m_selectedEntityId.clear();
|
||||
m_hasSelectedCamera = true;
|
||||
m_selectedCameraId = id;
|
||||
if (m_editorCanvas) {
|
||||
m_editorCanvas->clearEntitySelection();
|
||||
m_editorCanvas->selectCameraById(id);
|
||||
}
|
||||
if (m_timeline) {
|
||||
updateTimelineTracks();
|
||||
@@ -2246,8 +2411,11 @@ void MainWindow::onProjectTreeItemClicked(QTreeWidgetItem* item, int column) {
|
||||
m_hasSelectedEntity = false;
|
||||
m_selectedEntityId.clear();
|
||||
m_selectedEntityDisplayNameCache.clear();
|
||||
m_hasSelectedCamera = false;
|
||||
m_selectedCameraId.clear();
|
||||
if (m_editorCanvas) {
|
||||
m_editorCanvas->clearEntitySelection();
|
||||
m_editorCanvas->clearCameraSelection();
|
||||
m_editorCanvas->selectBlackholeByEntityId(entityId);
|
||||
}
|
||||
updateTimelineTracks();
|
||||
@@ -2257,8 +2425,11 @@ void MainWindow::onProjectTreeItemClicked(QTreeWidgetItem* item, int column) {
|
||||
m_selectedBlackholeEntityId.clear();
|
||||
m_hasSelectedTool = false;
|
||||
m_selectedToolId.clear();
|
||||
m_hasSelectedCamera = false;
|
||||
m_selectedCameraId.clear();
|
||||
m_editorCanvas->clearEntitySelection();
|
||||
m_editorCanvas->clearBlackholeSelection();
|
||||
m_editorCanvas->clearCameraSelection();
|
||||
updateTimelineTracks();
|
||||
}
|
||||
}
|
||||
@@ -2751,6 +2922,8 @@ void MainWindow::rebuildCentralPages() {
|
||||
m_selectedEntityOrigin = origin;
|
||||
m_hasSelectedTool = false;
|
||||
m_selectedToolId.clear();
|
||||
m_hasSelectedCamera = false;
|
||||
m_selectedCameraId.clear();
|
||||
if (hasSel && !id.isEmpty()) {
|
||||
for (const auto& e : m_workspace.entities()) {
|
||||
if (e.id == id) {
|
||||
@@ -2776,6 +2949,8 @@ void MainWindow::rebuildCentralPages() {
|
||||
m_hasSelectedEntity = false;
|
||||
m_selectedEntityId.clear();
|
||||
m_selectedEntityDisplayNameCache.clear();
|
||||
m_hasSelectedCamera = false;
|
||||
m_selectedCameraId.clear();
|
||||
}
|
||||
updateTimelineTracks();
|
||||
if (!m_timelineScrubbing) {
|
||||
@@ -3176,6 +3351,50 @@ void MainWindow::rebuildCentralPages() {
|
||||
refreshProjectTree();
|
||||
updateUiEnabledState();
|
||||
});
|
||||
connect(m_editorCanvas, &EditorCanvas::requestMoveCamera, this, [this](const QString& id, const QPointF& delta) {
|
||||
const bool autoKey = true;
|
||||
if (!m_workspace.moveCameraBy(id, delta, m_currentFrame % core::Project::kClipFixedFrames, autoKey)) {
|
||||
return;
|
||||
}
|
||||
refreshEditorPage();
|
||||
refreshProjectTree();
|
||||
updateUiEnabledState();
|
||||
});
|
||||
connect(m_editorCanvas, &EditorCanvas::requestCameraViewScaleAdjust, this, [this](const QString& id, double factor) {
|
||||
if (id.isEmpty() || !m_workspace.isOpen()) return;
|
||||
const core::eval::ResolvedProjectFrame resFrame =
|
||||
core::eval::evaluateAtFrame(m_workspace.project(), m_currentFrame, 10);
|
||||
for (const auto& rc : resFrame.cameras) {
|
||||
if (rc.camera.id != id) continue;
|
||||
const double ns = std::clamp(rc.camera.viewScale * factor, 1e-6, 1e3);
|
||||
const int kf = std::clamp(m_currentFrame, 0, core::Project::kClipFixedFrames - 1);
|
||||
if (!m_workspace.setCameraViewScaleValue(id, ns, kf)) return;
|
||||
refreshEditorPage();
|
||||
refreshDopeSheet();
|
||||
return;
|
||||
}
|
||||
});
|
||||
connect(m_editorCanvas, &EditorCanvas::selectedCameraChanged, this,
|
||||
[this](bool hasSel, const QString& id, const QPointF& centerWorld, double viewScale) {
|
||||
Q_UNUSED(centerWorld);
|
||||
Q_UNUSED(viewScale);
|
||||
m_hasSelectedCamera = hasSel;
|
||||
m_selectedCameraId = id;
|
||||
if (hasSel) {
|
||||
m_hasSelectedEntity = false;
|
||||
m_selectedEntityId.clear();
|
||||
m_selectedEntityDisplayNameCache.clear();
|
||||
m_hasSelectedTool = false;
|
||||
m_selectedToolId.clear();
|
||||
m_selectedBlackholeEntityId.clear();
|
||||
}
|
||||
updateTimelineTracks();
|
||||
if (!m_timelineScrubbing) {
|
||||
updateStatusBarText();
|
||||
refreshPropertyPanel();
|
||||
syncProjectTreeFromCanvasSelection();
|
||||
}
|
||||
});
|
||||
connect(m_editorCanvas, &EditorCanvas::requestResolveBlackholeCopy, this,
|
||||
[this](const QString& entityId, const QPoint& sourceOffsetPx) {
|
||||
if (!m_workspace.resolveBlackholeByCopyBackground(entityId, sourceOffsetPx, true)) {
|
||||
@@ -3406,12 +3625,32 @@ void MainWindow::refreshEditorPage() {
|
||||
opacities.push_back(rt.opacity);
|
||||
}
|
||||
m_editorCanvas->setTools(tools, opacities);
|
||||
QVector<core::Project::Camera> cams;
|
||||
cams.reserve(rf.cameras.size());
|
||||
for (const auto& rc : rf.cameras) {
|
||||
cams.push_back(rc.camera);
|
||||
}
|
||||
m_editorCanvas->setCameraOverlays(cams, m_selectedCameraId, m_tempHiddenCameraIds);
|
||||
m_editorCanvas->setCurrentFrame(m_currentFrame);
|
||||
m_editorCanvas->setTempHiddenIds(m_tempHiddenEntityIds, m_tempHiddenToolIds);
|
||||
m_editorCanvas->setPreviewCameraViewLocked(false);
|
||||
if (presentation) {
|
||||
const QString acid = m_workspace.project().activeCameraId();
|
||||
if (!acid.isEmpty()) {
|
||||
for (const auto& rc : rf.cameras) {
|
||||
if (rc.camera.id == acid) {
|
||||
m_editorCanvas->setPreviewCameraViewLocked(true);
|
||||
m_editorCanvas->applyCameraViewport(rc.camera.centerWorld, rc.camera.viewScale);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
updateTimelineTracks();
|
||||
|
||||
} else {
|
||||
m_editorCanvas->setEntities({}, {}, QString());
|
||||
m_editorCanvas->setCameraOverlays({}, QString(), {});
|
||||
if (m_timeline) {
|
||||
m_timeline->setKeyframeTracks({}, {}, {}, {});
|
||||
m_timeline->setToolKeyframeTracks({}, {});
|
||||
@@ -3438,11 +3677,13 @@ void MainWindow::updateTimelineTracks() {
|
||||
|
||||
const bool wantEntity = !m_selectedEntityId.isEmpty();
|
||||
const bool wantTool = (m_hasSelectedTool && !m_selectedToolId.isEmpty());
|
||||
const bool wantCamera = (m_hasSelectedCamera && !m_selectedCameraId.isEmpty());
|
||||
|
||||
// 未选中时不显示关键帧(按需求)
|
||||
if (!wantEntity) m_timeline->setKeyframeTracks({}, {}, {}, {});
|
||||
if (!wantTool) m_timeline->setToolKeyframeTracks({}, {});
|
||||
if (!wantEntity && !wantTool) return;
|
||||
if (!wantEntity && !wantTool && !wantCamera) {
|
||||
m_timeline->setKeyframeTracks({}, {}, {}, {});
|
||||
m_timeline->setToolKeyframeTracks({}, {});
|
||||
return;
|
||||
}
|
||||
|
||||
// 选择当前 clip(与 workspace 写入规则一致)
|
||||
const core::Project::AnimationClip* clip = nullptr;
|
||||
@@ -3513,7 +3754,16 @@ void MainWindow::updateTimelineTracks() {
|
||||
const auto im = clip->entityImageFrames.value(m_selectedEntityId);
|
||||
const auto vis = clip->entityVisibilityKeys.value(m_selectedEntityId);
|
||||
m_timeline->setKeyframeTracks(framesOfVec2(loc), framesOfDouble(sc), framesOfImage(im), framesOfBool(vis));
|
||||
} else if (wantCamera) {
|
||||
const auto loc = clip->cameraLocationKeys.value(m_selectedCameraId);
|
||||
const auto sc = clip->cameraScaleKeys.value(m_selectedCameraId);
|
||||
m_timeline->setKeyframeTracks(framesOfVec2(loc), framesOfDouble(sc), {}, {});
|
||||
} else {
|
||||
m_timeline->setKeyframeTracks({}, {}, {}, {});
|
||||
}
|
||||
|
||||
// 注意:未选中工具时不能调用 setToolKeyframeTracks({}, {}),其实现会清空 m_locFrames/m_scaleFrames,
|
||||
// 从而冲掉上面已为实体/摄像机写入的轨道数据。
|
||||
if (wantTool) {
|
||||
const auto loc = clip->toolLocationKeys.value(m_selectedToolId);
|
||||
const auto vis = clip->toolVisibilityKeys.value(m_selectedToolId);
|
||||
@@ -4082,6 +4332,9 @@ void MainWindow::onCloseProject() {
|
||||
m_selectedEntityDepth = 0;
|
||||
m_selectedEntityOrigin = QPointF();
|
||||
m_selectedEntityId.clear();
|
||||
m_hasSelectedCamera = false;
|
||||
m_selectedCameraId.clear();
|
||||
m_tempHiddenCameraIds.clear();
|
||||
m_currentFrame = 0;
|
||||
|
||||
statusBar()->showMessage(QStringLiteral("工程已关闭"));
|
||||
|
||||
@@ -37,6 +37,7 @@ class BackgroundPropertySection;
|
||||
class BlackholePropertySection;
|
||||
class EntityPropertySection;
|
||||
class ToolPropertySection;
|
||||
class CameraPropertySection;
|
||||
class EntityIntroPopup;
|
||||
class ResourceLibraryDock;
|
||||
}
|
||||
@@ -130,6 +131,7 @@ private:
|
||||
gui::BlackholePropertySection* m_blackholePropertySection = nullptr;
|
||||
gui::EntityPropertySection* m_entityPropertySection = nullptr;
|
||||
gui::ToolPropertySection* m_toolPropertySection = nullptr;
|
||||
gui::CameraPropertySection* m_cameraPropertySection = nullptr;
|
||||
QToolButton* m_btnCreateEntity = nullptr;
|
||||
ToolOptionPopup* m_createEntityPopup = nullptr;
|
||||
QToolButton* m_btnToggleDepthOverlay = nullptr;
|
||||
@@ -170,11 +172,13 @@ private:
|
||||
int m_lastWorldZ = -1;
|
||||
bool m_hasSelectedEntity = false;
|
||||
bool m_hasSelectedTool = false;
|
||||
bool m_hasSelectedCamera = false;
|
||||
bool m_syncingTreeSelection = false;
|
||||
int m_selectedEntityDepth = 0;
|
||||
QPointF m_selectedEntityOrigin;
|
||||
QString m_selectedEntityId;
|
||||
QString m_selectedToolId;
|
||||
QString m_selectedCameraId;
|
||||
QString m_selectedBlackholeEntityId;
|
||||
QString m_selectedEntityDisplayNameCache;
|
||||
QString m_bgAbsCache;
|
||||
@@ -182,6 +186,7 @@ private:
|
||||
// 项目树“眼睛”:仅用于画布临时隐藏(不持久化、不进时间轴)
|
||||
QSet<QString> m_tempHiddenEntityIds;
|
||||
QSet<QString> m_tempHiddenToolIds;
|
||||
QSet<QString> m_tempHiddenCameraIds;
|
||||
void updateStatusBarText();
|
||||
void syncCreateEntityToolButtonTooltip();
|
||||
void refreshPropertyPanel();
|
||||
|
||||
Reference in New Issue
Block a user