From 9df23631c84ed4edaa1eb0fa25cf51f7f760552e Mon Sep 17 00:00:00 2001 From: brige Date: Sat, 1 Nov 2025 18:08:02 +0800 Subject: [PATCH 1/5] add persets --- src/translations/Dyt_zh_CN.ts | 1042 +++++++++++------ src/ui/ModelBrowser/ModelInfo.h | 46 + .../ModelBrowser/PresetModelConfigParser.cpp | 2 + src/ui/ModelBrowser/PresetModelConfigParser.h | 19 +- src/ui/ModelBrowser/PresetModelListWidget.cpp | 144 +++ src/ui/ModelBrowser/PresetModelListWidget.h | 23 + src/ui/ModelBrowser/PresetModelPanel.cpp | 275 +++-- src/ui/ModelBrowser/PresetModelPanel.h | 65 +- src/ui/ModelBrowser/PresetModelPanel.ui | 98 +- src/viewer/OsgWidget.cpp | 21 +- workspace/presets/jammers.xml | 14 +- workspace/presets/missiles.xml | 17 +- workspace/presets/satellites.xml | 19 +- workspace/presets/ships.xml | 14 +- 14 files changed, 1173 insertions(+), 626 deletions(-) create mode 100644 src/ui/ModelBrowser/ModelInfo.h create mode 100644 src/ui/ModelBrowser/PresetModelListWidget.cpp create mode 100644 src/ui/ModelBrowser/PresetModelListWidget.h diff --git a/src/translations/Dyt_zh_CN.ts b/src/translations/Dyt_zh_CN.ts index eae835fa..114ec4c7 100644 --- a/src/translations/Dyt_zh_CN.ts +++ b/src/translations/Dyt_zh_CN.ts @@ -14,198 +14,198 @@ - + File Path: - + Select curve data file... - + ... - + File Name: - - + + - - + File Size: - + Chart Properties - + Chart Name: - + Chart 1 - + Enter chart name... - + Chart Type: - + X Axis Title: - + Enter X axis title... - + Y Axis Title: - + Enter Y axis title... - + Time: - + Axis Range Settings - + X Tick Count: - + X Min: - + Y Max: - + Y Min: - + X Max: - + Y Tick Count: - + Curve Management - + Curves: - + Add Curve - + Remove - + Selected Curve Properties - + Curve Name: - + Enter curve name... - + Curve Color: - + Select Color - + background-color: rgb(255, 0, 0); border: 1px solid black; - + Data Start: - + Data Stop: - + X Value: - + Y Value: - + Add File - + Cancel @@ -406,166 +406,186 @@ - + File Path: - + Select light data file... - + ... - + File Name: - - - + + + - - + File Size: - + + Chart Properties + + + + + Chart Names: + + + + Color Properties - + Open Color: - - + + Select Color - + background-color: rgb(0, 255, 0); border: 1px solid black; - + Close Color: - + background-color: rgb(255, 0, 0); border: 1px solid black; - + Light Management - + Lights: - + Add Light Row - + Remove - + Selected Light Properties - + Light Names: - + Enter light names (comma separated)... - - Light Data: + + Light Datas: - + Enter data values (comma separated integers)... - + Edit Data - + Row Index: - + Add File - + Cancel - - + + Warning + + + + + Please enter a Chart name. + + + + + Error - + Unable to get current workspace - + Curve file count has reached the limit (9 files) - + File already exists - + File copy failed - + Invalid file - + Failed to add file @@ -656,6 +676,351 @@ + + AddPolarDlg + + + Add Polar + + + + + File Selection + + + + + File Path: + + + + + Select curve data file... + + + + + ... + + + + + File Name: + + + + + + - + + + + + File Size: + + + + + Angular Axis + + + + + + Title: + + + + + + Enter axis title... + + + + + + Min: + + + + + + Max: + + + + + + Count: + + + + + + Unit: + + + + + + Enter axis Unit... + + + + + Radial Axis + + + + + Curve Management + + + + + Curves: + + + + + Add + + + + + Remove + + + + + Selected Curve Properties + + + + + Name: + + + + + Enter curve name... + + + + + Color: + + + + + Select Color + + + + + background-color: rgb(255, 0, 0); border: 1px solid black; + + + + + Angular: + + + + + Radial: + + + + + X Value: + + + + + Y Value: + + + + + Add File + + + + + Cancel + + + + + Chart Properties + + + + + Time: + + + + + Chart Name: + + + + + Chart 1 + + + + + Enter chart name... + + + + + AddPolarFileDlg + + + + + + + + + + + + + + + + + Validation Error + + + + + Please select a data file. + + + + + Selected file does not exist. + + + + + Selected file is not readable. Please check file permissions. + + + + + File is too large (over 100MB). Please select a smaller file. + + + + + At least one curve must be defined. + + + + + Curve %1 name cannot be empty. + + + + + Curve name '%1' is duplicated. Please use different names. + + + + + Curve name '%1' is too long. Please limit to 50 characters. + + + + + Curve '%1' X value is out of range. Please ensure it is between -1000000 and 1000000. + + + + + Curve '%1' Y value is out of range. Please ensure it is between -1000000 and 1000000. + + + + + Chart name cannot be empty. + + + + + Chart name is too long. Please limit to 100 characters. + + + + + Angular axis minimum value must be less than maximum value. + + + + + Radial axis minimum value must be less than maximum value. + + + + + Time parameter cannot be negative. + + + + + Curve %1 + + + + + + + Error + + + + + Failed to create file entry + + + + + Unable to get current workspace + + + + + Curve file count has reached the limit (9 files) + + + + + File already exists + + + + + File copy failed + + + + + Invalid file + + + + + Failed to add file + + + AddSurfaceFileDlg @@ -669,237 +1034,280 @@ - + Select surface data file... - + Browse... - + Chart Properties - + X Axis Title: - + Y Axis Title: - + Z Axis Title: - + Time Parameter: - + X Range: - - - + + + to - + X Count: - + Y Range: - + Y Count: - + Z Range: - + Z Count: - + Surface Management - + Add Surface - + Remove - + Surface Properties - + + Name: - + Color: - + Select Color - + Start Point: - + End Point: - + + X-Map: + + + + + + + x + + + + + + + y + + + + + + + z + + + + + Y-Map: + + + + + Z-Map: + + + + Data Format Parameters - + X Column: - + Y Column: - + Z Column: - + Separator: - + , - + X Grid Size: - + Y Grid Size: - + Has Header Row - + Surface Data Files (*.txt *.dat *.csv);;All Files (*.*) - - Select Surface Data File + + Add Surface Dialog - - + + + Warning - + Please add at least one surface. - + Please fill in all axis titles. - + Select Surface Color - - - + + Please enter a Surface name. + + + + + + Error - + Failed to create surface file entry. - + Unable to get current workspace - + Surface file count has reached the limit (9 files) - + File already exists - + File copy failed - + Invalid file - + Failed to add file @@ -917,276 +1325,270 @@ - + File Path: - + Select table data file... - + ... - + File Name: - - + + - - + File Size: - + Basic Information - + Enter table name... - + Table Name: - + Time Parameter: - + Table Headers Configuration - + File has header row - + Headers (comma-separated): - + e.g., Time, Value1, Value2, Value3... - + Tip: Headers will be auto-detected if file has header row - + Parsing Parameters - + Skip Rows: - + Auto-detect parameters - + Encoding: - + UTF-8 - + GBK - + ASCII - + ISO-8859-1 - + Curves Configuration - + Add Curve - + Remove Curve - + Curve Properties - + Name: - + Enter curve name... - + Color: - + Select Color - + Data: - + Enter data values (comma-separated)... - + Data values must match the number of headers - + Add File - + Cancel - - - - + + - + + Warning - + Please configure table headers first. - + Select Curve Color - + Please configure table headers. - - Please add at least one curve. - - - - + Curve %1 name cannot be empty. - + Curve '%1' data cannot be empty. - + Curve '%1' data count (%2) doesn't match headers count (%3). - + Please enter a table name. - - - + + + Error - + Failed to create table file entry. - + Unable to get current workspace - + Table file count has reached the limit - + File already exists - + File copy failed - + Invalid file - + Failed to add file @@ -1505,6 +1907,11 @@ new light file + + + new polar file + + FitCurveChartView @@ -1782,27 +2189,22 @@ - - PresetModelToolBox - - - - + Ships - + Satellites - + Missiles - + Jammers @@ -1892,127 +2294,70 @@ - + Open Workspace - - + + Dyt Files (*.dyt) - - - - - - - - - - - - - - - - - - - - - + + + + + + prompt - - - - - + + + + + + please create workspace first - + Save Workspace - - - - - Error - - - - - - - - Failed to create file entry - - - - - - - - up to 9 files allowed for this type - - - - - - - - file already added for this type - - - - - - - - copy file failed - - - - - - - - invalid file - - - - - + + Curve[%1] - - + + Surface[%1] - - + + Table[%1] - - + + Light[%1] + + + Polar[%1] + + QtBoolEdit @@ -2085,48 +2430,48 @@ QtConeWaveComponentManager - - + + ConeWaveComponent - + Height - + Radius - + waveCount - + waveSpeed - + baseColor - + waveColor - + ringBrightAlpha - + ringDarkAlpha @@ -2232,28 +2577,28 @@ QtDashedLineComponentManager - - + + DashedLineComponent - + Start - + End - + Radius - + Color @@ -2269,17 +2614,17 @@ QtEntityPropertyManager - + Name - + Visible - + Transform @@ -2361,13 +2706,13 @@ QtMeshComponetManager - - + + MeshComponent - + Mesh @@ -2416,13 +2761,13 @@ QtPathComponentManager - - + + PathComponent - + Path @@ -2672,95 +3017,100 @@ QtWorkspacePropertyManager - + Name - + Description - + Timestep - + SimMatlab - + MatlabParam - + WavePath - + ReportPath - + RDPath - + CommondPath - + Count - + Curve[%1] - + Surface[%1] - + Table[%1] - + Light[%1] - + Curves - + Surfaces - + Tables - + Lights + + + Polars + + SignalIndicatorLampUI @@ -2773,22 +3123,22 @@ SimuRunMenu - + no workspace - + no commands - + Commands - + unnamed @@ -2874,53 +3224,53 @@ - + Name - + input workspace name - + Path - + select workspace save path - - + + ... - + commond Path - + select commond file path - + describe - + Sure - + Cancel diff --git a/src/ui/ModelBrowser/ModelInfo.h b/src/ui/ModelBrowser/ModelInfo.h new file mode 100644 index 00000000..e6582e44 --- /dev/null +++ b/src/ui/ModelBrowser/ModelInfo.h @@ -0,0 +1,46 @@ +#pragma once + +#include +#include + +/** + * @brief Model information structure + * + * Contains all information for a single preset model, including name, display name, + * description, mesh file path, icon path, and enabled status. + */ +struct ModelInfo { + QString name; ///< Model name (unique identifier) + QString displayName; ///< Display name (for UI display) + QString description; ///< Model description + QString meshFile; ///< 3D mesh file path + QString icon; ///< Icon file path + bool enabled; ///< Whether this model is enabled + + /** + * @brief Default constructor + * Model is enabled by default + */ + ModelInfo() : enabled(true) {} + + /** + * @brief Constructor with parameters + */ + ModelInfo(const QString& name, const QString& displayName, const QString& description, + const QString& meshFile, const QString& icon, bool enabled = true) + : name(name), displayName(displayName), description(description), + meshFile(meshFile), icon(icon), enabled(enabled) {} +}; + +/** + * @brief Model category structure + * + * Contains information for a model category, such as ships, satellites, missiles, jammers, etc. + * Each category contains multiple specific models. + */ +struct ModelCategory { + QString name; ///< Category name (unique identifier) + QString displayName; ///< Display name (for UI display) + QString icon; ///< Category icon path + QVector models; ///< All models in this category +}; \ No newline at end of file diff --git a/src/ui/ModelBrowser/PresetModelConfigParser.cpp b/src/ui/ModelBrowser/PresetModelConfigParser.cpp index 2b02c3c0..ffecfe72 100644 --- a/src/ui/ModelBrowser/PresetModelConfigParser.cpp +++ b/src/ui/ModelBrowser/PresetModelConfigParser.cpp @@ -102,6 +102,7 @@ bool PresetModelConfigParser::ParseXmlFile(const QString& filePath) const char* modelDisplayName = modelElement->Attribute("displayName"); const char* description = modelElement->Attribute("description"); const char* meshFile = modelElement->Attribute("meshFile"); + const char* modelIcon = modelElement->Attribute("icon"); const char* enabled = modelElement->Attribute("enabled"); if (!modelName) { @@ -114,6 +115,7 @@ bool PresetModelConfigParser::ParseXmlFile(const QString& filePath) model.displayName = modelDisplayName ? QString::fromUtf8(modelDisplayName) : model.name; model.description = description ? QString::fromUtf8(description) : ""; model.meshFile = meshFile ? QString::fromUtf8(meshFile) : ""; + model.icon = modelIcon ? QString::fromUtf8(modelIcon) : ""; model.enabled = enabled ? (strcmp(enabled, "true") == 0) : true; category.models.append(model); diff --git a/src/ui/ModelBrowser/PresetModelConfigParser.h b/src/ui/ModelBrowser/PresetModelConfigParser.h index ffa556c2..a61f1d27 100644 --- a/src/ui/ModelBrowser/PresetModelConfigParser.h +++ b/src/ui/ModelBrowser/PresetModelConfigParser.h @@ -3,24 +3,7 @@ #include #include #include -#include - -struct ModelInfo { - QString name; - QString displayName; - QString description; - QString meshFile; - bool enabled; - - ModelInfo() : enabled(true) {} -}; - -struct ModelCategory { - QString name; - QString displayName; - QString icon; - QVector models; -}; +#include "ModelInfo.h" class PresetModelConfigParser { diff --git a/src/ui/ModelBrowser/PresetModelListWidget.cpp b/src/ui/ModelBrowser/PresetModelListWidget.cpp new file mode 100644 index 00000000..3942decf --- /dev/null +++ b/src/ui/ModelBrowser/PresetModelListWidget.cpp @@ -0,0 +1,144 @@ +#include "ui/ModelBrowser/PresetModelListWidget.h" + +#include +#include +#include +#include + +#include "common/RecourceHelper.h" +#include "common/SpdLogger.h" + +PresetModelListWidget::PresetModelListWidget(QWidget* parent) + : QListWidget(parent) +{ + setDragEnabled(true); + setDragDropMode(QAbstractItemView::DragOnly); + + // Configure list widget for icon-text vertical layout + setViewMode(QListView::IconMode); + setResizeMode(QListView::Adjust); + setMovement(QListView::Static); + setGridSize(QSize(100, 80)); // Width x Height for each item + setIconSize(QSize(48, 48)); // Icon size + setSpacing(5); // Spacing between items +} + +void PresetModelListWidget::startDrag(Qt::DropActions supportedActions) { + QListWidgetItem* item = currentItem(); + if (nullptr == item) { + return; + } + + QString modelType = property("modelType").toString(); + QString modelName = item->data(Qt::UserRole).toString(); + if (modelName.isEmpty()) { + modelName = item->text(); // Fallback to item text + } + + QMimeData* mimeData = new QMimeData; + + QString dragData = QString("%1|%2").arg(modelType, modelName); + mimeData->setData("application/x-preset-model", dragData.toUtf8()); + + // Create drag object + QDrag* drag = new QDrag(this); + drag->setMimeData(mimeData); + + // Create drag pixmap with icon on top and text below + const int iconSize = 32; + const int textHeight = 20; + const int totalWidth = 80; + const int totalHeight = iconSize + textHeight + 5; // 5px spacing between icon and text + + QPixmap pixmap(totalWidth, totalHeight); + pixmap.fill(Qt::transparent); + + QPainter painter(&pixmap); + painter.setRenderHint(QPainter::Antialiasing); + + // Draw semi-transparent background + painter.setBrush(QColor(255, 255, 255, 80)); // White with 70% opacity + painter.setPen(QPen(QColor(200, 200, 200, 200), 1)); // Light gray border + painter.drawRoundedRect(2, 2, totalWidth - 4, totalHeight - 4, 4, 4); + + // Draw icon at the top center + QPixmap iconPixmap = item->icon().pixmap(iconSize, iconSize); + if (!iconPixmap.isNull()) { + int iconX = (totalWidth - iconSize) / 2; + painter.drawPixmap(iconX, 0, iconPixmap); + } else { + // Draw a default placeholder icon + int iconX = (totalWidth - iconSize) / 2; + painter.setPen(QPen(Qt::gray, 2)); + painter.setBrush(Qt::lightGray); + painter.drawRect(iconX, 0, iconSize, iconSize); + painter.setPen(Qt::black); + painter.drawText(iconX, 0, iconSize, iconSize, Qt::AlignCenter, "?"); + } + + // Draw text below the icon + painter.setPen(Qt::black); + QFont font = painter.font(); + font.setPointSize(8); + painter.setFont(font); + + QRect textRect(0, iconSize + 5, totalWidth, textHeight); + painter.drawText(textRect, Qt::AlignCenter | Qt::TextWordWrap, item->text()); + drag->setPixmap(pixmap); + + // Set hot spot to the center of the icon (not the entire pixmap) + int hotSpotX = totalWidth / 2; + int hotSpotY = iconSize / 2; // Center of the icon part only + drag->setHotSpot(QPoint(hotSpotX, hotSpotY)); + + // Execute drag + drag->exec(Qt::CopyAction); + +} + +void PresetModelListWidget::setModelType(const QString& modelType) { + modelType_ = modelType; + setProperty("modelType", modelType_); +} + +QString PresetModelListWidget::getModelType() const { + return modelType_; +} + +void PresetModelListWidget::setModelList(const QVector& models) { + modelList_ = models; + + clear(); + + for (const ModelInfo& modelInfo : models) { + if (!modelInfo.enabled) continue; // Skip disabled models + + QListWidgetItem* item = new QListWidgetItem(modelInfo.displayName.isEmpty() ? modelInfo.name : modelInfo.displayName); + item->setFlags(item->flags() | Qt::ItemIsDragEnabled); + + // Use icon from ModelInfo if available + if (!modelInfo.icon.isEmpty()) { + QString presetsPath = QString("%1/%2").arg(RecourceHelper::Get().GetResourcesPath(), modelInfo.icon); + QIcon icon(presetsPath); + item->setIcon(icon); + } else { + // Create a default icon if no icon is specified + QPixmap pixmap(48, 48); + pixmap.fill(Qt::lightGray); + QPainter painter(&pixmap); + painter.setPen(Qt::black); + painter.drawRect(0, 0, 47, 47); + painter.drawText(pixmap.rect(), Qt::AlignCenter, "?"); + item->setIcon(QIcon(pixmap)); + } + + // Store model name as simple string + item->setData(Qt::UserRole, modelInfo.name); + + addItem(item); + } +} + +QVector PresetModelListWidget::getModelList() const { + return modelList_; +} diff --git a/src/ui/ModelBrowser/PresetModelListWidget.h b/src/ui/ModelBrowser/PresetModelListWidget.h new file mode 100644 index 00000000..408fec71 --- /dev/null +++ b/src/ui/ModelBrowser/PresetModelListWidget.h @@ -0,0 +1,23 @@ +#include +#include +#include "ui/ModelBrowser/ModelInfo.h" + +class PresetModelListWidget : public QListWidget +{ + Q_OBJECT +public: + explicit PresetModelListWidget(QWidget* parent = nullptr); + + void setModelType(const QString& modelType); + QString getModelType() const; + + void setModelList(const QVector& models); + QVector getModelList() const; + +protected: + void startDrag(Qt::DropActions supportedActions); + +private: + QString modelType_; + QVector modelList_; +}; diff --git a/src/ui/ModelBrowser/PresetModelPanel.cpp b/src/ui/ModelBrowser/PresetModelPanel.cpp index 01d8d18a..dcafb751 100644 --- a/src/ui/ModelBrowser/PresetModelPanel.cpp +++ b/src/ui/ModelBrowser/PresetModelPanel.cpp @@ -8,11 +8,14 @@ #include #include #include +#include #include #include #include #include +#include +#include "common/RecourceHelper.h" #include "common/SpdLogger.h" #include "ui/DockTitleBar.h" #include "ui/DockWidget.h" @@ -23,6 +26,7 @@ PresetModelPanel::PresetModelPanel(QWidget *parent) : QWidget(parent) , ui(new Ui::PresetModelPanel) + , dragSourceWidget_(nullptr) { ui->setupUi(this); InitUI(); @@ -50,13 +54,14 @@ void PresetModelPanel::AttachDock(DockWidget* dockWidget) void PresetModelPanel::InitUI() { // Try to load model data from configuration files - if (m_configParser.LoadAllConfigs("workspace/presets")) { + QString presetsPath = QString("%1/presets").arg(RecourceHelper::Get().GetResourcesPath()); + if (configParser_.LoadAllConfigs(presetsPath)) { // Successfully loaded from configuration files LoadModelsFromConfig(); } else { // Failed to load configuration files, use hardcoded defaults as fallback LOG_WARN("Failed to load preset models from config files, using hardcoded defaults. Error: {}", - m_configParser.GetLastError().toStdString()); + configParser_.GetLastError().toStdString()); LoadDefaultModels(); } } @@ -64,161 +69,175 @@ void PresetModelPanel::InitUI() void PresetModelPanel::LoadModelsFromConfig() { // Load ship models - QStringList shipModels = m_configParser.GetModelNames("Ship"); - if (!shipModels.isEmpty()) { - PopulateModelList(ui->shipList, "Ship", shipModels); + ModelCategory shipCategory = configParser_.GetCategory("Ship"); + if (!shipCategory.models.isEmpty()) { + ui->shipList->setModelType("Ship"); + ui->shipList->setModelList(shipCategory.models); } // Load satellite models - QStringList satelliteModels = m_configParser.GetModelNames("Satellite"); - if (!satelliteModels.isEmpty()) { - PopulateModelList(ui->satelliteList, "Satellite", satelliteModels); + ModelCategory satelliteCategory = configParser_.GetCategory("Satellite"); + if (!satelliteCategory.models.isEmpty()) { + ui->satelliteList->setModelType("Satellite"); + ui->satelliteList->setModelList(satelliteCategory.models); } // Load missile models - QStringList missileModels = m_configParser.GetModelNames("Missile"); - if (!missileModels.isEmpty()) { - PopulateModelList(ui->missileList, "Missile", missileModels); + ModelCategory missileCategory = configParser_.GetCategory("Missile"); + if (!missileCategory.models.isEmpty()) { + ui->missileList->setModelType("Missile"); + ui->missileList->setModelList(missileCategory.models); } // Load jammer models - QStringList jammerModels = m_configParser.GetModelNames("Jammer"); - if (!jammerModels.isEmpty()) { - PopulateModelList(ui->jammerList, "Jammer", jammerModels); + ModelCategory jammerCategory = configParser_.GetCategory("Jammer"); + if (!jammerCategory.models.isEmpty()) { + ui->jammerList->setModelType("Jammer"); + ui->jammerList->setModelList(jammerCategory.models); } } void PresetModelPanel::LoadDefaultModels() { // Use hardcoded default models as fallback mechanism - QStringList shipModels = { - "Destroyer", - "Aircraft Carrier", - "Frigate", - "Submarine" + QVector shipModels = { + {"destroyer", "Destroyer", "Naval destroyer vessel", "", ":/icons/ship.png", true}, + {"aircraft_carrier", "Aircraft Carrier", "Large naval vessel", "", ":/icons/ship.png", true}, + {"frigate", "Frigate", "Fast naval vessel", "", ":/icons/ship.png", true}, + {"submarine", "Submarine", "Underwater vessel", "", ":/icons/ship.png", true} }; - PopulateModelList(ui->shipList, "Ship", shipModels); + ui->shipList->setModelType("Ship"); + ui->shipList->setModelList(shipModels); - QStringList satelliteModels = { - "Communication Satellite", - "Weather Satellite", - "GPS Satellite", - "Spy Satellite" + QVector satelliteModels = { + {"comm_satellite", "Communication Satellite", "Communication satellite", "", ":/icons/satellite.png", true}, + {"weather_satellite", "Weather Satellite", "Weather monitoring satellite", "", ":/icons/satellite.png", true}, + {"gps_satellite", "GPS Satellite", "GPS navigation satellite", "", ":/icons/satellite.png", true}, + {"spy_satellite", "Spy Satellite", "Reconnaissance satellite", "", ":/icons/satellite.png", true} }; - PopulateModelList(ui->satelliteList, "Satellite", satelliteModels); + ui->satelliteList->setModelType("Satellite"); + ui->satelliteList->setModelList(satelliteModels); - QStringList missileModels = { - "Cruise Missile", - "Ballistic Missile", - "Anti-Ship Missile", - "Surface-to-Air Missile" + QVector missileModels = { + {"cruise_missile", "Cruise Missile", "Long-range cruise missile", "", ":/icons/missile.png", true}, + {"ballistic_missile", "Ballistic Missile", "Ballistic missile", "", ":/icons/missile.png", true}, + {"anti_ship_missile", "Anti-Ship Missile", "Anti-ship missile", "", ":/icons/missile.png", true}, + {"sam_missile", "Surface-to-Air Missile", "Surface-to-air missile", "", ":/icons/missile.png", true} }; - PopulateModelList(ui->missileList, "Missile", missileModels); + ui->missileList->setModelType("Missile"); + ui->missileList->setModelList(missileModels); - QStringList jammerModels = { - "Electronic Jammer", - "Communication Jammer", - "Radar Jammer", - "GPS Jammer" + QVector jammerModels = { + {"electronic_jammer", "Electronic Jammer", "Electronic warfare jammer", "", ":/icons/jammer.png", true}, + {"comm_jammer", "Communication Jammer", "Communication jammer", "", ":/icons/jammer.png", true}, + {"radar_jammer", "Radar Jammer", "Radar jamming device", "", ":/icons/jammer.png", true}, + {"gps_jammer", "GPS Jammer", "GPS jamming device", "", ":/icons/jammer.png", true} }; - PopulateModelList(ui->jammerList, "Jammer", jammerModels); + ui->jammerList->setModelType("Jammer"); + ui->jammerList->setModelList(jammerModels); } -void PresetModelPanel::PopulateModelList(QListWidget* listWidget, const QString& modelType, const QStringList& models) -{ - if (!listWidget) return; +// void PresetModelPanel::OnModelItemDoubleClicked(QListWidgetItem* item) +// { +// if (!item) return; - // Set the model type as a property for later use - listWidget->setProperty("modelType", modelType); +// // Get the list widget that contains this item +// QListWidget* listWidget = item->listWidget(); +// if (!listWidget) return; - for (const QString& model : models) { - QListWidgetItem* item = new QListWidgetItem(model); - item->setFlags(item->flags() | Qt::ItemIsDragEnabled); +// QString modelType = listWidget->property("modelType").toString(); + +// // Get model name from stored data +// QString modelName = item->data(Qt::UserRole).toString(); +// if (modelName.isEmpty()) { +// modelName = item->text(); // Fallback to display text +// } + +// LOG_INFO("Model double-clicked: {} of type {}", qPrintable(modelName), qPrintable(modelType)); + +// // Emit signal for model selection +// emit ModelDropped(modelType, modelName); +// } + +// bool PresetModelPanel::eventFilter(QObject* obj, QEvent* event) +// { +// QListWidget* listWidget = qobject_cast(obj); +// if (!listWidget) { +// return QWidget::eventFilter(obj, event); +// } + +// if (event->type() == QEvent::MouseButtonPress) { +// QMouseEvent* mouseEvent = static_cast(event); +// if (mouseEvent->button() == Qt::LeftButton) { +// dragStartPosition_ = mouseEvent->pos(); +// dragSourceWidget_ = listWidget; +// } +// } else if (event->type() == QEvent::MouseMove) { +// QMouseEvent* mouseEvent = static_cast(event); +// if (!(mouseEvent->buttons() & Qt::LeftButton)) { +// return QWidget::eventFilter(obj, event); +// } - if (modelType == "Ship") { - item->setIcon(QIcon(":/icons/ship.png")); - } else if (modelType == "Satellite") { - item->setIcon(QIcon(":/icons/satellite.png")); - } else if (modelType == "Missile") { - item->setIcon(QIcon(":/icons/missile.png")); - } else if (modelType == "Jammer") { - item->setIcon(QIcon(":/icons/jammer.png")); - } +// if ((mouseEvent->pos() - dragStartPosition_).manhattanLength() < QApplication::startDragDistance()) { +// return QWidget::eventFilter(obj, event); +// } - listWidget->addItem(item); - } +// QListWidgetItem* item = listWidget->itemAt(dragStartPosition_); +// if (!item) { +// return QWidget::eventFilter(obj, event); +// } + +// QString modelType = listWidget->property("modelType").toString(); + +// // Get model name from stored data +// QString modelName = item->data(Qt::UserRole).toString(); +// if (modelName.isEmpty()) { +// modelName = item->text(); // Fallback to item text +// } + +// // Create mime data +// QMimeData* mimeData = new QMimeData; +// QString dragData = QString("%1|%2").arg(modelType, modelName); +// mimeData->setData("application/x-preset-model", dragData.toUtf8()); + +// // Create drag object +// QDrag* drag = new QDrag(this); +// drag->setMimeData(mimeData); + +// // Set drag pixmap (icon or text) +// QPixmap pixmap(item->icon().pixmap(32, 32)); +// if (pixmap.isNull()) { +// pixmap = QPixmap(100, 30); +// pixmap.fill(Qt::transparent); +// QPainter painter(&pixmap); +// painter.setPen(Qt::black); +// painter.drawText(pixmap.rect(), Qt::AlignCenter, item->text()); +// } +// drag->setPixmap(pixmap); + +// // Execute drag +// Qt::DropAction dropAction = drag->exec(Qt::CopyAction); +// Q_UNUSED(dropAction); + +// return true; +// } - connect(listWidget, &QListWidget::itemDoubleClicked, this, &PresetModelPanel::OnModelItemDoubleClicked); -} +// return QWidget::eventFilter(obj, event); +// } -void PresetModelPanel::SetupDragAndDrop(QListWidget* listWidget) -{ - listWidget->setDragDropMode(QAbstractItemView::DragOnly); - listWidget->setDefaultDropAction(Qt::CopyAction); -} +// // DraggableListWidget implementation +// DraggableListWidget::DraggableListWidget(const QString& modelType, QWidget* parent) +// : QListWidget(parent), modelType_(modelType) +// { +// setProperty("modelType", modelType); +// } -void PresetModelPanel::OnModelItemDoubleClicked(QListWidgetItem* item) -{ - if (!item) return; - - QListWidget* listWidget = item->listWidget(); - if (!listWidget) return; - - QString modelType = listWidget->property("modelType").toString(); - QString modelName = item->text(); - - emit ModelDropped(modelType, modelName); -} +// void DraggableListWidget::mousePressEvent(QMouseEvent* event) +// { +// QListWidget::mousePressEvent(event); +// } -// DraggableListWidget implementation -DraggableListWidget::DraggableListWidget(const QString& modelType, QWidget* parent) - : QListWidget(parent), modelType_(modelType) -{ - setProperty("modelType", modelType); -} - -void DraggableListWidget::mousePressEvent(QMouseEvent* event) -{ - if (event->button() == Qt::LeftButton) { - dragStartPosition_ = event->pos(); - } - QListWidget::mousePressEvent(event); -} - -void DraggableListWidget::mouseMoveEvent(QMouseEvent* event) { - if (!(event->buttons() & Qt::LeftButton)) { - return; - } - - if ((event->pos() - dragStartPosition_).manhattanLength() < QApplication::startDragDistance()) { - return; - } - - QListWidgetItem* item = itemAt(dragStartPosition_); - if (!item) { - return; - } - - QDrag* drag = new QDrag(this); - QMimeData* mimeData = new QMimeData; - - QString modelType = this->property("modelType").toString(); - QString modelName = item->text(); - QString dragData = QString("%1|%2").arg(modelType, modelName); - - mimeData->setData("application/x-preset-model", dragData.toUtf8()); - drag->setMimeData(mimeData); - - QPixmap pixmap(item->icon().pixmap(32, 32)); - if (pixmap.isNull()) { - pixmap = QPixmap(100, 30); - pixmap.fill(Qt::transparent); - QPainter painter(&pixmap); - painter.setPen(Qt::black); - painter.drawText(pixmap.rect(), Qt::AlignCenter, modelName); - } - drag->setPixmap(pixmap); - - Qt::DropAction dropAction = drag->exec(Qt::CopyAction); - Q_UNUSED(dropAction); -} \ No newline at end of file +// void DraggableListWidget::mouseMoveEvent(QMouseEvent* event) +// { +// QListWidget::mouseMoveEvent(event); +// } \ No newline at end of file diff --git a/src/ui/ModelBrowser/PresetModelPanel.h b/src/ui/ModelBrowser/PresetModelPanel.h index d73543c3..1fe4aa62 100644 --- a/src/ui/ModelBrowser/PresetModelPanel.h +++ b/src/ui/ModelBrowser/PresetModelPanel.h @@ -10,14 +10,18 @@ #include #include #include +#include +#include +#include +#include +#include "ModelInfo.h" #include "PresetModelConfigParser.h" -QT_BEGIN_NAMESPACE namespace Ui { class PresetModelPanel; } -QT_END_NAMESPACE class DockWidget; + /** * @brief Preset model panel class * @@ -40,42 +44,12 @@ public: */ void AttachDock(DockWidget* dockWidget); -Q_SIGNALS: - /** - * @brief Signal emitted when model is dragged to scene - * @param modelType Model type - * @param modelName Model name - * @param position Scene position (optional) - */ - void ModelDropped(const QString& modelType, const QString& modelName, const QPointF& position = QPointF()); - -private slots: - /** - * @brief Handle model item double click event - * @param item Clicked item - */ - void OnModelItemDoubleClicked(QListWidgetItem* item); - private: /** * @brief Initialize user interface */ void InitUI(); - /** - * @brief Populate model list with items - * @param listWidget List widget to populate - * @param modelType Model type - * @param models Model name list - */ - void PopulateModelList(QListWidget* listWidget, const QString& modelType, const QStringList& models); - - /** - * @brief Setup drag and drop functionality - * @param listWidget List widget to setup drag and drop - */ - void SetupDragAndDrop(QListWidget* listWidget); - /** * @brief Load models from configuration file */ @@ -88,26 +62,9 @@ private: private: Ui::PresetModelPanel *ui; - PresetModelConfigParser m_configParser; -}; - -/** - * @brief Custom list widget with drag and drop support - */ -class DraggableListWidget : public QListWidget { - Q_OBJECT - -public: - explicit DraggableListWidget(const QString& modelType, QWidget* parent = nullptr); - -Q_SIGNALS: - void ItemDragged(const QString& modelType, const QString& modelName); - -protected: - void mousePressEvent(QMouseEvent* event) override; - void mouseMoveEvent(QMouseEvent* event) override; - -private: - QString modelType_; + PresetModelConfigParser configParser_; + + // Drag and drop support QPoint dragStartPosition_; -}; \ No newline at end of file + QListWidget* dragSourceWidget_; +}; diff --git a/src/ui/ModelBrowser/PresetModelPanel.ui b/src/ui/ModelBrowser/PresetModelPanel.ui index 8df142c3..f0b9eab7 100644 --- a/src/ui/ModelBrowser/PresetModelPanel.ui +++ b/src/ui/ModelBrowser/PresetModelPanel.ui @@ -14,6 +14,9 @@ Preset Models + + 5 + 5 @@ -26,16 +29,10 @@ 5 - - 5 - - - - PresetModelToolBox - + - 0 + 1 @@ -43,15 +40,30 @@ 0 0 290 - 540 + 470 Ships + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + - + QAbstractItemView::DragOnly @@ -68,15 +80,30 @@ 0 0 290 - 540 + 470 Satellites + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + - + QAbstractItemView::DragOnly @@ -93,15 +120,30 @@ 0 0 290 - 540 + 470 Missiles + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + - + QAbstractItemView::DragOnly @@ -118,15 +160,30 @@ 0 0 290 - 540 + 470 Jammers + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + - + QAbstractItemView::DragOnly @@ -141,6 +198,13 @@ + + + PresetModelListWidget + QListWidget +
ui/ModelBrowser/PresetModelListWidget.h
+
+
- \ No newline at end of file + diff --git a/src/viewer/OsgWidget.cpp b/src/viewer/OsgWidget.cpp index 9d90214a..96628894 100644 --- a/src/viewer/OsgWidget.cpp +++ b/src/viewer/OsgWidget.cpp @@ -191,10 +191,14 @@ void OsgWidget::paintEvent(QPaintEvent*) { } void OsgWidget::dragEnterEvent(QDragEnterEvent* event) { - // 检查是否是预制模型拖拽 + LOG_INFO("OsgWidget::dragEnterEvent - Available formats:{}", event->mimeData()->formats().join(",").toStdString()); + + // Check if it's a preset model drag if (event->mimeData()->hasFormat("application/x-preset-model")) { + LOG_INFO("OsgWidget::dragEnterEvent - Accept preset model drag"); event->acceptProposedAction(); } else { + LOG_INFO("OsgWidget::dragEnterEvent - Ignore drag, not preset model format"); event->ignore(); } } @@ -209,28 +213,33 @@ void OsgWidget::dragMoveEvent(QDragMoveEvent* event) { } void OsgWidget::dropEvent(QDropEvent* event) { - // 检查是否是预制模型拖拽 + LOG_INFO("OsgWidget::dropEvent - Start processing drop event"); + if (event->mimeData()->hasFormat("application/x-preset-model")) { QByteArray data = event->mimeData()->data("application/x-preset-model"); QString modelInfo = QString::fromUtf8(data); - // 解析模型信息 (格式: "modelType|modelName") + LOG_INFO("OsgWidget::dropEvent - Received model info:{}", modelInfo.toStdString()); + QStringList parts = modelInfo.split("|"); if (parts.size() == 2) { QString modelType = parts[0]; QString modelName = parts[1]; - // 获取拖拽位置 QPointF position = event->posF(); - // 处理预制模型拖拽 + LOG_INFO("OsgWidget::dropEvent - Parse success, type:{} name:{} position:({},{})", modelType.toStdString(), modelName.toStdString(), position.x(), position.y()); + OnPresetModelDropped(modelType, modelName, position); event->acceptProposedAction(); + LOG_INFO("OsgWidget::dropEvent - Drag processing completed, accepted"); } else { + LOG_ERROR("OsgWidget::dropEvent - Data format error, expected: type|name, actual:{}", modelInfo.toStdString()); event->ignore(); } } else { + LOG_INFO("OsgWidget::dropEvent - Not preset model format, ignore drag"); event->ignore(); } } @@ -241,7 +250,7 @@ void OsgWidget::OnPresetModelDropped(const QString& modelType, const QString& mo // 2. 创建对应的Entity和MeshComponent // 3. 添加到场景中 - qDebug() << "预制模型拖拽:" << modelType << modelName << "位置:" << position; + LOG_INFO("OsgWidget::OnPresetModelDropped - Dropped model type:{} name:{} position:({}, {})", modelType.toStdString(), modelName.toStdString(), position.x(), position.y()); // 这里暂时只是输出调试信息,后续需要集成实体系统 } diff --git a/workspace/presets/jammers.xml b/workspace/presets/jammers.xml index cd936285..9941ef0c 100644 --- a/workspace/presets/jammers.xml +++ b/workspace/presets/jammers.xml @@ -3,21 +3,11 @@ - - - \ No newline at end of file diff --git a/workspace/presets/missiles.xml b/workspace/presets/missiles.xml index d38d5838..f3b873ed 100644 --- a/workspace/presets/missiles.xml +++ b/workspace/presets/missiles.xml @@ -1,23 +1,8 @@ - - - - \ No newline at end of file diff --git a/workspace/presets/satellites.xml b/workspace/presets/satellites.xml index 07f48118..00e6057b 100644 --- a/workspace/presets/satellites.xml +++ b/workspace/presets/satellites.xml @@ -1,23 +1,8 @@ - - - - \ No newline at end of file diff --git a/workspace/presets/ships.xml b/workspace/presets/ships.xml index 98af9f07..8979bbbb 100644 --- a/workspace/presets/ships.xml +++ b/workspace/presets/ships.xml @@ -3,21 +3,11 @@ - - - \ No newline at end of file From 743565fd75f82f3d1549b188632ec343b8c4935f Mon Sep 17 00:00:00 2001 From: brige Date: Sat, 1 Nov 2025 19:33:08 +0800 Subject: [PATCH 2/5] add scene world postion get geo --- src/scene/OEScene.cpp | 58 ++++++++++ src/scene/OEScene.h | 2 + src/translations/Dyt_zh_CN.ts | 4 +- src/ui/ModelBrowser/PresetModelListWidget.cpp | 2 +- src/ui/ModelBrowser/PresetModelPanel.ui | 10 +- src/viewer/OsgWidget.cpp | 108 +++++++++++++++++- src/viewer/OsgWidget.h | 4 +- 7 files changed, 173 insertions(+), 15 deletions(-) diff --git a/src/scene/OEScene.cpp b/src/scene/OEScene.cpp index f5ce9c5e..2c595a4e 100644 --- a/src/scene/OEScene.cpp +++ b/src/scene/OEScene.cpp @@ -103,6 +103,64 @@ osg::Viewport* OEScene::GetViewport() const { return curentView_->getCamera()->getViewport(); } +bool OEScene::ScreenToWorldCoordinate(int x, int y, osg::Vec3d* output) const { + if (nullptr == output) { + LOG_WARN("OEScene::ScreenToWorldCoordinate - worldPoint is null"); + return false; + } + + osg::Camera* camera = curentView_->getCamera(); + if (!camera) { + LOG_WARN("OEScene::ScreenToWorldCoordinate - camera is null"); + return false; + } + + osg::ref_ptr intersector = + new osgUtil::LineSegmentIntersector(osgUtil::Intersector::WINDOW, x, y); + + osgUtil::IntersectionVisitor iv(intersector.get()); + camera->accept(iv); + + if (intersector->containsIntersections()) { + osgUtil::LineSegmentIntersector::Intersections& intersections = intersector->getIntersections(); + osgUtil::LineSegmentIntersector::Intersection intersection = *intersections.begin(); + + osg::Vec3d worldPoint = intersection.getWorldIntersectPoint(); + + Vec3ToLocation(worldPoint, output); + + LOG_INFO("OsgWidget::ScreenToWorldCoordinate - Screen({}, {}) -> World({:.6f}, {:.6f}, {:.2f})", + x, y, output->x(), output->y(), output->z()); + return true; + } + + LOG_WARN("OsgWidget::ScreenToWorldCoordinate - No intersection found for screen coordinates ({}, {})", x, y); + return false; +} + +bool OEScene::Vec3ToLocation(const osg::Vec3& input, osg::Vec3d* output) const { + if (nullptr == output) { + LOG_WARN("OEScene::vec3ToLocation - output is null"); + return false; + } + const osgEarth::SpatialReference* srs = OEScene::GetSrs(); + if (!srs) { + LOG_WARN("OEScene::vec3ToLocation - spatial reference system is null"); + return false; + } + + osgEarth::GeoPoint geoPoint; + geoPoint.fromWorld(srs, input); + + const osgEarth::SpatialReference* wgs84 = osgEarth::SpatialReference::get("wgs84"); + if (wgs84) { + geoPoint = geoPoint.transform(wgs84); + } + + output->set(geoPoint.x(), geoPoint.y(),geoPoint.z()); + return true; +} + const osgEarth::SpatialReference* OEScene::GetSrs() { dyt_check(nullptr != g_srs_); return g_srs_; diff --git a/src/scene/OEScene.h b/src/scene/OEScene.h index d56a954c..f3ba50c6 100644 --- a/src/scene/OEScene.h +++ b/src/scene/OEScene.h @@ -35,6 +35,8 @@ public: } osg::Viewport* GetViewport() const; + bool ScreenToWorldCoordinate(int x, int y, osg::Vec3d* output) const; + bool Vec3ToLocation(const osg::Vec3& input, osg::Vec3d* output) const; static const osgEarth::SpatialReference* GetSrs(); diff --git a/src/translations/Dyt_zh_CN.ts b/src/translations/Dyt_zh_CN.ts index 114ec4c7..49825d5e 100644 --- a/src/translations/Dyt_zh_CN.ts +++ b/src/translations/Dyt_zh_CN.ts @@ -2082,12 +2082,12 @@ OsgWidget - + warning - + open dyt file failed diff --git a/src/ui/ModelBrowser/PresetModelListWidget.cpp b/src/ui/ModelBrowser/PresetModelListWidget.cpp index 3942decf..c0cabf0b 100644 --- a/src/ui/ModelBrowser/PresetModelListWidget.cpp +++ b/src/ui/ModelBrowser/PresetModelListWidget.cpp @@ -57,7 +57,7 @@ void PresetModelListWidget::startDrag(Qt::DropActions supportedActions) { painter.setRenderHint(QPainter::Antialiasing); // Draw semi-transparent background - painter.setBrush(QColor(255, 255, 255, 80)); // White with 70% opacity + painter.setBrush(QColor(255, 255, 255, 50)); // White with 70% opacity painter.setPen(QPen(QColor(200, 200, 200, 200), 1)); // Light gray border painter.drawRoundedRect(2, 2, totalWidth - 4, totalHeight - 4, 4, 4); diff --git a/src/ui/ModelBrowser/PresetModelPanel.ui b/src/ui/ModelBrowser/PresetModelPanel.ui index f0b9eab7..60d64214 100644 --- a/src/ui/ModelBrowser/PresetModelPanel.ui +++ b/src/ui/ModelBrowser/PresetModelPanel.ui @@ -32,7 +32,7 @@ - 1 + 0 @@ -119,8 +119,8 @@ 0 0 - 290 - 470 + 98 + 73 @@ -159,8 +159,8 @@ 0 0 - 290 - 470 + 98 + 73 diff --git a/src/viewer/OsgWidget.cpp b/src/viewer/OsgWidget.cpp index 96628894..5280a035 100644 --- a/src/viewer/OsgWidget.cpp +++ b/src/viewer/OsgWidget.cpp @@ -11,6 +11,8 @@ #include #include #include +#include +#include #include #include @@ -24,6 +26,11 @@ #include "workspace/WorkSpace.h" #include "scutcheon/osgScutcheon.h" +#include "entities/EntitiesManager.h" +#include "entities/ComponentFactory.h" +#include "entities/Entity.h" +#include "entities/Component.h" + static void ConfigureView( osgViewer::View* view ) { view->addEventHandler(new osgViewer::StatsHandler()); view->addEventHandler(new osgViewer::WindowSizeHandler()); @@ -245,13 +252,102 @@ void OsgWidget::dropEvent(QDropEvent* event) { } void OsgWidget::OnPresetModelDropped(const QString& modelType, const QString& modelName, const QPointF& position) { - // TODO: 实现预制模型拖拽处理逻辑 - // 1. 将屏幕坐标转换为世界坐标 - // 2. 创建对应的Entity和MeshComponent - // 3. 添加到场景中 - LOG_INFO("OsgWidget::OnPresetModelDropped - Dropped model type:{} name:{} position:({}, {})", modelType.toStdString(), modelName.toStdString(), position.x(), position.y()); - // 这里暂时只是输出调试信息,后续需要集成实体系统 + WorkSpace* currentWorkSpace = WorkSpaceManager::Get().GetCurrent(); + if (nullptr == currentWorkSpace) { + LOG_WARN("OsgWidget::OnPresetModelDropped - Current workspace is nullptr"); + return; + } + + // 将屏幕坐标转换为世界坐标 + double longitude, latitude, height; + if (!ScreenToWorldCoordinate(static_cast(position.x()), static_cast(position.y()), longitude, latitude, height)) { + LOG_WARN("OsgWidget::OnPresetModelDropped - Failed to convert screen coordinates to world coordinates"); + // 使用默认位置 + longitude = 0.0; + latitude = 0.0; + height = 0.0; + } + + QString meshPath; + if (modelType == "boke") { + meshPath = "boke/boke.ive"; + } else if (modelType == "lsjhqt") { + meshPath = "lsjhqt/lsjhqt.flt"; + } else if (modelType == "nimizi") { + meshPath = "nimizi/nimizi.ive"; + } else if (modelType == "tkdlj") { + meshPath = "tkdlj/tkdlj.ive"; + } else if (modelType == "jiaofan") { + meshPath = "jf/decorative-shape-008.obj"; + } else if (modelType == "satellite") { + meshPath = "satellite/satellite.ive"; + } else { + LOG_WARN("OsgWidget::OnPresetModelDropped - Unknown model type: {}", modelType.toStdString()); + return; + } + + // 创建实体 + Entity* entity = EntitiesManager::Get().CreateMesh(meshPath); + if (nullptr == entity) { + LOG_ERROR("OsgWidget::OnPresetModelDropped - Failed to create entity for mesh: {}", meshPath.toStdString()); + return; + } + + // 设置实体名称(添加序号避免重名) + int count = currentWorkSpace->GetEntities().size(); + entity->SetName(QString("%1_%2").arg(modelName).arg(count)); + + // 设置实体位置 + SceneComponent* rootComponent = entity->GetRootComponent(); + if (rootComponent) { + // 将经纬高坐标转换为OSG世界坐标 + osg::Vec3 worldPos(longitude, latitude, height); + rootComponent->SetLocation(worldPos); + LOG_INFO("OsgWidget::OnPresetModelDropped - Set entity position to ({:.6f}, {:.6f}, {:.2f})", longitude, latitude, height); + } + + // 添加到工作空间 + currentWorkSpace->AddEntity(entity); + + // 添加标签组件 + if (rootComponent) { + SceneComponent* labelComponent = ComponentFactory::Create("LabelComponent", rootComponent); + if (labelComponent) { + labelComponent->AttachTo(rootComponent); + } + } + + // 如果是卫星类型,添加锥波组件 + if (modelType == "satellite") { + if (rootComponent) { + SceneComponent* coneWaveComponent = ComponentFactory::Create("ConeWaveComponent", rootComponent); + if (coneWaveComponent) { + coneWaveComponent->AttachTo(rootComponent); + } + } + } + + LOG_INFO("OsgWidget::OnPresetModelDropped - Successfully created entity: {} with mesh: {} at position ({:.6f}, {:.6f}, {:.2f})", + entity->GetName().toStdString(), meshPath.toStdString(), longitude, latitude, height); +} + +bool OsgWidget::ScreenToWorldCoordinate(int x, int y, double& longitude, double& latitude, double& height) { + if (!activeScene_.valid()) { + LOG_WARN("OsgWidget::ScreenToWorldCoordinate - active scene is null"); + return false; + } + + osg::Vec3d output; + if (!activeScene_->ScreenToWorldCoordinate(x, y, &output)) { + LOG_WARN("OsgWidget::ScreenToWorldCoordinate - Failed to convert screen coordinates to world coordinates"); + return false; + } + + longitude = output.x(); + latitude = output.y(); + height = output.z(); + return true; } diff --git a/src/viewer/OsgWidget.h b/src/viewer/OsgWidget.h index b8664ac4..e8cfc972 100644 --- a/src/viewer/OsgWidget.h +++ b/src/viewer/OsgWidget.h @@ -1,4 +1,4 @@ -#pragma once +#pragma once #include #include @@ -43,6 +43,8 @@ public slots: */ void OnPresetModelDropped(const QString& modelType, const QString& modelName, const QPointF& position = QPointF()); + bool ScreenToWorldCoordinate(int x, int y, double& longitude, double& latitude, double& altitude); + private: QTimer timer_; From 5c5143e6c28dc940c734b924302f3a7eb348d52e Mon Sep 17 00:00:00 2001 From: brige Date: Sat, 1 Nov 2025 21:30:06 +0800 Subject: [PATCH 3/5] add entity factory --- src/app/Application.cpp | 6 + src/entities/EntitiesManager.cpp | 81 +++++++++++- src/entities/EntitiesManager.h | 17 ++- src/entities/EntityFactory.cpp | 108 +++++++++++++++ src/entities/EntityFactory.h | 86 ++++++++++++ src/entities/EntityRegistration.cpp | 88 +++++++++++++ src/entities/EntityRegistration.h | 11 ++ src/entities/SpecializedEntities.cpp | 60 +++++++++ src/entities/SpecializedEntities.h | 58 +++++++++ src/translations/Dyt_zh_CN.ts | 4 +- src/ui/ModelBrowser/PresetModelListWidget.h | 3 +- src/ui/ModelBrowser/PresetModelPanel.cpp | 123 ++---------------- src/ui/ModelBrowser/PresetModelPanel.h | 6 +- src/viewer/OsgWidget.cpp | 60 ++------- .../ModelBrowser => workspace}/ModelInfo.h | 0 .../PresetModelConfigParser.cpp | 26 ++-- .../PresetModelConfigParser.h | 14 +- 17 files changed, 560 insertions(+), 191 deletions(-) create mode 100644 src/entities/EntityFactory.cpp create mode 100644 src/entities/EntityFactory.h create mode 100644 src/entities/EntityRegistration.cpp create mode 100644 src/entities/EntityRegistration.h create mode 100644 src/entities/SpecializedEntities.cpp create mode 100644 src/entities/SpecializedEntities.h rename src/{ui/ModelBrowser => workspace}/ModelInfo.h (100%) rename src/{ui/ModelBrowser => workspace}/PresetModelConfigParser.cpp (91%) rename src/{ui/ModelBrowser => workspace}/PresetModelConfigParser.h (80%) diff --git a/src/app/Application.cpp b/src/app/Application.cpp index 1adb4a48..887fe11d 100644 --- a/src/app/Application.cpp +++ b/src/app/Application.cpp @@ -3,8 +3,10 @@ #include "common/RecourceHelper.h" #include "workspace/WorkSpaceManager.h" #include "entities/EntitiesManager.h" +#include "entities/EntityFactory.h" #include "viewer/OsgViewer.h" #include "scene/MeshManager.h" +#include "workspace/PresetModelConfigParser.h" #include "network/NetDriver.h" #include "python/PythonModule.h" @@ -30,9 +32,11 @@ QString Application::GetBinPath() { void Application::Init() { Singleton::Create(this); Singleton::Create(this); + Singleton::Create(this); Singleton::Create(this); Singleton::Create(this); Singleton::Create(this); + Singleton::Create(this); //Singleton::Create(this); connect(&timer_, &QTimer::timeout, this, &Application::OnTimeout); @@ -46,8 +50,10 @@ void Application::OnTimeout() { void Application::Uninit() { //Singleton::Destory(); Singleton::Destory(); + Singleton::Destory(); Singleton::Destory(); Singleton::Destory(); + Singleton::Destory(); Singleton::Destory(); Singleton::Destory(); } diff --git a/src/entities/EntitiesManager.cpp b/src/entities/EntitiesManager.cpp index a2c47199..e8271843 100644 --- a/src/entities/EntitiesManager.cpp +++ b/src/entities/EntitiesManager.cpp @@ -7,6 +7,8 @@ #include "common/SpdLogger.h" #include "entities/Entity.h" #include "entities/ComponentFactory.h" +#include "entities/EntityFactory.h" +#include "entities/EntityRegistration.h" #include "xml/tinyxml2.h" @@ -14,6 +16,7 @@ template<> EntitiesManager* Singleton::instance_ = nullptr; EntitiesManager::EntitiesManager(QObject* parent) : QObject(parent) { + Initialize(); } EntitiesManager::~EntitiesManager() { @@ -106,9 +109,6 @@ Entity* EntitiesManager::CreateMesh(const QString& mesh) { SceneComponent* conponent = ComponentFactory::Create("MeshComponent", nullptr); conponent->SetAttribute("mesh", mesh.toStdString().c_str()); conponent->AttachEntity(entity); - - //conponent = ComponentFactory::Create("PathComponent", conponent); - //conponent->AttachEntity(entity); return entity; } @@ -148,3 +148,78 @@ void EntitiesManager::RemoveEntity(Entity* entity) { entities_.erase(entity->GetUUid()); } + +void EntitiesManager::Initialize() { + if (!initialized_) { + EntityRegistration::RegisterAllEntities(); + initialized_ = true; + LOG_INFO("EntitiesManager::Initialize - Entity factory initialized"); + } +} + +Entity* EntitiesManager::CreateEntity(const QString& type, WorkSpace* workspace) { + if (!initialized_) { + Initialize(); + } + + if (!workspace) { + workspace = WorkSpaceManager::Get().GetCurrent(); + } + + Entity* entity = EntityFactory::Get().CreateEntity(type, workspace); + if (entity) { + AddEntity(entity); + if (workspace) { + workspace->AddEntity(entity); + } + LOG_INFO("EntitiesManager::CreateEntity - Created entity of type: {}", type.toStdString()); + } else { + LOG_ERROR("EntitiesManager::CreateEntity - Failed to create entity of type: {}", type.toStdString()); + } + + return entity; +} + +Entity* EntitiesManager::CreateEntityWithComponents(const QString& type, WorkSpace* workspace) { + if (!initialized_) { + Initialize(); + } + + if (!workspace) { + workspace = WorkSpaceManager::Get().GetCurrent(); + } + + Entity* entity = EntityFactory::Get().CreateEntityWithComponents(type, workspace); + if (entity) { + AddEntity(entity); + if (workspace) { + workspace->AddEntity(entity); + } + LOG_INFO("EntitiesManager::CreateEntityWithComponents - Created entity with components of type: {}", type.toStdString()); + } else { + LOG_ERROR("EntitiesManager::CreateEntityWithComponents - Failed to create entity of type: {}", type.toStdString()); + } + + return entity; +} + +QStringList EntitiesManager::GetSupportedEntityTypes() const { + if (!initialized_) { + return QStringList(); + } + return EntityFactory::Get().GetRegisteredTypes(); +} + +QString EntitiesManager::GetEntityDisplayName(const QString& type) const { + if (!initialized_) { + return type; + } + return EntityFactory::Get().GetDisplayName(type); +} + +QString EntitiesManager::GetEntityDescription(const QString& type) const { + if (!initialized_) { + return QString(); + } + return EntityFactory::Get().GetDescription(type); +} diff --git a/src/entities/EntitiesManager.h b/src/entities/EntitiesManager.h index 80bdd69a..ad2b43d5 100644 --- a/src/entities/EntitiesManager.h +++ b/src/entities/EntitiesManager.h @@ -9,6 +9,7 @@ #include "xml/tinyxml2.h" class Entity; +class WorkSpace; class EntitiesManager : public QObject, public Singleton { Q_OBJECT @@ -18,12 +19,26 @@ public: ~EntitiesManager(); void OnDestory(); + // Initialize entity factory + void Initialize(); + bool Contains(const QString& name) const; bool Parse(const tinyxml2::XMLElement* element, class WorkSpace* workspce); + // Legacy methods (for backward compatibility) Entity* Create(const QString& name); Entity* CreateMesh(const QString& mesh); + + // New factory methods + Entity* CreateEntity(const QString& type, WorkSpace* workspace = nullptr); + Entity* CreateEntityWithComponents(const QString& type, WorkSpace* workspace = nullptr); + + // Get supported entity types + QStringList GetSupportedEntityTypes() const; + QString GetEntityDisplayName(const QString& type) const; + QString GetEntityDescription(const QString& type) const; + bool DeleteEntity(Entity* entity); Entity* GetEntity(const QString& uuid); @@ -36,5 +51,5 @@ protected: private: std::unordered_map entities_; - + bool initialized_{false}; }; \ No newline at end of file diff --git a/src/entities/EntityFactory.cpp b/src/entities/EntityFactory.cpp new file mode 100644 index 00000000..3107dfd4 --- /dev/null +++ b/src/entities/EntityFactory.cpp @@ -0,0 +1,108 @@ +#include "entities/EntityFactory.h" +#include "entities/ComponentFactory.h" +#include "common/SpdLogger.h" +#include "workspace/WorkSpace.h" +#include + +template<> EntityFactory* Singleton::instance_ = nullptr; + +Entity* EntityFactory::CreateEntity(const QString& type, WorkSpace* workspace) { + auto it = creators_.find(type); + if (it == creators_.end()) { + LOG_WARN("EntityFactory::CreateEntity - Unknown entity type: {}", type.toStdString()); + return nullptr; + } + + Entity* entity = it->second->CreateEntity(workspace); + if (!entity) { + LOG_ERROR("EntityFactory::CreateEntity - Failed to create entity of type: {}", type.toStdString()); + return nullptr; + } + + LOG_INFO("EntityFactory::CreateEntity - Successfully created entity of type: {}", type.toStdString()); + return entity; +} + +Entity* EntityFactory::CreateEntityWithComponents(const QString& type, WorkSpace* workspace) { + Entity* entity = CreateEntity(type, workspace); + if (!entity) { + return nullptr; + } + + auto it = creators_.find(type); + if (it == creators_.end()) { + return entity; + } + + // Get default mesh + QString defaultMesh = it->second->GetDefaultMesh(); + if (!defaultMesh.isEmpty()) { + SceneComponent* meshComponent = ComponentFactory::Create("MeshComponent", nullptr); + if (meshComponent) { + meshComponent->SetAttribute("mesh", defaultMesh.toStdString().c_str()); + meshComponent->AttachEntity(entity); + LOG_INFO("EntityFactory::CreateEntityWithComponents - Added MeshComponent with mesh: {}", defaultMesh.toStdString()); + } + } + + // Add required components + QStringList requiredComponents = it->second->GetRequiredComponents(); + SceneComponent* rootComponent = entity->GetRootComponent(); + + for (int i = 0; i < requiredComponents.size(); ++i) { + QString componentType = requiredComponents.at(i); + SceneComponent* component = ComponentFactory::Create(componentType, rootComponent); + if (component && rootComponent) { + component->AttachTo(rootComponent); + LOG_INFO("EntityFactory::CreateEntityWithComponents - Added component: {}", componentType.toStdString()); + } else { + LOG_WARN("EntityFactory::CreateEntityWithComponents - Failed to create component: {}", componentType.toStdString()); + } + } + + return entity; +} + +QStringList EntityFactory::GetRegisteredTypes() const { + QStringList types; + for (auto it = creators_.begin(); it != creators_.end(); ++it) { + types.append(it->first); + } + return types; +} + +QString EntityFactory::GetDisplayName(const QString& type) const { + auto it = creators_.find(type); + if (it != creators_.end()) { + return it->second->GetDisplayName(); + } + return type; // 返回类型名作为默认显示名 +} + +QString EntityFactory::GetDescription(const QString& type) const { + auto it = creators_.find(type); + if (it != creators_.end()) { + return it->second->GetDescription(); + } + return QString(); +} + +QString EntityFactory::GetDefaultMesh(const QString& type) const { + auto it = creators_.find(type); + if (it != creators_.end()) { + return it->second->GetDefaultMesh(); + } + return QString(); +} + +QStringList EntityFactory::GetRequiredComponents(const QString& type) const { + auto it = creators_.find(type); + if (it != creators_.end()) { + return it->second->GetRequiredComponents(); + } + return QStringList(); +} + +bool EntityFactory::IsTypeSupported(const QString& type) const { + return creators_.find(type) != creators_.end(); +} \ No newline at end of file diff --git a/src/entities/EntityFactory.h b/src/entities/EntityFactory.h new file mode 100644 index 00000000..7a40e94f --- /dev/null +++ b/src/entities/EntityFactory.h @@ -0,0 +1,86 @@ +#pragma once + +#include +#include +#include +#include + +#include "app/Singleton.h" +#include "entities/Entity.h" + +class WorkSpace; + +class IEntityCreator { +public: + virtual ~IEntityCreator() = default; + virtual Entity* CreateEntity(WorkSpace* workspace) = 0; + virtual QString GetEntityType() const = 0; + virtual QString GetDisplayName() const = 0; + virtual QString GetDescription() const = 0; + virtual QString GetDefaultMesh() const = 0; + virtual QStringList GetRequiredComponents() const = 0; +}; + +template +class EntityCreator : public IEntityCreator { +public: + EntityCreator(const QString& type, const QString& displayName, + const QString& description, const QString& defaultMesh, + const QStringList& requiredComponents = QStringList()) + : entityType_(type), displayName_(displayName), description_(description), + defaultMesh_(defaultMesh), requiredComponents_(requiredComponents) {} + + Entity* CreateEntity(WorkSpace* workspace) override { + return new T(workspace); + } + + QString GetEntityType() const override { return entityType_; } + QString GetDisplayName() const override { return displayName_; } + QString GetDescription() const override { return description_; } + QString GetDefaultMesh() const override { return defaultMesh_; } + QStringList GetRequiredComponents() const override { return requiredComponents_; } + +private: + QString entityType_; + QString displayName_; + QString description_; + QString defaultMesh_; + QStringList requiredComponents_; +}; + +class EntityFactory : public Singleton { +public: + EntityFactory() = default; + explicit EntityFactory(class Application* app) : app_(app) {} + ~EntityFactory() = default; + + void OnDestory() { } + + template + void RegisterEntity(const QString& type, const QString& displayName, + const QString& description, const QString& defaultMesh, + const QStringList& requiredComponents = QStringList()) { + auto creator = std::make_unique>(type, displayName, description, defaultMesh, requiredComponents); + creators_[type] = std::move(creator); + } + + Entity* CreateEntity(const QString& type, WorkSpace* workspace); + + Entity* CreateEntityWithComponents(const QString& type, WorkSpace* workspace); + + QStringList GetRegisteredTypes() const; + + QString GetDisplayName(const QString& type) const; + QString GetDescription(const QString& type) const; + QString GetDefaultMesh(const QString& type) const; + QStringList GetRequiredComponents(const QString& type) const; + + bool IsTypeSupported(const QString& type) const; + +private: + std::unordered_map> creators_; + class Application* app_ = nullptr; +}; + +#define REGISTER_ENTITY(EntityClass, Type, DisplayName, Description, DefaultMesh, ...) \ + EntityFactory::Get().RegisterEntity(Type, DisplayName, Description, DefaultMesh, QStringList{__VA_ARGS__}) \ No newline at end of file diff --git a/src/entities/EntityRegistration.cpp b/src/entities/EntityRegistration.cpp new file mode 100644 index 00000000..ae6a086e --- /dev/null +++ b/src/entities/EntityRegistration.cpp @@ -0,0 +1,88 @@ +#include "entities/EntityRegistration.h" +#include "common/SpdLogger.h" + +void EntityRegistration::RegisterAllEntities() { + EntityFactory& factory = EntityFactory::Get(); + + // Register missile entity + factory.RegisterEntity("Missile", + "Missile", + "Military missile entity with target tracking and flight capabilities", + "models/missile.osg", + QStringList() << "LabelComponent" << "TrajectoryComponent" + ); + + // Register satellite entity + factory.RegisterEntity("Satellite", + "Satellite", + "Artificial satellite entity with orbital motion and communication capabilities", + "models/satellite.osg", + QStringList() << "LabelComponent" << "ConeWaveComponent" << "OrbitComponent" + ); + + // Register ship entity + factory.RegisterEntity("Ship", + "Ship", + "Naval vessel entity with maritime navigation and combat capabilities", + "models/ship.osg", + QStringList() << "LabelComponent" << "NavigationComponent" + ); + + // Register radar entity + factory.RegisterEntity("Radar", + "Radar", + "Radar system entity with target detection and tracking capabilities", + "models/radar.osg", + QStringList() << "LabelComponent" << "RadarComponent" << "ScanComponent" + ); + + // Register vehicle entity + factory.RegisterEntity("Vehicle", + "Vehicle", + "Generic vehicle entity configurable for different types of transportation", + "models/vehicle.osg", + QStringList() << "LabelComponent" << "MovementComponent" + ); + + // Register aircraft entity + factory.RegisterEntity("Aircraft", + "Aircraft", + "Aviation entity with flight and aerial combat capabilities", + "models/aircraft.osg", + QStringList() << "LabelComponent" << "FlightComponent" << "NavigationComponent" + ); + + // Register tank entity + factory.RegisterEntity("Tank", + "Tank", + "Armored vehicle entity with ground combat capabilities", + "models/tank.osg", + QStringList() << "LabelComponent" << "ArmorComponent" << "WeaponComponent" + ); + + // Register submarine entity + factory.RegisterEntity("Submarine", + "Submarine", + "Submarine entity with underwater combat capabilities", + "models/submarine.osg", + QStringList() << "LabelComponent" << "SonarComponent" << "TorpedoComponent" + ); + + // Register base entity + factory.RegisterEntity("Base", + "Base", + "Military base entity serving as command and logistics center", + "models/base.osg", + QStringList() << "LabelComponent" << "CommandComponent" << "DefenseComponent" + ); + + // Register generic entity (for backward compatibility) + factory.RegisterEntity("Entity", + "Entity", + "Basic entity type extensible through component system", + "models/default.osg", + QStringList() << "LabelComponent" + ); + + LOG_INFO("EntityRegistration::RegisterAllEntities - All entity types registered successfully"); +} \ No newline at end of file diff --git a/src/entities/EntityRegistration.h b/src/entities/EntityRegistration.h new file mode 100644 index 00000000..832cd7bf --- /dev/null +++ b/src/entities/EntityRegistration.h @@ -0,0 +1,11 @@ +#pragma once + +#include "entities/EntityFactory.h" +#include "entities/SpecializedEntities.h" + +// Entity registration class +class EntityRegistration { +public: + // Register all entity types + static void RegisterAllEntities(); +}; \ No newline at end of file diff --git a/src/entities/SpecializedEntities.cpp b/src/entities/SpecializedEntities.cpp new file mode 100644 index 00000000..58288a1e --- /dev/null +++ b/src/entities/SpecializedEntities.cpp @@ -0,0 +1,60 @@ +#include "entities/SpecializedEntities.h" + +#include + +#include "common/SpdLogger.h" + +MissileEntity::MissileEntity(WorkSpace* workspace) + : Entity(workspace) { + SetName("Missile"); + LOG_INFO("MissileEntity created"); +} + +MissileEntity::MissileEntity(const QString& name, QObject* parent) + : Entity(name, parent) { + LOG_INFO("MissileEntity created with name: {}", name.toStdString()); +} + +SatelliteEntity::SatelliteEntity(WorkSpace* workspace) + : Entity(workspace) { + SetName("Satellite"); + LOG_INFO("SatelliteEntity created"); +} + +SatelliteEntity::SatelliteEntity(const QString& name, QObject* parent) + : Entity(name, parent) { + LOG_INFO("SatelliteEntity created with name: {}", name.toStdString()); +} + +ShipEntity::ShipEntity(WorkSpace* workspace) + : Entity(workspace) { + SetName("Ship"); + LOG_INFO("ShipEntity created"); +} + +ShipEntity::ShipEntity(const QString& name, QObject* parent) + : Entity(name, parent) { + LOG_INFO("ShipEntity created with name: {}", name.toStdString()); +} + +RadarEntity::RadarEntity(WorkSpace* workspace) + : Entity(workspace) { + SetName("Radar"); + LOG_INFO("RadarEntity created"); +} + +RadarEntity::RadarEntity(const QString& name, QObject* parent) + : Entity(name, parent) { + LOG_INFO("RadarEntity created with name: {}", name.toStdString()); +} + +VehicleEntity::VehicleEntity(WorkSpace* workspace) + : Entity(workspace) { + SetName("Vehicle"); + LOG_INFO("VehicleEntity created"); +} + +VehicleEntity::VehicleEntity(const QString& name, QObject* parent) + : Entity(name, parent) { + LOG_INFO("VehicleEntity created with name: {}", name.toStdString()); +} diff --git a/src/entities/SpecializedEntities.h b/src/entities/SpecializedEntities.h new file mode 100644 index 00000000..44c16011 --- /dev/null +++ b/src/entities/SpecializedEntities.h @@ -0,0 +1,58 @@ +#pragma once + +#include "entities/Entity.h" + +// 导弹实体 +class MissileEntity : public Entity { + Q_OBJECT + +public: + explicit MissileEntity(WorkSpace* workspace); + explicit MissileEntity(const QString& name, QObject* parent = nullptr); + ~MissileEntity() override = default; + +}; + +// 卫星实体 +class SatelliteEntity : public Entity { + Q_OBJECT + +public: + explicit SatelliteEntity(WorkSpace* workspace); + explicit SatelliteEntity(const QString& name, QObject* parent = nullptr); + ~SatelliteEntity() override = default; + +}; + +// 舰船实体 +class ShipEntity : public Entity { + Q_OBJECT + +public: + explicit ShipEntity(WorkSpace* workspace); + explicit ShipEntity(const QString& name, QObject* parent = nullptr); + ~ShipEntity() override = default; + +}; + +// 雷达实体 +class RadarEntity : public Entity { + Q_OBJECT + +public: + explicit RadarEntity(WorkSpace* workspace); + explicit RadarEntity(const QString& name, QObject* parent = nullptr); + ~RadarEntity() override = default; + +}; + +// 通用载具实体 +class VehicleEntity : public Entity { + Q_OBJECT + +public: + explicit VehicleEntity(WorkSpace* workspace); + explicit VehicleEntity(const QString& name, QObject* parent = nullptr); + ~VehicleEntity() override = default; + +}; \ No newline at end of file diff --git a/src/translations/Dyt_zh_CN.ts b/src/translations/Dyt_zh_CN.ts index 49825d5e..9a5fbf47 100644 --- a/src/translations/Dyt_zh_CN.ts +++ b/src/translations/Dyt_zh_CN.ts @@ -2082,12 +2082,12 @@ OsgWidget - + warning - + open dyt file failed diff --git a/src/ui/ModelBrowser/PresetModelListWidget.h b/src/ui/ModelBrowser/PresetModelListWidget.h index 408fec71..65c46e78 100644 --- a/src/ui/ModelBrowser/PresetModelListWidget.h +++ b/src/ui/ModelBrowser/PresetModelListWidget.h @@ -1,6 +1,7 @@ #include #include -#include "ui/ModelBrowser/ModelInfo.h" + +#include "workspace/ModelInfo.h" class PresetModelListWidget : public QListWidget { diff --git a/src/ui/ModelBrowser/PresetModelPanel.cpp b/src/ui/ModelBrowser/PresetModelPanel.cpp index dcafb751..c9f7cccf 100644 --- a/src/ui/ModelBrowser/PresetModelPanel.cpp +++ b/src/ui/ModelBrowser/PresetModelPanel.cpp @@ -55,50 +55,48 @@ void PresetModelPanel::InitUI() { // Try to load model data from configuration files QString presetsPath = QString("%1/presets").arg(RecourceHelper::Get().GetResourcesPath()); - if (configParser_.LoadAllConfigs(presetsPath)) { + if (PresetModelConfigParser::Get().LoadAllConfigs(presetsPath)) { // Successfully loaded from configuration files LoadModelsFromConfig(); } else { // Failed to load configuration files, use hardcoded defaults as fallback LOG_WARN("Failed to load preset models from config files, using hardcoded defaults. Error: {}", - configParser_.GetLastError().toStdString()); + PresetModelConfigParser::Get().GetLastError().toStdString()); LoadDefaultModels(); } } -void PresetModelPanel::LoadModelsFromConfig() -{ +void PresetModelPanel::LoadModelsFromConfig() { // Load ship models - ModelCategory shipCategory = configParser_.GetCategory("Ship"); + ModelCategory shipCategory = PresetModelConfigParser::Get().GetCategory("Ship"); if (!shipCategory.models.isEmpty()) { ui->shipList->setModelType("Ship"); ui->shipList->setModelList(shipCategory.models); } // Load satellite models - ModelCategory satelliteCategory = configParser_.GetCategory("Satellite"); + ModelCategory satelliteCategory = PresetModelConfigParser::Get().GetCategory("Satellite"); if (!satelliteCategory.models.isEmpty()) { ui->satelliteList->setModelType("Satellite"); ui->satelliteList->setModelList(satelliteCategory.models); } // Load missile models - ModelCategory missileCategory = configParser_.GetCategory("Missile"); + ModelCategory missileCategory = PresetModelConfigParser::Get().GetCategory("Missile"); if (!missileCategory.models.isEmpty()) { ui->missileList->setModelType("Missile"); ui->missileList->setModelList(missileCategory.models); } // Load jammer models - ModelCategory jammerCategory = configParser_.GetCategory("Jammer"); + ModelCategory jammerCategory = PresetModelConfigParser::Get().GetCategory("Jammer"); if (!jammerCategory.models.isEmpty()) { ui->jammerList->setModelType("Jammer"); ui->jammerList->setModelList(jammerCategory.models); } } -void PresetModelPanel::LoadDefaultModels() -{ +void PresetModelPanel::LoadDefaultModels() { // Use hardcoded default models as fallback mechanism QVector shipModels = { {"destroyer", "Destroyer", "Naval destroyer vessel", "", ":/icons/ship.png", true}, @@ -136,108 +134,3 @@ void PresetModelPanel::LoadDefaultModels() ui->jammerList->setModelType("Jammer"); ui->jammerList->setModelList(jammerModels); } - -// void PresetModelPanel::OnModelItemDoubleClicked(QListWidgetItem* item) -// { -// if (!item) return; - -// // Get the list widget that contains this item -// QListWidget* listWidget = item->listWidget(); -// if (!listWidget) return; - -// QString modelType = listWidget->property("modelType").toString(); - -// // Get model name from stored data -// QString modelName = item->data(Qt::UserRole).toString(); -// if (modelName.isEmpty()) { -// modelName = item->text(); // Fallback to display text -// } - -// LOG_INFO("Model double-clicked: {} of type {}", qPrintable(modelName), qPrintable(modelType)); - -// // Emit signal for model selection -// emit ModelDropped(modelType, modelName); -// } - -// bool PresetModelPanel::eventFilter(QObject* obj, QEvent* event) -// { -// QListWidget* listWidget = qobject_cast(obj); -// if (!listWidget) { -// return QWidget::eventFilter(obj, event); -// } - -// if (event->type() == QEvent::MouseButtonPress) { -// QMouseEvent* mouseEvent = static_cast(event); -// if (mouseEvent->button() == Qt::LeftButton) { -// dragStartPosition_ = mouseEvent->pos(); -// dragSourceWidget_ = listWidget; -// } -// } else if (event->type() == QEvent::MouseMove) { -// QMouseEvent* mouseEvent = static_cast(event); -// if (!(mouseEvent->buttons() & Qt::LeftButton)) { -// return QWidget::eventFilter(obj, event); -// } - -// if ((mouseEvent->pos() - dragStartPosition_).manhattanLength() < QApplication::startDragDistance()) { -// return QWidget::eventFilter(obj, event); -// } - -// QListWidgetItem* item = listWidget->itemAt(dragStartPosition_); -// if (!item) { -// return QWidget::eventFilter(obj, event); -// } - -// QString modelType = listWidget->property("modelType").toString(); - -// // Get model name from stored data -// QString modelName = item->data(Qt::UserRole).toString(); -// if (modelName.isEmpty()) { -// modelName = item->text(); // Fallback to item text -// } - -// // Create mime data -// QMimeData* mimeData = new QMimeData; -// QString dragData = QString("%1|%2").arg(modelType, modelName); -// mimeData->setData("application/x-preset-model", dragData.toUtf8()); - -// // Create drag object -// QDrag* drag = new QDrag(this); -// drag->setMimeData(mimeData); - -// // Set drag pixmap (icon or text) -// QPixmap pixmap(item->icon().pixmap(32, 32)); -// if (pixmap.isNull()) { -// pixmap = QPixmap(100, 30); -// pixmap.fill(Qt::transparent); -// QPainter painter(&pixmap); -// painter.setPen(Qt::black); -// painter.drawText(pixmap.rect(), Qt::AlignCenter, item->text()); -// } -// drag->setPixmap(pixmap); - -// // Execute drag -// Qt::DropAction dropAction = drag->exec(Qt::CopyAction); -// Q_UNUSED(dropAction); - -// return true; -// } - -// return QWidget::eventFilter(obj, event); -// } - -// // DraggableListWidget implementation -// DraggableListWidget::DraggableListWidget(const QString& modelType, QWidget* parent) -// : QListWidget(parent), modelType_(modelType) -// { -// setProperty("modelType", modelType); -// } - -// void DraggableListWidget::mousePressEvent(QMouseEvent* event) -// { -// QListWidget::mousePressEvent(event); -// } - -// void DraggableListWidget::mouseMoveEvent(QMouseEvent* event) -// { -// QListWidget::mouseMoveEvent(event); -// } \ No newline at end of file diff --git a/src/ui/ModelBrowser/PresetModelPanel.h b/src/ui/ModelBrowser/PresetModelPanel.h index 1fe4aa62..6d220217 100644 --- a/src/ui/ModelBrowser/PresetModelPanel.h +++ b/src/ui/ModelBrowser/PresetModelPanel.h @@ -14,8 +14,9 @@ #include #include #include -#include "ModelInfo.h" -#include "PresetModelConfigParser.h" + +#include "workspace/ModelInfo.h" +#include "workspace/PresetModelConfigParser.h" namespace Ui { class PresetModelPanel; } @@ -62,7 +63,6 @@ private: private: Ui::PresetModelPanel *ui; - PresetModelConfigParser configParser_; // Drag and drop support QPoint dragStartPosition_; diff --git a/src/viewer/OsgWidget.cpp b/src/viewer/OsgWidget.cpp index 5280a035..0a28707a 100644 --- a/src/viewer/OsgWidget.cpp +++ b/src/viewer/OsgWidget.cpp @@ -25,6 +25,7 @@ #include "workspace/WorkSpaceManager.h" #include "workspace/WorkSpace.h" #include "scutcheon/osgScutcheon.h" +#include "workspace/PresetModelConfigParser.h" #include "entities/EntitiesManager.h" #include "entities/ComponentFactory.h" @@ -269,68 +270,25 @@ void OsgWidget::OnPresetModelDropped(const QString& modelType, const QString& mo latitude = 0.0; height = 0.0; } - - QString meshPath; - if (modelType == "boke") { - meshPath = "boke/boke.ive"; - } else if (modelType == "lsjhqt") { - meshPath = "lsjhqt/lsjhqt.flt"; - } else if (modelType == "nimizi") { - meshPath = "nimizi/nimizi.ive"; - } else if (modelType == "tkdlj") { - meshPath = "tkdlj/tkdlj.ive"; - } else if (modelType == "jiaofan") { - meshPath = "jf/decorative-shape-008.obj"; - } else if (modelType == "satellite") { - meshPath = "satellite/satellite.ive"; - } else { - LOG_WARN("OsgWidget::OnPresetModelDropped - Unknown model type: {}", modelType.toStdString()); - return; - } - - // 创建实体 - Entity* entity = EntitiesManager::Get().CreateMesh(meshPath); + + Entity* entity = EntitiesManager::Get().CreateEntityWithComponents(modelType, currentWorkSpace); if (nullptr == entity) { - LOG_ERROR("OsgWidget::OnPresetModelDropped - Failed to create entity for mesh: {}", meshPath.toStdString()); + LOG_ERROR("OsgWidget::OnPresetModelDropped - Failed to create entity of type: {}", modelType.toStdString()); return; } - - // 设置实体名称(添加序号避免重名) + int count = currentWorkSpace->GetEntities().size(); entity->SetName(QString("%1_%2").arg(modelName).arg(count)); - - // 设置实体位置 + SceneComponent* rootComponent = entity->GetRootComponent(); if (rootComponent) { - // 将经纬高坐标转换为OSG世界坐标 osg::Vec3 worldPos(longitude, latitude, height); rootComponent->SetLocation(worldPos); LOG_INFO("OsgWidget::OnPresetModelDropped - Set entity position to ({:.6f}, {:.6f}, {:.2f})", longitude, latitude, height); } - - // 添加到工作空间 - currentWorkSpace->AddEntity(entity); - - // 添加标签组件 - if (rootComponent) { - SceneComponent* labelComponent = ComponentFactory::Create("LabelComponent", rootComponent); - if (labelComponent) { - labelComponent->AttachTo(rootComponent); - } - } - - // 如果是卫星类型,添加锥波组件 - if (modelType == "satellite") { - if (rootComponent) { - SceneComponent* coneWaveComponent = ComponentFactory::Create("ConeWaveComponent", rootComponent); - if (coneWaveComponent) { - coneWaveComponent->AttachTo(rootComponent); - } - } - } - - LOG_INFO("OsgWidget::OnPresetModelDropped - Successfully created entity: {} with mesh: {} at position ({:.6f}, {:.6f}, {:.2f})", - entity->GetName().toStdString(), meshPath.toStdString(), longitude, latitude, height); + + LOG_INFO("OsgWidget::OnPresetModelDropped - Successfully created {} entity: {} at position ({:.6f}, {:.6f}, {:.2f})", + modelType.toStdString(), entity->GetName().toStdString(), longitude, latitude, height); } bool OsgWidget::ScreenToWorldCoordinate(int x, int y, double& longitude, double& latitude, double& height) { diff --git a/src/ui/ModelBrowser/ModelInfo.h b/src/workspace/ModelInfo.h similarity index 100% rename from src/ui/ModelBrowser/ModelInfo.h rename to src/workspace/ModelInfo.h diff --git a/src/ui/ModelBrowser/PresetModelConfigParser.cpp b/src/workspace/PresetModelConfigParser.cpp similarity index 91% rename from src/ui/ModelBrowser/PresetModelConfigParser.cpp rename to src/workspace/PresetModelConfigParser.cpp index ffecfe72..c0fd4428 100644 --- a/src/ui/ModelBrowser/PresetModelConfigParser.cpp +++ b/src/workspace/PresetModelConfigParser.cpp @@ -1,19 +1,21 @@ -#include "PresetModelConfigParser.h" -#include "../../xml/tinyxml2.h" -#include "../../common/SpdLogger.h" +#include "workspace/PresetModelConfigParser.h" + #include #include -PresetModelConfigParser::PresetModelConfigParser() -{ +#include "xml/tinyxml2.h" +#include "common/SpdLogger.h" + +template<> PresetModelConfigParser* Singleton::instance_ = nullptr; + +PresetModelConfigParser::PresetModelConfigParser(QObject* parent) + : QObject(parent) { } -PresetModelConfigParser::~PresetModelConfigParser() -{ +PresetModelConfigParser::~PresetModelConfigParser() { } -bool PresetModelConfigParser::LoadAllConfigs(const QString& configDir) -{ +bool PresetModelConfigParser::LoadAllConfigs(const QString& configDir) { Clear(); QDir dir(configDir); @@ -158,16 +160,18 @@ ModelCategory PresetModelConfigParser::GetCategory(const QString& categoryName) return ModelCategory(); } -ModelInfo PresetModelConfigParser::GetModelInfo(const QString& categoryName, const QString& modelName) const -{ +ModelInfo PresetModelConfigParser::GetModelInfo(const QString& categoryName, const QString& modelName, bool* success) const { if (m_categories.contains(categoryName)) { const ModelCategory& category = m_categories[categoryName]; for (const ModelInfo& model : category.models) { if (model.name == modelName) { + if (success) *success = true; return model; } } } + + if (success) *success = false; return ModelInfo(); } diff --git a/src/ui/ModelBrowser/PresetModelConfigParser.h b/src/workspace/PresetModelConfigParser.h similarity index 80% rename from src/ui/ModelBrowser/PresetModelConfigParser.h rename to src/workspace/PresetModelConfigParser.h index a61f1d27..cb785a38 100644 --- a/src/ui/ModelBrowser/PresetModelConfigParser.h +++ b/src/workspace/PresetModelConfigParser.h @@ -1,16 +1,22 @@ #pragma once +#include #include #include #include + +#include "app/Singleton.h" + #include "ModelInfo.h" -class PresetModelConfigParser -{ +class PresetModelConfigParser : public QObject, public Singleton { + Q_OBJECT public: - PresetModelConfigParser(); + explicit PresetModelConfigParser(QObject* parent = nullptr); ~PresetModelConfigParser(); + void OnDestory() { } + // Load all preset model configurations bool LoadAllConfigs(const QString& configDir = "workspace/presets"); @@ -27,7 +33,7 @@ public: ModelCategory GetCategory(const QString& categoryName) const; // Get model information - ModelInfo GetModelInfo(const QString& categoryName, const QString& modelName) const; + ModelInfo GetModelInfo(const QString& categoryName, const QString& modelName, bool* success) const; // Check if configuration data exists bool HasData() const; From 596d60f6bcfed19f1edaf9887839cfc6c438cfde Mon Sep 17 00:00:00 2001 From: brige Date: Sun, 2 Nov 2025 00:02:20 +0800 Subject: [PATCH 4/5] modify entity --- src/entities/EntitiesManager.cpp | 4 +- src/entities/EntitiesManager.h | 2 +- src/entities/EntityFactory.cpp | 25 ++- src/entities/EntityFactory.h | 2 +- src/translations/Dyt_zh_CN.ts | 24 +-- src/ui/ModelBrowser/PresetModelListWidget.cpp | 2 + src/ui/ModelBrowser/PresetModelListWidget.h | 2 + src/ui/ModelBrowser/PresetModelPanel.cpp | 144 +++++++++------- src/ui/ModelBrowser/PresetModelPanel.h | 23 ++- src/ui/ModelBrowser/PresetModelPanel.ui | 162 +----------------- src/viewer/OsgWidget.cpp | 10 +- src/viewer/OsgWidget.h | 2 +- src/workspace/ModelInfo.h | 11 +- src/workspace/PresetModelConfigParser.cpp | 7 + workspace/presets/jammers.xml | 2 +- workspace/presets/ships.xml | 4 +- 16 files changed, 158 insertions(+), 268 deletions(-) diff --git a/src/entities/EntitiesManager.cpp b/src/entities/EntitiesManager.cpp index e8271843..c13abace 100644 --- a/src/entities/EntitiesManager.cpp +++ b/src/entities/EntitiesManager.cpp @@ -180,7 +180,7 @@ Entity* EntitiesManager::CreateEntity(const QString& type, WorkSpace* workspace) return entity; } -Entity* EntitiesManager::CreateEntityWithComponents(const QString& type, WorkSpace* workspace) { +Entity* EntitiesManager::CreateEntityWithComponents(const QString& type, const QString& mesh, bool useLabel, WorkSpace* workspace) { if (!initialized_) { Initialize(); } @@ -189,7 +189,7 @@ Entity* EntitiesManager::CreateEntityWithComponents(const QString& type, WorkSpa workspace = WorkSpaceManager::Get().GetCurrent(); } - Entity* entity = EntityFactory::Get().CreateEntityWithComponents(type, workspace); + Entity* entity = EntityFactory::Get().CreateEntityWithComponents(type, mesh, useLabel, workspace); if (entity) { AddEntity(entity); if (workspace) { diff --git a/src/entities/EntitiesManager.h b/src/entities/EntitiesManager.h index ad2b43d5..5d223127 100644 --- a/src/entities/EntitiesManager.h +++ b/src/entities/EntitiesManager.h @@ -32,7 +32,7 @@ public: // New factory methods Entity* CreateEntity(const QString& type, WorkSpace* workspace = nullptr); - Entity* CreateEntityWithComponents(const QString& type, WorkSpace* workspace = nullptr); + Entity* CreateEntityWithComponents(const QString& type, const QString& mesh, bool useLabel, WorkSpace* workspace = nullptr); // Get supported entity types QStringList GetSupportedEntityTypes() const; diff --git a/src/entities/EntityFactory.cpp b/src/entities/EntityFactory.cpp index 3107dfd4..9a46e28a 100644 --- a/src/entities/EntityFactory.cpp +++ b/src/entities/EntityFactory.cpp @@ -23,7 +23,7 @@ Entity* EntityFactory::CreateEntity(const QString& type, WorkSpace* workspace) { return entity; } -Entity* EntityFactory::CreateEntityWithComponents(const QString& type, WorkSpace* workspace) { +Entity* EntityFactory::CreateEntityWithComponents(const QString& type, const QString& mesh, bool useLabel, WorkSpace* workspace) { Entity* entity = CreateEntity(type, workspace); if (!entity) { return nullptr; @@ -36,15 +36,36 @@ Entity* EntityFactory::CreateEntityWithComponents(const QString& type, WorkSpace // Get default mesh QString defaultMesh = it->second->GetDefaultMesh(); + if (!mesh.isEmpty()) { + defaultMesh = mesh; + } if (!defaultMesh.isEmpty()) { SceneComponent* meshComponent = ComponentFactory::Create("MeshComponent", nullptr); if (meshComponent) { - meshComponent->SetAttribute("mesh", defaultMesh.toStdString().c_str()); + meshComponent->SetAttribute("mesh", defaultMesh.toLocal8Bit()); meshComponent->AttachEntity(entity); LOG_INFO("EntityFactory::CreateEntityWithComponents - Added MeshComponent with mesh: {}", defaultMesh.toStdString()); } } + if (useLabel) { + SceneComponent* rootComponent = entity->GetRootComponent(); + if (nullptr == rootComponent) { + SceneComponent* meshComponent = ComponentFactory::Create("LabelComponent", nullptr); + if (meshComponent) { + meshComponent->AttachEntity(entity); + LOG_INFO("EntityFactory::CreateEntityWithComponents - Added LabelComponent with mesh: {}", defaultMesh.toStdString()); + } + } + else { + SceneComponent* LabelComponent = ComponentFactory::Create("LabelComponent", rootComponent); + if (LabelComponent) { + LabelComponent->AttachTo(rootComponent); + LOG_INFO("EntityFactory::CreateEntityWithComponents - Added LabelComponent with mesh: {}", defaultMesh.toStdString()); + } + } + } + // Add required components QStringList requiredComponents = it->second->GetRequiredComponents(); SceneComponent* rootComponent = entity->GetRootComponent(); diff --git a/src/entities/EntityFactory.h b/src/entities/EntityFactory.h index 7a40e94f..33e02898 100644 --- a/src/entities/EntityFactory.h +++ b/src/entities/EntityFactory.h @@ -66,7 +66,7 @@ public: Entity* CreateEntity(const QString& type, WorkSpace* workspace); - Entity* CreateEntityWithComponents(const QString& type, WorkSpace* workspace); + Entity* CreateEntityWithComponents(const QString& type, const QString& mesh, bool useLabel, WorkSpace* workspace); QStringList GetRegisteredTypes() const; diff --git a/src/translations/Dyt_zh_CN.ts b/src/translations/Dyt_zh_CN.ts index 9a5fbf47..bb0a14dd 100644 --- a/src/translations/Dyt_zh_CN.ts +++ b/src/translations/Dyt_zh_CN.ts @@ -2082,12 +2082,12 @@ OsgWidget - + warning - + open dyt file failed @@ -2188,26 +2188,6 @@ Preset Models - - - Ships - - - - - Satellites - - - - - Missiles - - - - - Jammers - - PropertyBrowser diff --git a/src/ui/ModelBrowser/PresetModelListWidget.cpp b/src/ui/ModelBrowser/PresetModelListWidget.cpp index c0cabf0b..c79632ca 100644 --- a/src/ui/ModelBrowser/PresetModelListWidget.cpp +++ b/src/ui/ModelBrowser/PresetModelListWidget.cpp @@ -131,6 +131,8 @@ void PresetModelListWidget::setModelList(const QVector& models) { painter.drawText(pixmap.rect(), Qt::AlignCenter, "?"); item->setIcon(QIcon(pixmap)); } + + item->setToolTip(modelInfo.description.isEmpty() ? modelInfo.name : modelInfo.description); // Store model name as simple string item->setData(Qt::UserRole, modelInfo.name); diff --git a/src/ui/ModelBrowser/PresetModelListWidget.h b/src/ui/ModelBrowser/PresetModelListWidget.h index 65c46e78..96d30d32 100644 --- a/src/ui/ModelBrowser/PresetModelListWidget.h +++ b/src/ui/ModelBrowser/PresetModelListWidget.h @@ -1,3 +1,5 @@ +#pragma once + #include #include diff --git a/src/ui/ModelBrowser/PresetModelPanel.cpp b/src/ui/ModelBrowser/PresetModelPanel.cpp index c9f7cccf..2fd26676 100644 --- a/src/ui/ModelBrowser/PresetModelPanel.cpp +++ b/src/ui/ModelBrowser/PresetModelPanel.cpp @@ -14,11 +14,13 @@ #include #include #include +#include #include "common/RecourceHelper.h" #include "common/SpdLogger.h" #include "ui/DockTitleBar.h" #include "ui/DockWidget.h" +#include "workspace/PresetModelConfigParser.h" #include "ui_PresetModelPanel.h" @@ -34,6 +36,7 @@ PresetModelPanel::PresetModelPanel(QWidget *parent) PresetModelPanel::~PresetModelPanel() { + ClearDynamicUI(); delete ui; } @@ -57,80 +60,97 @@ void PresetModelPanel::InitUI() QString presetsPath = QString("%1/presets").arg(RecourceHelper::Get().GetResourcesPath()); if (PresetModelConfigParser::Get().LoadAllConfigs(presetsPath)) { // Successfully loaded from configuration files + CreateDynamicUI(); LoadModelsFromConfig(); } else { // Failed to load configuration files, use hardcoded defaults as fallback LOG_WARN("Failed to load preset models from config files, using hardcoded defaults. Error: {}", PresetModelConfigParser::Get().GetLastError().toStdString()); - LoadDefaultModels(); + } +} + +void PresetModelPanel::CreateDynamicUI() +{ + // Clear any existing dynamic UI + ClearDynamicUI(); + + // Get all category names from configuration + QStringList categoryNames = PresetModelConfigParser::Get().GetCategoryNames(); + + if (categoryNames.isEmpty()) { + LOG_WARN("No categories found in configuration"); + return; + } + + // Create UI for each category + for (const QString& categoryName : categoryNames) { + ModelCategory category = PresetModelConfigParser::Get().GetCategory(categoryName); + + // Create page widget + QWidget* page = new QWidget(); + page->setObjectName(QString("%1Page").arg(categoryName.toLower())); + + // Create layout for the page + QVBoxLayout* layout = new QVBoxLayout(page); + layout->setSpacing(0); + layout->setContentsMargins(0, 0, 0, 0); + + // Create list widget + PresetModelListWidget* listWidget = new PresetModelListWidget(page); + listWidget->setObjectName(QString("%1List").arg(categoryName.toLower())); + listWidget->setDragDropMode(QAbstractItemView::DragOnly); + listWidget->setDefaultDropAction(Qt::CopyAction); + + // Add list widget to layout + layout->addWidget(listWidget); + + // Add page to toolbox with display name + QString displayName = category.displayName.isEmpty() ? categoryName : category.displayName; + ui->PresetModelToolBox->addItem(page, displayName); + + // Store references for later use + categoryWidgets_[categoryName] = listWidget; + categoryPages_[categoryName] = page; + + LOG_INFO("Created dynamic UI for category: {}", categoryName.toStdString()); + } + + // Set first page as current if available + if (ui->PresetModelToolBox->count() > 0) { + ui->PresetModelToolBox->setCurrentIndex(0); } } void PresetModelPanel::LoadModelsFromConfig() { - // Load ship models - ModelCategory shipCategory = PresetModelConfigParser::Get().GetCategory("Ship"); - if (!shipCategory.models.isEmpty()) { - ui->shipList->setModelType("Ship"); - ui->shipList->setModelList(shipCategory.models); - } + // Load models for each dynamically created category + QStringList categoryNames = PresetModelConfigParser::Get().GetCategoryNames(); - // Load satellite models - ModelCategory satelliteCategory = PresetModelConfigParser::Get().GetCategory("Satellite"); - if (!satelliteCategory.models.isEmpty()) { - ui->satelliteList->setModelType("Satellite"); - ui->satelliteList->setModelList(satelliteCategory.models); - } - - // Load missile models - ModelCategory missileCategory = PresetModelConfigParser::Get().GetCategory("Missile"); - if (!missileCategory.models.isEmpty()) { - ui->missileList->setModelType("Missile"); - ui->missileList->setModelList(missileCategory.models); - } - - // Load jammer models - ModelCategory jammerCategory = PresetModelConfigParser::Get().GetCategory("Jammer"); - if (!jammerCategory.models.isEmpty()) { - ui->jammerList->setModelType("Jammer"); - ui->jammerList->setModelList(jammerCategory.models); + for (const QString& categoryName : categoryNames) { + ModelCategory category = PresetModelConfigParser::Get().GetCategory(categoryName); + + if (!category.models.isEmpty() && categoryWidgets_.contains(categoryName)) { + PresetModelListWidget* listWidget = categoryWidgets_[categoryName]; + listWidget->setModelType(categoryName); + listWidget->setModelList(category.models); + + LOG_INFO("Loaded {} models for category: {}", + category.models.size(), categoryName.toStdString()); + } } } -void PresetModelPanel::LoadDefaultModels() { - // Use hardcoded default models as fallback mechanism - QVector shipModels = { - {"destroyer", "Destroyer", "Naval destroyer vessel", "", ":/icons/ship.png", true}, - {"aircraft_carrier", "Aircraft Carrier", "Large naval vessel", "", ":/icons/ship.png", true}, - {"frigate", "Frigate", "Fast naval vessel", "", ":/icons/ship.png", true}, - {"submarine", "Submarine", "Underwater vessel", "", ":/icons/ship.png", true} - }; - ui->shipList->setModelType("Ship"); - ui->shipList->setModelList(shipModels); +void PresetModelPanel::ClearDynamicUI() +{ + // Clear all dynamic UI elements + categoryWidgets_.clear(); + categoryPages_.clear(); - QVector satelliteModels = { - {"comm_satellite", "Communication Satellite", "Communication satellite", "", ":/icons/satellite.png", true}, - {"weather_satellite", "Weather Satellite", "Weather monitoring satellite", "", ":/icons/satellite.png", true}, - {"gps_satellite", "GPS Satellite", "GPS navigation satellite", "", ":/icons/satellite.png", true}, - {"spy_satellite", "Spy Satellite", "Reconnaissance satellite", "", ":/icons/satellite.png", true} - }; - ui->satelliteList->setModelType("Satellite"); - ui->satelliteList->setModelList(satelliteModels); - - QVector missileModels = { - {"cruise_missile", "Cruise Missile", "Long-range cruise missile", "", ":/icons/missile.png", true}, - {"ballistic_missile", "Ballistic Missile", "Ballistic missile", "", ":/icons/missile.png", true}, - {"anti_ship_missile", "Anti-Ship Missile", "Anti-ship missile", "", ":/icons/missile.png", true}, - {"sam_missile", "Surface-to-Air Missile", "Surface-to-air missile", "", ":/icons/missile.png", true} - }; - ui->missileList->setModelType("Missile"); - ui->missileList->setModelList(missileModels); - - QVector jammerModels = { - {"electronic_jammer", "Electronic Jammer", "Electronic warfare jammer", "", ":/icons/jammer.png", true}, - {"comm_jammer", "Communication Jammer", "Communication jammer", "", ":/icons/jammer.png", true}, - {"radar_jammer", "Radar Jammer", "Radar jamming device", "", ":/icons/jammer.png", true}, - {"gps_jammer", "GPS Jammer", "GPS jamming device", "", ":/icons/jammer.png", true} - }; - ui->jammerList->setModelType("Jammer"); - ui->jammerList->setModelList(jammerModels); + // Remove all pages from toolbox + while (ui->PresetModelToolBox->count() > 0) { + QWidget* widget = ui->PresetModelToolBox->widget(0); + ui->PresetModelToolBox->removeItem(0); + if (widget) { + widget->deleteLater(); + } + } } diff --git a/src/ui/ModelBrowser/PresetModelPanel.h b/src/ui/ModelBrowser/PresetModelPanel.h index 6d220217..bd0e24d5 100644 --- a/src/ui/ModelBrowser/PresetModelPanel.h +++ b/src/ui/ModelBrowser/PresetModelPanel.h @@ -14,9 +14,10 @@ #include #include #include +#include #include "workspace/ModelInfo.h" -#include "workspace/PresetModelConfigParser.h" +#include "ui/ModelBrowser/PresetModelListWidget.h" namespace Ui { class PresetModelPanel; } @@ -26,11 +27,8 @@ class DockWidget; /** * @brief Preset model panel class * - * Provides preset 3D models for users to drag into the 3D scene, including: - * - Ships: Destroyer, Aircraft Carrier - * - Satellites: Geostationary Satellites - * - Missiles: Hypersonic Missiles - * - Jammers: Passive Jammers, Active Jammers + * Provides preset 3D models for users to drag into the 3D scene. + * Dynamically creates UI based on configuration files, supporting any number of categories. */ class PresetModelPanel : public QWidget { Q_OBJECT @@ -51,19 +49,28 @@ private: */ void InitUI(); + /** + * @brief Create dynamic UI based on configuration + */ + void CreateDynamicUI(); + /** * @brief Load models from configuration file */ void LoadModelsFromConfig(); /** - * @brief Load default models when config is unavailable + * @brief Clear all dynamic UI elements */ - void LoadDefaultModels(); + void ClearDynamicUI(); private: Ui::PresetModelPanel *ui; + // Dynamic UI management + QMap categoryWidgets_; + QMap categoryPages_; + // Drag and drop support QPoint dragStartPosition_; QListWidget* dragSourceWidget_; diff --git a/src/ui/ModelBrowser/PresetModelPanel.ui b/src/ui/ModelBrowser/PresetModelPanel.ui index 60d64214..8206ddf1 100644 --- a/src/ui/ModelBrowser/PresetModelPanel.ui +++ b/src/ui/ModelBrowser/PresetModelPanel.ui @@ -32,168 +32,8 @@ - 0 + -1 - - - - 0 - 0 - 290 - 470 - - - - Ships - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - QAbstractItemView::DragOnly - - - Qt::CopyAction - - - - - - - - - 0 - 0 - 290 - 470 - - - - Satellites - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - QAbstractItemView::DragOnly - - - Qt::CopyAction - - - - - - - - - 0 - 0 - 98 - 73 - - - - Missiles - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - QAbstractItemView::DragOnly - - - Qt::CopyAction - - - - - - - - - 0 - 0 - 98 - 73 - - - - Jammers - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - QAbstractItemView::DragOnly - - - Qt::CopyAction - - - - -
diff --git a/src/viewer/OsgWidget.cpp b/src/viewer/OsgWidget.cpp index 0a28707a..43f540c5 100644 --- a/src/viewer/OsgWidget.cpp +++ b/src/viewer/OsgWidget.cpp @@ -28,6 +28,7 @@ #include "workspace/PresetModelConfigParser.h" #include "entities/EntitiesManager.h" +#include "entities/EntityFactory.h" #include "entities/ComponentFactory.h" #include "entities/Entity.h" #include "entities/Component.h" @@ -271,7 +272,14 @@ void OsgWidget::OnPresetModelDropped(const QString& modelType, const QString& mo height = 0.0; } - Entity* entity = EntitiesManager::Get().CreateEntityWithComponents(modelType, currentWorkSpace); + bool success = false; + ModelInfo modelInfo = PresetModelConfigParser::Get().GetModelInfo(modelType, modelName, &success); + if (!success) { + LOG_ERROR("OsgWidget::OnPresetModelDropped - Failed to get model info of type:{} name:{}", modelType.toStdString(), modelName.toStdString()); + return; + } + + Entity* entity = EntitiesManager::Get().CreateEntityWithComponents(modelType, modelInfo.meshFile, modelInfo.useLabel, currentWorkSpace); if (nullptr == entity) { LOG_ERROR("OsgWidget::OnPresetModelDropped - Failed to create entity of type: {}", modelType.toStdString()); return; diff --git a/src/viewer/OsgWidget.h b/src/viewer/OsgWidget.h index e8cfc972..bb973ae4 100644 --- a/src/viewer/OsgWidget.h +++ b/src/viewer/OsgWidget.h @@ -1,4 +1,4 @@ -#pragma once +#pragma once #include #include diff --git a/src/workspace/ModelInfo.h b/src/workspace/ModelInfo.h index e6582e44..bf8ea4b8 100644 --- a/src/workspace/ModelInfo.h +++ b/src/workspace/ModelInfo.h @@ -7,7 +7,7 @@ * @brief Model information structure * * Contains all information for a single preset model, including name, display name, - * description, mesh file path, icon path, and enabled status. + * description, mesh file path, icon path, enabled status, and entity registration info. */ struct ModelInfo { QString name; ///< Model name (unique identifier) @@ -15,21 +15,24 @@ struct ModelInfo { QString description; ///< Model description QString meshFile; ///< 3D mesh file path QString icon; ///< Icon file path + bool useLabel; bool enabled; ///< Whether this model is enabled /** * @brief Default constructor * Model is enabled by default */ - ModelInfo() : enabled(true) {} + ModelInfo() : useLabel(true), enabled(true) {} /** * @brief Constructor with parameters */ ModelInfo(const QString& name, const QString& displayName, const QString& description, - const QString& meshFile, const QString& icon, bool enabled = true) + const QString& meshFile, const QString& icon, bool enabled = true, + const QString& entityType = "", const QString& entityClass = "", + const QStringList& requiredComponents = QStringList()) : name(name), displayName(displayName), description(description), - meshFile(meshFile), icon(icon), enabled(enabled) {} + meshFile(meshFile), icon(icon), useLabel(true), enabled(enabled) {} }; /** diff --git a/src/workspace/PresetModelConfigParser.cpp b/src/workspace/PresetModelConfigParser.cpp index c0fd4428..67f27ac0 100644 --- a/src/workspace/PresetModelConfigParser.cpp +++ b/src/workspace/PresetModelConfigParser.cpp @@ -105,8 +105,14 @@ bool PresetModelConfigParser::ParseXmlFile(const QString& filePath) const char* description = modelElement->Attribute("description"); const char* meshFile = modelElement->Attribute("meshFile"); const char* modelIcon = modelElement->Attribute("icon"); + const char* label = modelElement->Attribute("label"); const char* enabled = modelElement->Attribute("enabled"); + // Entity registration attributes + const char* entityType = modelElement->Attribute("entityType"); + const char* entityClass = modelElement->Attribute("entityClass"); + const char* requiredComponents = modelElement->Attribute("requiredComponents"); + if (!modelName) { LOG_WARN("Model element missing name attribute in file: {}", filePath.toStdString()); modelElement = modelElement->NextSiblingElement("model"); @@ -118,6 +124,7 @@ bool PresetModelConfigParser::ParseXmlFile(const QString& filePath) model.description = description ? QString::fromUtf8(description) : ""; model.meshFile = meshFile ? QString::fromUtf8(meshFile) : ""; model.icon = modelIcon ? QString::fromUtf8(modelIcon) : ""; + model.useLabel = label ? (strcmp(label, "true") == 0) : true; model.enabled = enabled ? (strcmp(enabled, "true") == 0) : true; category.models.append(model); diff --git a/workspace/presets/jammers.xml b/workspace/presets/jammers.xml index 9941ef0c..1e3ca715 100644 --- a/workspace/presets/jammers.xml +++ b/workspace/presets/jammers.xml @@ -9,5 +9,5 @@ description="Multi-frequency signal jamming equipment" meshFile="models/signal_jammer.ive" icon="icons/signal_jammer_icon.png" - enabled="true" /> + enabled="true"/> \ No newline at end of file diff --git a/workspace/presets/ships.xml b/workspace/presets/ships.xml index 8979bbbb..7acee27e 100644 --- a/workspace/presets/ships.xml +++ b/workspace/presets/ships.xml @@ -4,10 +4,10 @@ description="High-speed combat vessel with powerful anti-submarine and air defense capabilities" meshFile="models/destroyer.ive" icon="icons/destroyer_icon.png" - enabled="true" /> + enabled="true"/> + enabled="true"/> \ No newline at end of file From e3117d66876f692422002dddd455b53894d62556 Mon Sep 17 00:00:00 2001 From: brige Date: Sun, 2 Nov 2025 00:03:18 +0800 Subject: [PATCH 5/5] delete unuse files --- test_chart_parsing.cpp | 129 ----------------------------------------- test_workspace.xml | 45 -------------- 2 files changed, 174 deletions(-) delete mode 100644 test_chart_parsing.cpp delete mode 100644 test_workspace.xml diff --git a/test_chart_parsing.cpp b/test_chart_parsing.cpp deleted file mode 100644 index f67d5917..00000000 --- a/test_chart_parsing.cpp +++ /dev/null @@ -1,129 +0,0 @@ -#include -#include -#include -#include "workspace/WorkSpaceXMLParse.h" -#include "common/ChartData.h" - -void printChartData(const std::shared_ptr& chartData) { - if (!chartData) { - std::cout << " Chart data is null" << std::endl; - return; - } - - std::cout << " Chart Name: " << chartData->name.toStdString() << std::endl; - std::cout << " Chart Path: " << chartData->path.toStdString() << std::endl; - - // Handle different chart types - if (auto curveChart = std::dynamic_pointer_cast(chartData)) { - std::cout << " Chart Type: Curve" << std::endl; - std::cout << " X Title: " << curveChart->xTitle.toStdString() << std::endl; - std::cout << " Y Title: " << curveChart->yTitle.toStdString() << std::endl; - std::cout << " X Range: [" << curveChart->xMin << " - " << curveChart->xMax << "]" << std::endl; - std::cout << " Y Range: [" << curveChart->yMin << " - " << curveChart->yMax << "]" << std::endl; - std::cout << " X Count: " << curveChart->xCount << std::endl; - std::cout << " Time: " << curveChart->t << std::endl; - std::cout << " Curves (" << curveChart->curves.size() << "):" << std::endl; - - for (const auto& curve : curveChart->curves) { - std::cout << " - Name: " << curve.name.toStdString() - << ", Range: [" << curve.start << " - " << curve.stop << "]" - << ", Color: " << curve.color.toStdString() << std::endl; - } - - } else if (auto surfaceChart = std::dynamic_pointer_cast(chartData)) { - std::cout << " Chart Type: Surface" << std::endl; - std::cout << " X Title: " << surfaceChart->xTitle.toStdString() << std::endl; - std::cout << " Y Title: " << surfaceChart->yTitle.toStdString() << std::endl; - std::cout << " Z Title: " << surfaceChart->zTitle.toStdString() << std::endl; - std::cout << " X Range: [" << surfaceChart->xMin << " - " << surfaceChart->xMax << "]" << std::endl; - std::cout << " Y Range: [" << surfaceChart->yMin << " - " << surfaceChart->yMax << "]" << std::endl; - std::cout << " Z Range: [" << surfaceChart->zMin << " - " << surfaceChart->zMax << "]" << std::endl; - std::cout << " X Count: " << surfaceChart->xCount << std::endl; - std::cout << " Y Count: " << surfaceChart->yCount << std::endl; - std::cout << " Z Count: " << surfaceChart->zCount << std::endl; - std::cout << " Time: " << surfaceChart->t << std::endl; - std::cout << " Surface Curves (" << surfaceChart->curves.size() << "):" << std::endl; - - for (const auto& curve : surfaceChart->curves) { - std::cout << " - Name: " << curve.name.toStdString() - << ", Range: [" << curve.start << " - " << curve.stop << "]" - << ", Color: " << curve.color.toStdString() - << ", Position: (" << curve.x << "," << curve.y << "," << curve.z << ")" << std::endl; - } - - } else if (auto tableChart = std::dynamic_pointer_cast(chartData)) { - std::cout << " Chart Type: Table" << std::endl; - std::cout << " Head: " << tableChart->head.toStdString() << std::endl; - std::cout << " Time: " << tableChart->t << std::endl; - std::cout << " Table Data (" << tableChart->curves.size() << "):" << std::endl; - - for (const auto& curve : tableChart->curves) { - std::cout << " - Name: " << curve.name.toStdString() - << ", Color: " << curve.color.toStdString() - << ", Data: " << curve.data.toStdString() << std::endl; - } - - } else if (auto lightChart = std::dynamic_pointer_cast(chartData)) { - std::cout << " Chart Type: Light" << std::endl; - std::cout << " Open Color: " << lightChart->openColor.toStdString() << std::endl; - std::cout << " Close Color: " << lightChart->closeColor.toStdString() << std::endl; - std::cout << " Time: " << lightChart->t << std::endl; - std::cout << " Light Data (" << lightChart->curves.size() << "):" << std::endl; - - for (const auto& curve : lightChart->curves) { - std::cout << " - Name: " << curve.name.toStdString() - << ", Data: " << curve.data.toStdString() << std::endl; - } - } else { - std::cout << " Chart Type: Unknown" << std::endl; - } -} - -int main(int argc, char *argv[]) -{ - QApplication app(argc, argv); - - std::cout << "Testing XML Chart Parsing with New Inheritance Structure..." << std::endl; - - // Test XML file path - QString xmlFilePath = "test_workspace.xml"; - - // Create parser instance - WorkSpaceXMLParse parser; - - // Parse the XML file - FileTypeData fileData; - bool success = parser.ParseFiles(xmlFilePath, fileData); - - if (!success) { - std::cout << "Failed to parse XML file: " << xmlFilePath.toStdString() << std::endl; - return -1; - } - - std::cout << "Successfully parsed XML file!" << std::endl; - std::cout << "=== File Type Data ===" << std::endl; - std::cout << "Files count: " << fileData.files.size() << std::endl; - std::cout << "Charts count: " << fileData.charts.size() << std::endl; - - // Print file information - std::cout << "\n=== Files ===" << std::endl; - for (int i = 0; i < fileData.files.size(); ++i) { - const auto& file = fileData.files[i]; - std::cout << "File " << i << ":" << std::endl; - std::cout << " Name: " << file.name.toStdString() << std::endl; - std::cout << " Path: " << file.path.toStdString() << std::endl; - std::cout << " Type: " << static_cast(file.type) << std::endl; - } - - // Print chart information - std::cout << "\n=== Charts ===" << std::endl; - for (int i = 0; i < fileData.charts.size(); ++i) { - std::cout << "Chart " << i << ":" << std::endl; - printChartData(fileData.charts[i]); - std::cout << std::endl; - } - - std::cout << "Test completed successfully!" << std::endl; - - return 0; -} \ No newline at end of file diff --git a/test_workspace.xml b/test_workspace.xml deleted file mode 100644 index 4bf37e07..00000000 --- a/test_workspace.xml +++ /dev/null @@ -1,45 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file