diff --git a/client/src/main.cpp b/client/src/main.cpp index 58b025b..fe13d52 100644 --- a/client/src/main.cpp +++ b/client/src/main.cpp @@ -42,6 +42,7 @@ int main() { .size = glm::vec3(512, 1.2, 512), .color = glm::vec3(0.388235, 0.372549, 0.384314), .anchored = true, + .locked = true, })); gWorkspace()->AddChild(lastPart = Part::New({ diff --git a/core/src/objects/part.cpp b/core/src/objects/part.cpp index b1f08d6..8f0fc4b 100644 --- a/core/src/objects/part.cpp +++ b/core/src/objects/part.cpp @@ -62,7 +62,7 @@ Part::Part(): Part(PartConstructParams { .color = Data::Color3(0.639216f, 0.6352 } Part::Part(PartConstructParams params): Instance(&TYPE), cframe(Data::CFrame(params.position, params.rotation)), - size(params.size), color(params.color), anchored(params.anchored) { + size(params.size), color(params.color), anchored(params.anchored), locked(params.locked) { this->memberMap = std::make_unique(MemberMap { .super = std::move(this->memberMap), .members = { @@ -71,6 +71,10 @@ Part::Part(PartConstructParams params): Instance(&TYPE), cframe(Data::CFrame(par .type = &Data::Bool::TYPE, .codec = fieldCodecOf(), .updateCallback = memberFunctionOf(&Part::onUpdated, this) + }}, { "Locked", { + .backingField = &locked, + .type = &Data::Bool::TYPE, + .codec = fieldCodecOf(), }}, { "Position", { .backingField = &cframe, .type = &Vector3::TYPE, diff --git a/core/src/objects/part.h b/core/src/objects/part.h index 9c54074..530eb0b 100644 --- a/core/src/objects/part.h +++ b/core/src/objects/part.h @@ -20,6 +20,7 @@ struct PartConstructParams { Data::Color3 color; bool anchored = false; + bool locked = false; }; class Part : public Instance { @@ -36,6 +37,7 @@ public: bool selected = false; bool anchored = false; + bool locked = false; rp::RigidBody* rigidBody = nullptr; SurfaceType topSurface = SurfaceType::SurfaceStuds; diff --git a/editor/mainglwidget.cpp b/editor/mainglwidget.cpp index defe3bf..db97631 100644 --- a/editor/mainglwidget.cpp +++ b/editor/mainglwidget.cpp @@ -282,7 +282,7 @@ void MainGLWidget::handleCursorChange(QMouseEvent* evt) { }; std::optional rayHit = gWorkspace()->CastRayNearest(camera.cameraPos, pointDir, 50000); - if (rayHit && partFromBody(rayHit->body)->name != "Baseplate") { + if (rayHit && !partFromBody(rayHit->body)->locked) { setCursor(Qt::OpenHandCursor); return; } @@ -341,7 +341,7 @@ void MainGLWidget::mousePressEvent(QMouseEvent* evt) { std::optional rayHit = gWorkspace()->CastRayNearest(camera.cameraPos, pointDir, 50000); if (!rayHit || !partFromBody(rayHit->body)) return; std::shared_ptr part = partFromBody(rayHit->body); - if (part->name == "Baseplate") return; + if (part->locked) return; // Handle surface tool if (mainWindow()->selectedTool >= TOOL_SMOOTH) { diff --git a/editor/mainwindow.cpp b/editor/mainwindow.cpp index 506641f..9603a63 100644 --- a/editor/mainwindow.cpp +++ b/editor/mainwindow.cpp @@ -72,6 +72,99 @@ MainWindow::MainWindow(QWidget *parent) delete menu; }); + connectActionHandlers(); + + // Update handles + addSelectionListener([&](auto oldSelection, auto newSelection, bool fromExplorer) { + editorToolHandles->adornee = std::nullopt; + if (newSelection.size() == 0) return; + InstanceRef inst = newSelection[0].lock(); + if (inst->GetClass() != &Part::TYPE) return; + + editorToolHandles->adornee = std::dynamic_pointer_cast(inst); + }); + + // Update properties + addSelectionListener([&](auto oldSelection, auto newSelection, bool fromExplorer) { + if (newSelection.size() == 0) return; + if (newSelection.size() > 1) + ui->propertiesView->setSelected(std::nullopt); + ui->propertiesView->setSelected(newSelection[0].lock()); + }); + + // ui->explorerView->Init(ui); + + // Baseplate + gWorkspace()->AddChild(ui->mainWidget->lastPart = Part::New({ + .position = glm::vec3(0, -5, 0), + .rotation = glm::vec3(0), + .size = glm::vec3(512, 1.2, 512), + .color = glm::vec3(0.388235, 0.372549, 0.384314), + .anchored = true, + .locked = true, + })); + ui->mainWidget->lastPart->name = "Baseplate"; + gWorkspace()->SyncPartPhysics(ui->mainWidget->lastPart); + + gWorkspace()->AddChild(ui->mainWidget->lastPart = Part::New({ + .position = glm::vec3(0), + .rotation = glm::vec3(0.5, 2, 1), + .size = glm::vec3(4, 1.2, 2), + .color = glm::vec3(0.639216f, 0.635294f, 0.647059f), + })); + gWorkspace()->SyncPartPhysics(ui->mainWidget->lastPart); +} + +void MainWindow::closeEvent(QCloseEvent* evt) { + #ifdef NDEBUG + // Ask if the user wants to save their changes + // https://stackoverflow.com/a/33890731 + QMessageBox msgBox; + msgBox.setText("Save changes before creating new document?"); + msgBox.setStandardButtons(QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel); + msgBox.setDefaultButton(QMessageBox::Save); + int result = msgBox.exec(); + + if (result == QMessageBox::Cancel) return evt->ignore(); + if (result == QMessageBox::Save) { + std::optional path; + if (!gDataModel->HasFile()) + path = openFileDialog("Openblocks Level (*.obl)", ".obl", QFileDialog::AcceptSave, QString::fromStdString("Save " + gDataModel->name)); + if (!path || path == "") return evt->ignore(); + + gDataModel->SaveToFile(path); + } + #endif +} + +void MainWindow::handleLog(Logger::LogLevel logLevel, std::string message) { + if (logLevel == Logger::LogLevel::DEBUG) return; + + if (logLevel == Logger::LogLevel::INFO) + ui->outputTextView->appendHtml(QString("

%1

").arg(QString::fromStdString(message))); + if (logLevel == Logger::LogLevel::WARNING) + ui->outputTextView->appendHtml(QString("

%1

").arg(QString::fromStdString(message))); + if (logLevel == Logger::LogLevel::ERROR || logLevel == Logger::LogLevel::FATAL_ERROR) + ui->outputTextView->appendHtml(QString("

%1

").arg(QString::fromStdString(message))); +} + +static std::chrono::time_point lastTime = std::chrono::steady_clock::now(); +void MainWindow::timerEvent(QTimerEvent* evt) { + if (evt->timerId() != timer.timerId()) { + QWidget::timerEvent(evt); + return; + } + + float deltaTime = std::chrono::duration_cast>(std::chrono::steady_clock::now() - lastTime).count(); + lastTime = std::chrono::steady_clock::now(); + + if (simulationPlaying) + gWorkspace()->PhysicsStep(deltaTime); + ui->mainWidget->update(); + ui->mainWidget->updateCycle(); +} + +void MainWindow::connectActionHandlers() { // Explorer View ui->explorerView->buildContextMenu(); @@ -166,6 +259,7 @@ MainWindow::MainWindow(QWidget *parent) .size = glm::vec3(512, 1.2, 512), .color = glm::vec3(0.388235, 0.372549, 0.384314), .anchored = true, + .locked = true, })); ui->mainWidget->lastPart->name = "Baseplate"; gWorkspace()->SyncPartPhysics(ui->mainWidget->lastPart); @@ -309,94 +403,6 @@ MainWindow::MainWindow(QWidget *parent) selectedParent->AddChild(inst.expect()); } }); - - // Update handles - addSelectionListener([&](auto oldSelection, auto newSelection, bool fromExplorer) { - editorToolHandles->adornee = std::nullopt; - if (newSelection.size() == 0) return; - InstanceRef inst = newSelection[0].lock(); - if (inst->GetClass() != &Part::TYPE) return; - - editorToolHandles->adornee = std::dynamic_pointer_cast(inst); - }); - - // Update properties - addSelectionListener([&](auto oldSelection, auto newSelection, bool fromExplorer) { - if (newSelection.size() == 0) return; - if (newSelection.size() > 1) - ui->propertiesView->setSelected(std::nullopt); - ui->propertiesView->setSelected(newSelection[0].lock()); - }); - - // ui->explorerView->Init(ui); - - // Baseplate - gWorkspace()->AddChild(ui->mainWidget->lastPart = Part::New({ - .position = glm::vec3(0, -5, 0), - .rotation = glm::vec3(0), - .size = glm::vec3(512, 1.2, 512), - .color = glm::vec3(0.388235, 0.372549, 0.384314), - .anchored = true, - })); - ui->mainWidget->lastPart->name = "Baseplate"; - gWorkspace()->SyncPartPhysics(ui->mainWidget->lastPart); - - gWorkspace()->AddChild(ui->mainWidget->lastPart = Part::New({ - .position = glm::vec3(0), - .rotation = glm::vec3(0.5, 2, 1), - .size = glm::vec3(4, 1.2, 2), - .color = glm::vec3(0.639216f, 0.635294f, 0.647059f), - })); - gWorkspace()->SyncPartPhysics(ui->mainWidget->lastPart); -} - -void MainWindow::closeEvent(QCloseEvent* evt) { - #ifdef NDEBUG - // Ask if the user wants to save their changes - // https://stackoverflow.com/a/33890731 - QMessageBox msgBox; - msgBox.setText("Save changes before creating new document?"); - msgBox.setStandardButtons(QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel); - msgBox.setDefaultButton(QMessageBox::Save); - int result = msgBox.exec(); - - if (result == QMessageBox::Cancel) return evt->ignore(); - if (result == QMessageBox::Save) { - std::optional path; - if (!gDataModel->HasFile()) - path = openFileDialog("Openblocks Level (*.obl)", ".obl", QFileDialog::AcceptSave, QString::fromStdString("Save " + gDataModel->name)); - if (!path || path == "") return evt->ignore(); - - gDataModel->SaveToFile(path); - } - #endif -} - -void MainWindow::handleLog(Logger::LogLevel logLevel, std::string message) { - if (logLevel == Logger::LogLevel::DEBUG) return; - - if (logLevel == Logger::LogLevel::INFO) - ui->outputTextView->appendHtml(QString("

%1

").arg(QString::fromStdString(message))); - if (logLevel == Logger::LogLevel::WARNING) - ui->outputTextView->appendHtml(QString("

%1

").arg(QString::fromStdString(message))); - if (logLevel == Logger::LogLevel::ERROR || logLevel == Logger::LogLevel::FATAL_ERROR) - ui->outputTextView->appendHtml(QString("

%1

").arg(QString::fromStdString(message))); -} - -static std::chrono::time_point lastTime = std::chrono::steady_clock::now(); -void MainWindow::timerEvent(QTimerEvent* evt) { - if (evt->timerId() != timer.timerId()) { - QWidget::timerEvent(evt); - return; - } - - float deltaTime = std::chrono::duration_cast>(std::chrono::steady_clock::now() - lastTime).count(); - lastTime = std::chrono::steady_clock::now(); - - if (simulationPlaying) - gWorkspace()->PhysicsStep(deltaTime); - ui->mainWidget->update(); - ui->mainWidget->updateCycle(); } void MainWindow::updateToolbars() { diff --git a/editor/mainwindow.h b/editor/mainwindow.h index 35c446e..14300a6 100644 --- a/editor/mainwindow.h +++ b/editor/mainwindow.h @@ -54,6 +54,8 @@ private: void timerEvent(QTimerEvent*) override; void closeEvent(QCloseEvent* evt) override; void handleLog(Logger::LogLevel, std::string); + + void connectActionHandlers(); std::optional openFileDialog(QString filter, QString defaultExtension, QFileDialog::AcceptMode acceptMode, QString title = ""); };