diff --git a/CHANGELOG.md b/CHANGELOG.md index 8d8dedec..b391adba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,41 @@ If a copy of the MPL was not distributed with this file, You can obtain one at h --> +## [1.5.0] Multisampling, dynamic render order control, Python API improvements + +* **File version number has changed. Files saved with RaCo 1.5.0 cannot be opened by previous versions.** +* **Export now supports ramses-logic feature levels up to 3. Scenes exported with feature level 3 can't be opened with ramses-logic before v1.2.0.** + +### Added +* Available at feature level 3: Dynamic control of render order by making the individual `renderableTags` properties for each tag linkable. +* Python API changes + * Added `get/setTags`, `get/setMaterialFilterTags`, and `get/setRenderableTags` functions to access the `tags`, `materialFilterTags`, and `renderableTags` properties en bloc. + * The `renderableTags` property in `RenderLayer` objects is now visible from Python to allow linking it. + * Added `addExternalProject(path)` to add a project as external reference. + * Added `addExternalReferences(path, type)` to import all objects of a certain type as external references. Type can be either a single string or a list of strings to import multiple types at once. Returns the imported objects. +* Multisampling support + * Added MSAA option to Ramses preview window. + * Added RenderBufferMS usertype that handles Ramses TextureSampler2DMS objects. + * Added Material support for shader uniform type sampler2DMS. + * Added new example shaders multisampler.(frag/vert). +* Added BlitPass usertype. +* Added support for array uniforms in shaders. Only simple types but not struct are supported as array elements. + +### Changes +* Update ramses-logic from 1.1.0 to 1.3.0. +* Update ramses from 27.0.121 to 27.0.126. +* Items in the Resource View are now grouped by their type. +* If exporting in headless fails, the errors causing that failure are now always logged into the console output. +* Upgrade Feature Level menu moved down below Save As... +* TracePlayer now throws an error together with the trace line number, timestamp and the step at which error occurs. +* "Save As with new ID" added. + +### Fixes +* Removed double scrollbar in Project Settings. +* Fixed wrong Feature Level number in RCA file after upgrade. +* Fixed misleading error message sometimes appearing for empty texture uniforms. + + ## [1.4.0] Python API enhancements, various usability improvements and bugfixes ### Added diff --git a/CMakeLists.txt b/CMakeLists.txt index 45693e53..c66a2d19 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,7 +11,7 @@ cmake_minimum_required(VERSION 3.19) SET(CMAKE_CONFIGURATION_TYPES "Debug;RelWithDebInfo") -project(RaCoOS VERSION 1.4.0) +project(RaCoOS VERSION 1.5.0) SET(RACO_RELEASE_DIRECTORY ${CMAKE_BINARY_DIR}/release) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..84d42ff7 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,71 @@ +# Pull requests +All contributions must be offered as GitHub pull requests. Please read Github documentation for more info how to fork projects and create pull requests. + +# Commit guidelines + +All submitted changes have to pass our code review system. No code can be merged into the repository, without passing the review of another developer. In order to enable fast reviews, there are some requirements to be met. PRs that do not obey these rules, will not be considered during review and have no chance of being merged into the code base. We try to acknowledge that everyone has their own style, but we also try to keep things consistent across a large codebase. We strive to maintain friendly and helpful language when reviewing, give concrete suggestions how things could be done instead, in favor of just disliking a piece of code. As soon as a PR is created, it will be looked at by a reviewer. If you want to signal that it's being worked on/changed, put a WIP in the name, or add a WIP label. After a review round, address the comments of the review, and wait for the reviewer to mark them as 'resolved'. Once everything is resolved, the PR will enter the integration process. + +1) Review requirements + - The code works + - PR branch has to be based on the latest released branch HEAD version (unless the requested change is a hotfix for existing release) + - Unit tests are included - at least one test closely related to the change (even for proof-of-concept code) + - There are no code style or formatting violations (-> 5) + - Good PR description (-> 2) + +2) Meaningful PR description is required + - What does it do? Which were the considerations while implementing it, if relevant? Did you consider other options, but decided against them for a good reason? + - If reviewer does not understand the content from the description, review will be denied + - PR description matches exactly the code change + +3) Target is exactly one issue + - It changes exactly one issue in the code + - No mixing of refactoring and other work + - e.g. NOT: adds function to API and fixed memory leak + +4) Should be small + - Typically only a few lines + - Cannot be split + - As long as the reviewer can see a pattern the commit can be longer (e.g. renaming schema) + +5) Code style + - The coding style guidelines are fixed and commented in the [.clang-tidy](.clang-tidy) and [.clang-format](.clang-format) files in the repository root + - Those files will be picked up by Visual Studio and ReSharper++ (under some circumstances, the actual way how to do it seems more complicated than anticipated, see below) + - [More detailed Coding Style Guidelines in RaCO OSS](#more-detailed-coding-style-guidelines-in-raco-oss) + +6) It is self-contained + - It doesn't break anything + +7) Works for all platforms + - Make sure the unit tests succeed on the supported Windows and Linux platforms + - PRs that don't pass build servers will not be reviewed + +8) Respects licenses and external IP + - PRs must not contain any copied code + - PR content is free of external IP + - All mandatory (open source) license restrictions are satisfied + +9) Bundle commits into multiple PRs when it makes sense + - If you can split a complex feature into smaller PRs where some of them can be integrated earlier than others, do so + - When submitting multiple PRs which depend on each other, please mark their dependency by adding a line like this: "Depends-On: " where is a link to another PR which needs to go in first. + + +## More Detailed Coding Style Guidelines in RaCO OSS +### Points not checked / described by clang-tidy or clang-format +* Minimize the number of include files in a header file: + - Use forward declarations whenever possible in a few lines + - As a rule of thumb, includes should only exist for base classes and classes used by value. They are not necessary for references or pointers +* For include files, use quotes if the included file is in the same solution. Use angular brackets only for files which are not in the solution or for files which are part of an external library (= the file is in the "third-party" folder - e. g. Ramses or boost) +* For lambda functions, avoid using implicit captures with '[&]' and '[=]' + - (one) reason: implicit captures frequently can hide life-time issues with objects - e. g. a captured "this" pointer or shared_ptr binds the life-time of the lambda to the life-time of the captured pointer, and it makes potential issues more obvious if the capture is done explicitly +* Do not use "catch(...)" ever. It will capture crashes (access violation etc) in Windows, leaving the application in an unknown state. +* Only use "catch" for specific exceptions - do not do general "catch everything which happens in here" constructs + - "catch" should only be used if it is known which exceptions should be captured, and then it should be done as narrowly as possible + - In general it is better to have a crash due an uncaught exception on unexpected behaviour than an undefined state of the data (which is what happens if an unexpected exception is thrown at an unexpected place and it is unclear if the data is left in a consistent state) +### External Libraries +* In general prefer using mature and maintained external libraries to writing something yourself +* If a functionality is available in a C++ standard library, use that version rather than an external library +* Do not just pull in additional external libraries +* If you need an additional external library, first check what is already available (make sure to check what Ramses already uses in the ramses/external folder). If that is not sufficient and you know a library which would help, check with the development team if there are any objections against using a new external library. +* Make sure to check the license conditions of the external library. Virulent licenses like GPL are a no-go. +* Any third party libraries are added as submodules to the third_party folder + - Ideally use directly a repository as it is released by the third_party vendor, if that is not possible (e. g. because the code needs to be patched), create a separate repository on our Bitbucket and use that as a submodule \ No newline at end of file diff --git a/EditorApp/mainwindow.cpp b/EditorApp/mainwindow.cpp index e25d90bf..53083bfe 100644 --- a/EditorApp/mainwindow.cpp +++ b/EditorApp/mainwindow.cpp @@ -14,6 +14,7 @@ #include "OpenRecentMenu.h" #include "RaCoDockManager.h" #include "SavedLayoutsDialog.h" +#include "application/RaCoApplication.h" #include "common_widgets/ErrorView.h" #include "common_widgets/ExportDialog.h" #include "common_widgets/LogView.h" @@ -22,13 +23,13 @@ #include "common_widgets/RunScriptDialog.h" #include "common_widgets/TracePlayerWidget.h" #include "common_widgets/UndoView.h" -#include "core/Context.h" +#include "components/RaCoPreferences.h" +#include "core/BasicTypes.h" #include "core/EditorObject.h" #include "core/Handles.h" #include "core/PathManager.h" #include "core/Project.h" -#include "core/Queries.h" -#include "core/BasicTypes.h" +#include "core/ProjectMigration.h" #include "data_storage/Value.h" #include "gui_python_api/GUIPythonAPI.h" #include "log_system/log.h" @@ -39,23 +40,18 @@ #include "object_tree_view_model/ObjectTreeViewPrefabModel.h" #include "object_tree_view_model/ObjectTreeViewResourceModel.h" #include "object_tree_view_model/ObjectTreeViewSortProxyModels.h" -#include "ramses_widgets/PreviewMainWindow.h" #include "property_browser/PropertyBrowserItem.h" #include "property_browser/PropertyBrowserModel.h" #include "property_browser/PropertyBrowserWidget.h" #include "ramses_adaptor/SceneBackend.h" -#include "ramses_base/LogicEngineFormatter.h" #include "ramses_base/BaseEngineBackend.h" -#include "components/Naming.h" -#include "application/RaCoApplication.h" -#include "components/RaCoPreferences.h" -#include "core/ProjectMigration.h" -#include "core/Serialization.h" +#include "ramses_widgets/PreviewMainWindow.h" #include "ui_mainwindow.h" #include "user_types/AnchorPoint.h" #include "user_types/Animation.h" #include "user_types/AnimationChannel.h" +#include "user_types/BlitPass.h" #include "user_types/CubeMap.h" #include "user_types/LuaInterface.h" #include "user_types/LuaScript.h" @@ -67,36 +63,30 @@ #include "user_types/Prefab.h" #include "user_types/PrefabInstance.h" #include "user_types/RenderBuffer.h" +#include "user_types/RenderBufferMS.h" #include "user_types/RenderLayer.h" -#include "user_types/RenderTarget.h" #include "user_types/RenderPass.h" -#include "user_types/Timer.h" +#include "user_types/RenderTarget.h" #include "user_types/Texture.h" +#include "user_types/Timer.h" -#include "utils/FileUtils.h" -#include "versiondialog.h" #include "utils/u8path.h" +#include "versiondialog.h" -#include "python_api/PythonAPI.h" #include "DockAreaWidget.h" -#include "DockSplitter.h" #include "ads_globals.h" +#include "python_api/PythonAPI.h" #include #include -#include #include -#include -#include #include -#include #include #include #include #include #include #include -#include #include #include #include @@ -168,7 +158,7 @@ ads::CDockAreaWidget* createAndAddPropertyBrowser(MainWindow* mainWindow, const QObject::connect(mainWindow, &MainWindow::objectFocusRequestedForPropertyBrowser, propertyBrowser, &raco::property_browser::PropertyBrowserWidget::setValueHandleFromObjectId); connectPropertyBrowserAndTreeDockManager(propertyBrowser, treeDockManager); auto* dockWidget = createDockWidget(MainWindow::DockWidgetTypes::PROPERTY_BROWSER, mainWindow); - dockWidget->setWidget(propertyBrowser, ads::CDockWidget::ForceNoScrollArea); + dockWidget->setWidget(propertyBrowser); dockWidget->setObjectName(dockObjName); return dockManager->addDockWidget(ads::RightDockWidgetArea, dockWidget); } @@ -189,7 +179,11 @@ ads::CDockAreaWidget* createAndAddObjectTree(const char* title, const char* dock dockModel->buildObjectTree(); auto newTreeView = new raco::object_tree::view::ObjectTreeView(title, dockModel, sortFilterModel); if (sortFilterModel && sortFilterModel->sortingEnabled()) { - newTreeView->sortByColumn(raco::object_tree::model::ObjectTreeViewDefaultModel::COLUMNINDEX_TYPE, Qt::SortOrder::AscendingOrder); + newTreeView->sortByColumn( + title == MainWindow::DockWidgetTypes::RESOURCES + ? raco::object_tree::model::ObjectTreeViewDefaultModel::COLUMNINDEX_NAME + : raco::object_tree::model::ObjectTreeViewDefaultModel::COLUMNINDEX_TYPE, + Qt::SortOrder::AscendingOrder); } dockObjectView->setTreeView(newTreeView); treeDockManager.addTreeDock(dockObjectView); @@ -210,6 +204,7 @@ ads::CDockAreaWidget* createAndAddResourceTree(MainWindow* mainWindow, const cha static const std::vector allowedCreateableUserTypes{ AnchorPoint::typeDescription.typeName, AnimationChannel::typeDescription.typeName, + BlitPass::typeDescription.typeName, CubeMap::typeDescription.typeName, LuaScriptModule::typeDescription.typeName, Material::typeDescription.typeName, @@ -217,13 +212,14 @@ ads::CDockAreaWidget* createAndAddResourceTree(MainWindow* mainWindow, const cha Texture::typeDescription.typeName, Timer::typeDescription.typeName, RenderBuffer::typeDescription.typeName, + RenderBufferMS::typeDescription.typeName, RenderTarget::typeDescription.typeName, RenderLayer::typeDescription.typeName, RenderPass::typeDescription.typeName}; auto* model = new raco::object_tree::model::ObjectTreeViewResourceModel(racoApplication->activeRaCoProject().commandInterface(), racoApplication->dataChangeDispatcher(), racoApplication->externalProjects(), allowedCreateableUserTypes); return createAndAddObjectTree( - MainWindow::DockWidgetTypes::RESOURCES, dockObjName, model, new raco::object_tree::model::ObjectTreeViewTopLevelSortFilterProxyModel(mainWindow), + MainWindow::DockWidgetTypes::RESOURCES, dockObjName, model, new raco::object_tree::model::ObjectTreeViewResourceSortFilterProxyModel(mainWindow), ads::BottomDockWidgetArea, mainWindow, dockManager, treeDockManager, dockArea); } @@ -386,9 +382,7 @@ MainWindow::MainWindow(raco::application::RaCoApplication* racoApplication, raco }); ui->menuFile->insertMenu(ui->actionSave, recentFileMenu_); - upgradeMenu_ = new QMenu("&Upgrade Feature Level", this); - ui->menuFile->insertMenu(ui->actionSave, upgradeMenu_); - upgradeMenu_->setDisabled(true); + updateUpgradeMenu(); dockManager_ = createDockManager(this); setWindowIcon(QIcon(":applicationLogo")); @@ -419,13 +413,14 @@ MainWindow::MainWindow(raco::application::RaCoApplication* racoApplication, raco ui->actionSave->setShortcut(QKeySequence::Save); ui->actionSave->setShortcutContext(Qt::ApplicationShortcut); QObject::connect(ui->actionSave, &QAction::triggered, this, &MainWindow::saveActiveProject); - + ui->actionSaveAs->setShortcut(QKeySequence::SaveAs); ui->actionSaveAs->setShortcutContext(Qt::ApplicationShortcut); QObject::connect(ui->actionSaveAs, &QAction::triggered, this, &MainWindow::saveAsActiveProject); + + QObject::connect(ui->actionSaveAsWithNewID, &QAction::triggered, this, &MainWindow::saveAsActiveProjectWithNewID); } - QObject::connect(ui->actionOpen, &QAction::triggered, [this]() { auto file = QFileDialog::getOpenFileName(this, "Open", QString::fromStdString(raco::core::PathManager::getCachedPath(raco::core::PathManager::FolderTypeKeys::Project).string()), "Ramses Composer Assembly (*.rca);; All files (*.*)"); if (file.size() > 0) { @@ -689,18 +684,26 @@ bool MainWindow::upgradeActiveProject(int newFeatureLevel) { return false; } - if (racoApplication_->canSaveActiveProject()) { - std::string errorMsg; - if (racoApplication_->activeRaCoProject().save(errorMsg)) { - openProject(QString::fromStdString(racoApplication_->activeProjectPath()), newFeatureLevel); - return true; + const int currentFeatureLevel = racoApplication_->activeRaCoProject().project()->featureLevel(); + const auto upgradeConfirmed = QMessageBox::question(this, "Upgrade feature level", + "The scene will be upgraded from feature level " + QString::number(currentFeatureLevel) + + " to feature level " + QString::number(newFeatureLevel) + ". This can't be reverted after the scene is saved! Are you sure?"); + + if (upgradeConfirmed == QMessageBox::Yes) { + if (racoApplication_->canSaveActiveProject()) { + std::string errorMsg; + if (racoApplication_->activeRaCoProject().save(errorMsg)) { + openProject(QString::fromStdString(racoApplication_->activeProjectPath()), newFeatureLevel); + return true; + } else { + updateApplicationTitle(); + QMessageBox::critical(this, "Save Error", fmt::format("Can not save project: Writing the project file '{}' failed with error '{}'", racoApplication_->activeProjectPath(), errorMsg).c_str(), QMessageBox::Ok); + } } else { - updateApplicationTitle(); - QMessageBox::critical(this, "Save Error", fmt::format("Can not save project: Writing the project file '{}' failed with error '{}'", racoApplication_->activeProjectPath(), errorMsg).c_str(), QMessageBox::Ok); + QMessageBox::warning(this, "Save Error", fmt::format("Can not save project: externally referenced projects not clean.").c_str(), QMessageBox::Ok); } - } else { - QMessageBox::warning(this, "Save Error", fmt::format("Can not save project: externally referenced projects not clean.").c_str(), QMessageBox::Ok); } + return false; } @@ -711,6 +714,7 @@ bool MainWindow::saveActiveProject() { } else { std::string errorMsg; if (racoApplication_->activeRaCoProject().save(errorMsg)) { + updateUpgradeMenu(); return true; } else { updateApplicationTitle(); @@ -724,18 +728,20 @@ bool MainWindow::saveActiveProject() { return false; } -bool MainWindow::saveAsActiveProject() { +bool MainWindow::saveAsActiveProject(bool newID) { if (racoApplication_->canSaveActiveProject()) { - bool setProjectName = racoApplication_->activeProjectPath().empty(); - auto newPath = QFileDialog::getSaveFileName(this, "Save As...", QString::fromStdString(raco::core::PathManager::getCachedPath(raco::core::PathManager::FolderTypeKeys::Project).string()), "Ramses Composer Assembly (*.rca)"); + const bool setProjectName = racoApplication_->activeProjectPath().empty(); + const auto dialogCaption = newID ? "Save As with new ID..." : "Save As..."; + auto newPath = QFileDialog::getSaveFileName(this, dialogCaption, QString::fromStdString(raco::core::PathManager::getCachedPath(raco::core::PathManager::FolderTypeKeys::Project).string()), "Ramses Composer Assembly (*.rca)"); if (newPath.isEmpty()) { return false; } if (!newPath.endsWith(".rca")) newPath += ".rca"; std::string errorMsg; - if (racoApplication_->activeRaCoProject().saveAs(newPath, errorMsg, setProjectName)) { + if (racoApplication_->activeRaCoProject().saveAs(newPath, errorMsg, setProjectName, newID)) { updateActiveProjectConnection(); updateProjectSavedConnection(); + updateUpgradeMenu(); return true; } else { updateApplicationTitle(); @@ -747,6 +753,10 @@ bool MainWindow::saveAsActiveProject() { return false; } +bool MainWindow::saveAsActiveProjectWithNewID() { + return saveAsActiveProject(true); +} + void MainWindow::updateSavedLayoutMenu() { ui->menuSavedLayoutList->clear(); for (const auto& layoutName : dockManager_->perspectiveNames()) { @@ -761,16 +771,16 @@ void MainWindow::updateSavedLayoutMenu() { void MainWindow::updateUpgradeMenu() { auto maxFeatureLevel = static_cast(raco::ramses_base::BaseEngineBackend::maxFeatureLevel); auto currentFeatureLevel = racoApplication_->activeRaCoProject().project()->featureLevel(); - if (currentFeatureLevel < maxFeatureLevel) { - upgradeMenu_->clear(); + if ((currentFeatureLevel < maxFeatureLevel) && (!racoApplication_->activeProjectPath().empty())) { + ui->menuUpgrade->clear(); for (int fl = currentFeatureLevel + 1; fl <= maxFeatureLevel; fl++) { - upgradeMenu_->addAction(QString::fromStdString(fmt::format("&{}. Feature Level {}", fl, fl)), [fl, this]() { + ui->menuUpgrade->addAction(QString::fromStdString(fmt::format("&{}. Feature Level {}", fl, fl)), [fl, this]() { upgradeActiveProject(fl); }); } - upgradeMenu_->setDisabled(false); + ui->menuUpgrade->setDisabled(false); } else { - upgradeMenu_->setDisabled(true); + ui->menuUpgrade->setDisabled(true); } } diff --git a/EditorApp/mainwindow.h b/EditorApp/mainwindow.h index 9db07873..f210fb11 100644 --- a/EditorApp/mainwindow.h +++ b/EditorApp/mainwindow.h @@ -11,13 +11,12 @@ #include "RaCoDockManager.h" #include "common_widgets/TimingsWidget.h" +#include "common_widgets/log_model/LogViewModel.h" #include "object_tree_view/ObjectTreeDockManager.h" #include "ramses_widgets/RendererBackend.h" -#include "common_widgets/log_model/LogViewModel.h" #include #include -#include namespace Ui { class MainWindow; @@ -87,7 +86,8 @@ protected Q_SLOTS: void openProject(const QString& file = {}, int featureLevel = -1); bool saveActiveProject(); bool upgradeActiveProject(int newFeatureLevel); - bool saveAsActiveProject(); + bool saveAsActiveProject(bool newID = false); + bool saveAsActiveProjectWithNewID(); void resetDockManager(); void updateActiveProjectConnection(); void updateProjectSavedConnection(); @@ -102,7 +102,6 @@ protected Q_SLOTS: OpenRecentMenu* recentFileMenu_; RaCoDockManager* dockManager_; QListWidget* sceneObjectList_; - QMenu* upgradeMenu_; const std::vector pythonSearchPaths_; raco::ramses_widgets::RendererBackend* rendererBackend_; diff --git a/EditorApp/mainwindow.ui b/EditorApp/mainwindow.ui index 5c41ac66..ea3d4879 100644 --- a/EditorApp/mainwindow.ui +++ b/EditorApp/mainwindow.ui @@ -30,10 +30,18 @@ &File + + + &Upgrade Feature Level + + + + + @@ -122,6 +130,11 @@ Save &As... + + + Save As with new &ID... + + &Export... diff --git a/HeadlessApp/main.cpp b/HeadlessApp/main.cpp index 46951f19..f62a4c8c 100644 --- a/HeadlessApp/main.cpp +++ b/HeadlessApp/main.cpp @@ -90,7 +90,7 @@ public Q_SLOTS: std::string error; if (!app->exportProject(ramsesPath.toStdString(), logicPath.toStdString(), compressExport_, error)) { - LOG_ERROR(raco::log_system::COMMON, "error exporting to {}\n{}", error.c_str(), ramsesPath.toStdString()); + LOG_ERROR(raco::log_system::COMMON, "error exporting to {}\n{}", ramsesPath.toStdString(), error.c_str()); exitCode_ = 1; } } diff --git a/PyAPITests/pyt_general.py b/PyAPITests/pyt_general.py index 35372fb4..ef782bf1 100644 --- a/PyAPITests/pyt_general.py +++ b/PyAPITests/pyt_general.py @@ -18,6 +18,16 @@ def setUp(self): def cwd(self): return os.getcwd() + def findObjectByName(self, name): + for object in raco.instances(): + if object.objectName.value() == name: + return object + + def findObjectByType(self, type): + for object in raco.instances(): + if object.typeName() == type: + return object + def test_reset(self): raco.reset() self.assertEqual(len(raco.instances()), 1) @@ -79,6 +89,30 @@ def test_load_feature_levels(self): raco.load(self.cwd() + "/../resources/empty-fl2.rca", 2) self.assertEqual(raco.projectFeatureLevel(), 2) + def test_save_check_object_id(self): + objectInitID = self.findObjectByType("ProjectSettings").objectID() + raco.save(self.cwd() + "/project.rca") + self.assertEqual(self.findObjectByType("ProjectSettings").objectID(), objectInitID) + + def test_save_check_object_name(self): + raco.reset() + raco.save(self.cwd() + "/project.rca") + self.assertEqual("project", self.findObjectByType("ProjectSettings").objectName.value()) + raco.load(self.cwd() + "/project.rca") + self.assertEqual("project", self.findObjectByType("ProjectSettings").objectName.value()) + + def test_save_as_with_new_id(self): + objectInitID = self.findObjectByType("ProjectSettings").objectID() + raco.save(self.cwd() + "/project.rca", True) + self.assertNotEqual(self.findObjectByType("ProjectSettings").objectID(), objectInitID) + + def test_save_as_with_new_id_same_path(self): + objectInitID = self.findObjectByType("ProjectSettings").objectID() + raco.save(self.cwd() + "/project.rca", True) + objectNewID1 = self.findObjectByType("ProjectSettings").objectID() + raco.save(self.cwd() + "/project.rca", True) + objectNewID2 = self.findObjectByType("ProjectSettings").objectID() + self.assertNotEqual(objectNewID1, objectNewID2) def test_project_path(self): self.assertEqual(raco.projectPath(), "") @@ -1011,3 +1045,99 @@ def test_get_mesh_metadata(self): mesh.meshIndex = 1 self.assertEqual({'prop1': 'truck mesh property'}, mesh.metadata()) + def test_setTags(self): + node = raco.create("Node", "node") + node.setTags(['foo', 'bar']) + self.assertEqual(node.getTags(), ['foo', 'bar']) + + material = raco.create("Material", "mat") + material.setTags(['foo', 'bar']) + self.assertEqual(material.getTags(), ['foo', 'bar']) + + layer = raco.create("RenderLayer", "layer") + layer.setTags(['foo', 'bar']) + self.assertEqual(layer.getTags(), ['foo', 'bar']) + + def test_setMaterialFilterTags(self): + layer = raco.create("RenderLayer", "layer") + layer.setMaterialFilterTags(['foo', 'bar']) + self.assertEqual(layer.getMaterialFilterTags(), ['foo', 'bar']) + + def test_setRenderableTags(self): + layer = raco.create("RenderLayer", "layer") + self.assertEqual(layer.renderableTags.keys(), []) + + layer.setRenderableTags([("red", 1), ("blue", 3)]) + + self.assertEqual(layer.renderableTags.keys(), ["red", "blue"]) + self.assertEqual(layer.renderableTags.red.value(), 1) + self.assertEqual(layer.renderableTags.blue.value(), 3) + + def test_feature_level_3_render_order(self): + lua = raco.create("LuaScript", "lua") + lua.uri = self.cwd() + R"/../resources/scripts/types-scalar.lua" + + layer = raco.create("RenderLayer", "layer") + layer.setRenderableTags([("red", 1), ("blue", 3)]) + + self.assertEqual(layer.getRenderableTags(), [("red", 1), ("blue", 3)]) + self.assertEqual(layer.renderableTags.keys(), ["red", "blue"]) + self.assertEqual(layer.renderableTags.red.value(), 1) + self.assertEqual(layer.renderableTags.blue.value(), 3) + + link_order = raco.addLink(lua.outputs.ointeger, layer.renderableTags.red) + + self.assertEqual(layer.renderableTags.red.value(), 0) + lua.inputs.integer = 3 + self.assertEqual(layer.renderableTags.red.value(), 6) + self.assertEqual(layer.getRenderableTags(), [("red", 6), ("blue", 3)]) + + def test_add_external_project(self): + raco.addExternalProject(self.cwd() + R"/../resources/example_scene.rca") + + def test_add_external_project_invalid_path(self): + with self.assertRaises(ValueError): + raco.addExternalProject("thisShouldNotExist.rca") + + def test_add_external_references_empty_project(self): + with self.assertRaises(ValueError): + raco.addExternalReferences("thisShouldNotExist.rca", "Texture") + + def test_add_external_references(self): + result = raco.addExternalReferences(self.cwd() + R"/../resources/example_scene.rca", "Texture") + self.assertEqual(1, len(result)) + self.assertTrue(any(filter(lambda x: x.objectName.value() == "DuckTexture" and x.typeName() == "Texture", result))) + + instances = raco.instances() + self.assertTrue(any(filter(lambda x: x.objectName.value() == "DuckTexture" and x.typeName() == "Texture", instances))) + self.assertFalse(any(filter(lambda x: x.objectName.value() == "DuckMaterial" and x.typeName() == "Material", instances))) + self.assertFalse(any(filter(lambda x: x.objectName.value() == "DuckMesh" and x.typeName() == "Mesh", instances))) + + def test_add_external_references_should_also_add_dependencies(self): + raco.addExternalReferences(self.cwd() + R"/../resources/example_scene.rca", "Material") + instances = raco.instances() + self.assertTrue(any(filter(lambda x: x.objectName.value() == "DuckTexture" and x.typeName() == "Texture", instances))) + self.assertTrue(any(filter(lambda x: x.objectName.value() == "DuckMaterial" and x.typeName() == "Material", instances))) + self.assertFalse(any(filter(lambda x: x.objectName.value() == "DuckMesh" and x.typeName() == "Mesh", instances))) + + def test_add_external_references_multiple_times_different_types_should_work(self): + raco.addExternalReferences(self.cwd() + R"/../resources/example_scene.rca", "Texture") + raco.addExternalReferences(self.cwd() + R"/../resources/example_scene.rca", "Mesh") + instances = raco.instances() + self.assertTrue(any(filter(lambda x: x.objectName.value() == "DuckTexture" and x.typeName() == "Texture", instances))) + self.assertFalse(any(filter(lambda x: x.objectName.value() == "DuckMaterial" and x.typeName() == "Material", instances))) + self.assertTrue(any(filter(lambda x: x.objectName.value() == "DuckMesh" and x.typeName() == "Mesh", instances))) + + def test_add_external_references_multiple_types_should_work(self): + result = raco.addExternalReferences(self.cwd() + R"/../resources/example_scene.rca", ["Texture", "Mesh"]) + self.assertEqual(2, len(result)) + + instances = raco.instances() + self.assertTrue(any(filter(lambda x: x.objectName.value() == "DuckTexture" and x.typeName() == "Texture", instances))) + self.assertFalse(any(filter(lambda x: x.objectName.value() == "DuckMaterial" and x.typeName() == "Material", instances))) + self.assertTrue(any(filter(lambda x: x.objectName.value() == "DuckMesh" and x.typeName() == "Mesh", instances))) + + def test_add_external_references_multiple_times_same_types_should_work(self): + raco.addExternalReferences(self.cwd() + R"/../resources/example_scene.rca", "Texture") + second_result = raco.addExternalReferences(self.cwd() + R"/../resources/example_scene.rca", "Texture") + self.assertEqual(0, len(second_result)) diff --git a/PyAPITests/use_pip.py b/PyAPITests/use_pip.py index 681e13ae..775151ad 100644 --- a/PyAPITests/use_pip.py +++ b/PyAPITests/use_pip.py @@ -22,7 +22,7 @@ def setUp(self): if platform.system() == "Windows": self._tested_module_path = '../build/release/bin/python-3.8.10-with-pip/Lib/site-packages/simplejson/__init__.py' elif platform.system() == "Linux": - self._tested_module_path = '../build/release/bin/python-3.8.10-with-pip/python3.8/lib/python3.8/site-packages/simplejson/__init__.py' + self._tested_module_path = '../build/release/bin/python-3.8.0-with-pip/python3.8/lib/python3.8/site-packages/simplejson/__init__.py' else: raise RuntimeError('Unknown platform') diff --git a/README.md b/README.md index e789294e..d30737a1 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,7 @@ set the environment variable ```RACO_QT_BASE``` to it. \raco\build> cmake --build . --target RaCoEditor --config # either Release or Debug ``` -Ramses Composer is built on Windows 10 with Visual Studio 2019 and on Ubuntu 18.04 with gcc 7.5.0. +Ramses Composer is built on Windows 10 with Visual Studio 2019 and on Ubuntu 18.04 with gcc 8.4.0. ## Starting Ramses Composer @@ -106,7 +106,7 @@ same environment as the user. ## Linux (Ubuntu 18.04) -The linux build needs newer versions of CMake and Qt than available from the Ubuntu 18.04 repositories. Install CMake >= 3.19 and Qt 5.15.2. +The linux build needs newer versions of CMake and Qt than available from the Ubuntu 18.04 repositories. Install CMake >= 3.19 and Qt 5.15.2. It also needs gcc/g++ >=8 which is available from the Ubuntu 18.04 repositories. Installing Qt into /usr/local/opt/Qt/5.15.2 can be done like this: ```console diff --git a/components/libApplication/include/application/ExternalProjectsStore.h b/components/libApplication/include/application/ExternalProjectsStore.h index 2bb15eed..505c5938 100644 --- a/components/libApplication/include/application/ExternalProjectsStore.h +++ b/components/libApplication/include/application/ExternalProjectsStore.h @@ -37,7 +37,7 @@ class ExternalProjectsStore : public raco::core::ExternalProjectsStoreInterface bool isCurrent(const std::string& projectPath) const; - // @return true if loaded successfully + // @return project if loaded successfully raco::core::Project* addExternalProject(const std::string& projectPath, core::LoadContext& loadContext) override; void removeExternalProject(const std::string& projectPath) override; bool canRemoveExternalProject(const std::string& projectPath) const override; diff --git a/components/libApplication/include/application/RaCoProject.h b/components/libApplication/include/application/RaCoProject.h index e998bf1c..b7d26af6 100644 --- a/components/libApplication/include/application/RaCoProject.h +++ b/components/libApplication/include/application/RaCoProject.h @@ -79,7 +79,7 @@ class RaCoProject : public QObject { bool dirty() const noexcept; bool save(std::string &outError); - bool saveAs(const QString& fileName, std::string& outError, bool setProjectName = false); + bool saveAs(const QString& fileName, std::string& outError, bool setProjectName = false, bool setNewID = false); // @exception ExtrefError void updateExternalReferences(core::LoadContext& loadContext); @@ -96,13 +96,15 @@ class RaCoProject : public QObject { QJsonDocument serializeProject(const std::unordered_map>& currentVersions); void applyDefaultCachedPaths(); - void subscribeDefaultCachedPathChanges(const raco::components::SDataChangeDispatcher& dataChangeDispatcher); - + void setupCachedPathSubscriptions(const raco::components::SDataChangeDispatcher& dataChangeDispatcher); + Q_SIGNALS: void activeProjectFileChanged(); void projectSuccessfullySaved(); private: + void subscribeDefaultCachedPathChanges(const raco::components::SDataChangeDispatcher& dataChangeDispatcher); + // @exception ExtrefError RaCoProject(const QString& file, raco::core::Project& p, raco::core::EngineInterface* engineInterface, const raco::core::UndoStack::Callback& callback, raco::core::ExternalProjectsStoreInterface* externalProjectsStore, RaCoApplication* app, core::LoadContext& loadContext); @@ -118,6 +120,7 @@ class RaCoProject : public QObject { raco::core::Errors errors_; raco::core::Project project_; + raco::components::Subscription lifecycleSubscription_; raco::components::Subscription imageSubdirectoryUpdateSubscription_; raco::components::Subscription meshSubdirectoryUpdateSubscription_; raco::components::Subscription scriptSubdirectoryUpdateSubscription_; diff --git a/components/libApplication/src/RaCoApplication.cpp b/components/libApplication/src/RaCoApplication.cpp index 2c3c6c69..a480f1e8 100644 --- a/components/libApplication/src/RaCoApplication.cpp +++ b/components/libApplication/src/RaCoApplication.cpp @@ -142,7 +142,7 @@ void RaCoApplication::switchActiveRaCoProject(const QString& file, std::function logicEngineNeedsUpdate_ = true; activeProject_->applyDefaultCachedPaths(); - activeProject_->subscribeDefaultCachedPathChanges(dataChangeDispatcher_); + activeProject_->setupCachedPathSubscriptions(dataChangeDispatcher_); setupScene(false); startTime_ = std::chrono::high_resolution_clock::now(); @@ -159,20 +159,12 @@ core::ErrorLevel RaCoApplication::getExportSceneDescriptionAndStatus(std::vector outDescription = scenesBackend_->getSceneItemDescriptions(); - core::ErrorLevel errorLevel = core::ErrorLevel::NONE; - std::string message; - if (!scenesBackend_->sceneValid()) { - message = sceneBackend()->getValidationReport(core::ErrorLevel::ERROR); - if (!message.empty()) { - errorLevel = core::ErrorLevel::ERROR; - } else { - message = sceneBackend()->getValidationReport(core::ErrorLevel::WARNING); - if (!message.empty()) { - errorLevel = core::ErrorLevel::WARNING; - } - } + core::ErrorLevel errorLevel = scenesBackend_->sceneValid(); + if (errorLevel != core::ErrorLevel::NONE) { + outMessage = sceneBackend()->getValidationReport(errorLevel); + } else { + outMessage = std::string(); } - outMessage = message; setupScene(false); logicEngineNeedsUpdate_ = true; @@ -201,11 +193,18 @@ bool RaCoApplication::exportProjectImpl(const std::string& ramsesExport, const s if (!forceExportWithErrors) { if (activeRaCoProject().errors()->hasError(raco::core::ErrorLevel::ERROR)) { - outError = "Export failed: scene contains Composer errors"; + outError = "Export failed: scene contains Composer errors:\n"; + auto errors = activeRaCoProject().errors()->getAllErrors(); + for (const auto& item : errors) { + auto error = item.second; + if (error.level() >= raco::core::ErrorLevel::ERROR) { + outError.append(raco::core::Errors::formatError(error)); + } + } return false; } - if (!sceneBackend()->sceneValid()) { - outError = "Export failed: scene contains Ramses errors"; + if (sceneBackend()->sceneValid() != core::ErrorLevel::NONE) { + outError = "Export failed: scene contains Ramses errors:\n" + sceneBackend()->getValidationReport(core::ErrorLevel::WARNING); return false; } } @@ -215,7 +214,9 @@ bool RaCoApplication::exportProjectImpl(const std::string& ramsesExport, const s return false; } rlogic::SaveFileConfig metadata; - metadata.setValidationEnabled(!forceExportWithErrors); + // Since the SceneBackend filters some validation warnings we have to disable validation when saving, because + // we can't selectively disable some validation warnings here: + metadata.setValidationEnabled(false); // Use JSON format for the metadata string to allow future extensibility // CAREFUL: only include data here which we are certain all users agree to have included in the exported files. metadata.setMetadataString(fmt::format( diff --git a/components/libApplication/src/RaCoProject.cpp b/components/libApplication/src/RaCoProject.cpp index 76c4e776..bdfe4d5e 100644 --- a/components/libApplication/src/RaCoProject.cpp +++ b/components/libApplication/src/RaCoProject.cpp @@ -129,7 +129,7 @@ void RaCoProject::onAfterProjectPathChange(const std::string& oldPath, const std // Mechanism for losing the module references: // - reroot / set LuaModule uri property // - call LuaScript::onAfterReferencedObjectChanged as side effect of operation above - // - LuaScript module sync will now remove the module properties because the lua file can't be loaded because + // - LuaScript module sync will now remove the module properties because the lua file can't be loaded because // the LuaScript uri property has not been rerooted yet but is resolved using the new project path. // - reroot / set LuaScript uri property // restores the module properties again but the reference values are now lost (not anymore, caching works again) @@ -138,7 +138,7 @@ void RaCoProject::onAfterProjectPathChange(const std::string& oldPath, const std // - set all uri properties before calling the side effect handlers; problematic because we would be opening the // BaseContext::set function and reordering the steps it takes. might be necessary though. // update: we now have module caching in the LuaScript working again; - // but: we still potentially call some callback handlers multiple times; + // but: we still potentially call some callback handlers multiple times; // it would be nice the optimize this but that needs an extension of the second solution above. auto instances{project_.instances()}; for (auto& object : instances) { @@ -189,6 +189,16 @@ void RaCoProject::applyDefaultCachedPaths() { PathManager::setCachedPath(PathManager::FolderTypeKeys::Shader, raco::utils::u8path(defaultFolders->shaderSubdirectory_.asString()).normalizedAbsolutePath(projectFolder)); } +void RaCoProject::setupCachedPathSubscriptions(const raco::components::SDataChangeDispatcher& dataChangeDispatcher) { + lifecycleSubscription_ = dataChangeDispatcher->registerOnObjectsLifeCycle([this, dataChangeDispatcher](SEditorObject obj) { + if (obj->isType()) { + subscribeDefaultCachedPathChanges(dataChangeDispatcher); + } + }, + [](auto) {}); + subscribeDefaultCachedPathChanges(dataChangeDispatcher); +} + void RaCoProject::subscribeDefaultCachedPathChanges(const raco::components::SDataChangeDispatcher& dataChangeDispatcher) { auto project = this->project(); auto settings = project_.settings(); @@ -275,7 +285,7 @@ std::unique_ptr RaCoProject::createNew(RaCoApplication* app, bool c result->context_->set({sRenderPass, &user_types::RenderPass::camera_}, sCamera); result->context_->set({sRenderPass, &user_types::RenderPass::layer0_}, sRenderLayer); - result->context_->addProperty({sRenderLayer, &user_types::RenderLayer::renderableTags_}, "render_main", std::make_unique>(0)); + result->context_->addProperty({sRenderLayer, &user_types::RenderLayer::renderableTags_}, "render_main", std::make_unique>(0, LinkEndAnnotation(3))); result->context_->set({sNode, &user_types::Node::tags_}, std::vector({"render_main"})); result->context_->set({sCamera, &user_types::Node::translation_, &Vec3f::z}, 10.0); @@ -391,7 +401,7 @@ std::unique_ptr RaCoProject::loadFromFile(const QString& filename, } } - // Selectively log errors: + // Selectively log errors: // - we need to log errors when loading external projects here // - we don't need to log errors here when called from RaCoApplication::switchActiveRacoProject; see comment there if (logErrors) { @@ -435,7 +445,7 @@ QJsonDocument RaCoProject::serializeProjectData(const std::unordered_map>& currentVersions) { // On discarding objects during save // - we discard all objects which can not be recreated by the external reference and/or prefab update during load. - // + // // The rules in detail // - local PrefabInstance children are discarded // - except the modifiable lua interface scripts @@ -456,7 +466,7 @@ QJsonDocument RaCoProject::serializeProject(const std::unordered_map bool { - return (PrefabOperations::findContainingPrefabInstance(object->getParent()) && Queries::isReadOnly(object)) || - object->query(); + return (PrefabOperations::findContainingPrefabInstance(object->getParent()) && Queries::isReadOnly(object)) || + object->query(); }); // Iteratively remove objects from the discardable set until we achieved a stable discardable set @@ -547,9 +557,9 @@ QJsonDocument RaCoProject::serializeProject(const std::unordered_mapas()) { if (!object->referencesToThis().empty()) { @@ -677,16 +687,38 @@ bool RaCoProject::save(std::string& outError) { return true; } -bool RaCoProject::saveAs(const QString& fileName, std::string& outError, bool setProjectName) { +bool RaCoProject::saveAs(const QString& fileName, std::string& outError, bool setProjectName, bool setNewID) { auto oldPath = project_.currentPath(); auto oldProjectFolder = project_.currentFolder(); QString absPath = QFileInfo(fileName).absoluteFilePath(); auto newPath = absPath.toStdString(); - if (newPath == oldPath) { + if ((newPath == oldPath) && (!setNewID)) { return save(outError); } else { - project_.setCurrentPath(newPath); - onAfterProjectPathChange(oldProjectFolder, project_.currentFolder()); + // Note on the undo stack: + // both the creation of a new ProjectSettings object and the project name change will be recorded in the change recorder + // by the context operations, but no undo stack entry will be created. + // But we need an undo stack entry since the save below wil performa a restore from the undo stack. + // However the onAfterProjectPathChange will perform an unconditional undo stack push. + // This means that using the context in the operations below is OK as long as we perform the onAfterProjectPathChange afterwards. + if (setNewID) { + // We can't change the id of an object so we need some trickery here: + // Create a new ProjectSettings object, copy the properties from the old one (except objectID) and then delete the old object + auto newProjectSettingsObject = context_->createObject(ProjectSettings::typeDescription.typeName, project_.settings()->objectName()); + auto translateRef = [this](SEditorObject srcObj) -> SEditorObject { + return srcObj; + }; + DataChangeRecorder localChanges; + UndoHelpers::updateEditorObject( + project_.settings().get(), newProjectSettingsObject, translateRef, + [](const std::string& propName) { + return propName == "objectID"; + }, + *context_->objectFactory(), + &localChanges, true, false); + + context_->deleteObjects(std::vector{project_.settings()}); + } if (setProjectName) { auto projName = raco::utils::u8path(newPath).stem().string(); auto settings = project_.settings(); @@ -694,6 +726,10 @@ bool RaCoProject::saveAs(const QString& fileName, std::string& outError, bool se context_->set({settings, &raco::core::EditorObject::objectName_}, projName); } } + project_.setCurrentPath(newPath); + // Note: this will perform an undo stack push + onAfterProjectPathChange(oldProjectFolder, project_.currentFolder()); + // Note: save will perform a restore from the undo stack as part of the file save optimization if (save(outError)) { undoStack_.reset(); dirty_ = false; @@ -701,7 +737,6 @@ bool RaCoProject::saveAs(const QString& fileName, std::string& outError, bool se } else { return false; } - } } @@ -721,7 +756,7 @@ Errors* RaCoProject::errors() { return &errors_; } -const Errors* RaCoProject::errors() const{ +const Errors* RaCoProject::errors() const { return &errors_; } diff --git a/components/libApplication/tests/RaCoProject_test.cpp b/components/libApplication/tests/RaCoProject_test.cpp index af0fddba..0b007006 100644 --- a/components/libApplication/tests/RaCoProject_test.cpp +++ b/components/libApplication/tests/RaCoProject_test.cpp @@ -416,6 +416,43 @@ TEST_F(RaCoProjectFixture, saveAsToDifferentDriveSetsRelativeURIsToAbsolute) { } #endif +TEST_F(RaCoProjectFixture, saveAsWithNewID) { + RaCoApplication app{backend}; + std::string msg; + const auto objectInitID = app.activeRaCoProject().project()->settings()->objectID(); + ASSERT_TRUE(app.activeRaCoProject().saveAs((test_path() / "project.rca").string().c_str(), msg, false, true)); + const auto objectNewID = app.activeRaCoProject().project()->settings()->objectID(); + ASSERT_NE(objectInitID, objectNewID); + + raco::core::LoadContext loadContext; + auto project = raco::application::RaCoProject::loadFromFile(QString::fromUtf8((test_path() / "project.rca").string().data()), &app, loadContext); + ASSERT_EQ(project->project()->settings()->objectID(), objectNewID); +} + +TEST_F(RaCoProjectFixture, saveAsWithNewIDSamePath) { + RaCoApplication app{backend}; + std::string msg; + const auto objectInitID = app.activeRaCoProject().project()->settings()->objectID(); + ASSERT_TRUE(app.activeRaCoProject().saveAs((test_path() / "project.rca").string().c_str(), msg, false, true)); + const auto objectNewID1 = app.activeRaCoProject().project()->settings()->objectID(); + ASSERT_NE(objectNewID1, objectInitID); + ASSERT_TRUE(app.activeRaCoProject().saveAs((test_path() / "project.rca").string().c_str(), msg, false, true)); + const auto objectNewID2 = app.activeRaCoProject().project()->settings()->objectID(); + ASSERT_NE(objectNewID1, objectNewID2); +} + +TEST_F(RaCoProjectFixture, saveAsSetProjectName) { + RaCoApplication app{backend}; + std::string msg; + ASSERT_TRUE(app.activeRaCoProject().project()->settings()->objectName().empty()); + ASSERT_TRUE(app.activeRaCoProject().saveAs((test_path() / "project.rca").string().c_str(), msg, true, false)); + ASSERT_EQ("project", app.activeRaCoProject().project()->settings()->objectName()); + + raco::core::LoadContext loadContext; + auto project = raco::application::RaCoProject::loadFromFile(QString::fromUtf8((test_path() / "project.rca").string().data()), &app, loadContext); + ASSERT_EQ("project", project->project()->settings()->objectName()); +} + TEST_F(RaCoProjectFixture, idChange) { RaCoApplication app{backend}; app.activeRaCoProject().project()->setCurrentPath((test_path() / "project.rca").string()); @@ -527,6 +564,70 @@ TEST_F(RaCoProjectFixture, launchApplicationWithResourceSubFoldersCachedPathsAre ASSERT_EQ(raco::core::PathManager::getCachedPath(raco::core::PathManager::FolderTypeKeys::Shader), newProjectFolder + "/" + shaderSubdirectory); } +TEST_F(RaCoProjectFixture, saveAs_set_path_updates_cached_path) { + std::string imageSubdirectory = u8"images abc"; + std::string meshSubdirectory = u8"meshes def"; + std::string scriptSubdirectory = u8"shared"; + std::string interfaceSubdirectory = u8"shared"; + std::string shaderSubdirectory = u8"shared"; + + using namespace raco::user_types; + + RaCoApplication app{backend}; + std::string msg; + ASSERT_TRUE(app.activeRaCoProject().saveAs((test_path() / "project.rca").string().c_str(), msg, false, false)); + + const auto& settings = app.activeRaCoProject().project()->settings(); + const auto& commandInterface = app.activeRaCoProject().commandInterface(); + + commandInterface->set({settings, &ProjectSettings::defaultResourceDirectories_, &ProjectSettings::DefaultResourceDirectories::imageSubdirectory_}, imageSubdirectory); + commandInterface->set({settings, &ProjectSettings::defaultResourceDirectories_, &ProjectSettings::DefaultResourceDirectories::meshSubdirectory_}, meshSubdirectory); + commandInterface->set({settings, &ProjectSettings::defaultResourceDirectories_, &ProjectSettings::DefaultResourceDirectories::scriptSubdirectory_}, scriptSubdirectory); + commandInterface->set({settings, &ProjectSettings::defaultResourceDirectories_, &ProjectSettings::DefaultResourceDirectories::interfaceSubdirectory_}, interfaceSubdirectory); + commandInterface->set({settings, &ProjectSettings::defaultResourceDirectories_, &ProjectSettings::DefaultResourceDirectories::shaderSubdirectory_}, shaderSubdirectory); + app.doOneLoop(); + + std::string newProjectFolder = app.activeRaCoProject().project()->currentFolder(); + + ASSERT_EQ(raco::core::PathManager::getCachedPath(raco::core::PathManager::FolderTypeKeys::Mesh), newProjectFolder + "/" + meshSubdirectory); + ASSERT_EQ(raco::core::PathManager::getCachedPath(raco::core::PathManager::FolderTypeKeys::Script), newProjectFolder + "/" + scriptSubdirectory); + ASSERT_EQ(raco::core::PathManager::getCachedPath(raco::core::PathManager::FolderTypeKeys::Interface), newProjectFolder + "/" + interfaceSubdirectory); + ASSERT_EQ(raco::core::PathManager::getCachedPath(raco::core::PathManager::FolderTypeKeys::Image), newProjectFolder + "/" + imageSubdirectory); + ASSERT_EQ(raco::core::PathManager::getCachedPath(raco::core::PathManager::FolderTypeKeys::Shader), newProjectFolder + "/" + shaderSubdirectory); +} + +TEST_F(RaCoProjectFixture, saveAs_with_new_id_set_path_updates_cached_path) { + std::string imageSubdirectory = u8"images abc"; + std::string meshSubdirectory = u8"meshes def"; + std::string scriptSubdirectory = u8"shared"; + std::string interfaceSubdirectory = u8"shared"; + std::string shaderSubdirectory = u8"shared"; + + using namespace raco::user_types; + + RaCoApplication app{backend}; + std::string msg; + ASSERT_TRUE(app.activeRaCoProject().saveAs((test_path() / "project.rca").string().c_str(), msg, false, true)); + + const auto& settings = app.activeRaCoProject().project()->settings(); + const auto& commandInterface = app.activeRaCoProject().commandInterface(); + + commandInterface->set({settings, &ProjectSettings::defaultResourceDirectories_, &ProjectSettings::DefaultResourceDirectories::imageSubdirectory_}, imageSubdirectory); + commandInterface->set({settings, &ProjectSettings::defaultResourceDirectories_, &ProjectSettings::DefaultResourceDirectories::meshSubdirectory_}, meshSubdirectory); + commandInterface->set({settings, &ProjectSettings::defaultResourceDirectories_, &ProjectSettings::DefaultResourceDirectories::scriptSubdirectory_}, scriptSubdirectory); + commandInterface->set({settings, &ProjectSettings::defaultResourceDirectories_, &ProjectSettings::DefaultResourceDirectories::interfaceSubdirectory_}, interfaceSubdirectory); + commandInterface->set({settings, &ProjectSettings::defaultResourceDirectories_, &ProjectSettings::DefaultResourceDirectories::shaderSubdirectory_}, shaderSubdirectory); + app.doOneLoop(); + + std::string newProjectFolder = app.activeRaCoProject().project()->currentFolder(); + + ASSERT_EQ(raco::core::PathManager::getCachedPath(raco::core::PathManager::FolderTypeKeys::Mesh), newProjectFolder + "/" + meshSubdirectory); + ASSERT_EQ(raco::core::PathManager::getCachedPath(raco::core::PathManager::FolderTypeKeys::Script), newProjectFolder + "/" + scriptSubdirectory); + ASSERT_EQ(raco::core::PathManager::getCachedPath(raco::core::PathManager::FolderTypeKeys::Interface), newProjectFolder + "/" + interfaceSubdirectory); + ASSERT_EQ(raco::core::PathManager::getCachedPath(raco::core::PathManager::FolderTypeKeys::Image), newProjectFolder + "/" + imageSubdirectory); + ASSERT_EQ(raco::core::PathManager::getCachedPath(raco::core::PathManager::FolderTypeKeys::Shader), newProjectFolder + "/" + shaderSubdirectory); +} + TEST_F(RaCoProjectFixture, saveAsNewProjectGeneratesResourceSubFolders) { auto newProjectFolder = (test_path() / "newProject").string(); std::filesystem::create_directory(newProjectFolder); diff --git a/components/libComponents/include/components/TracePlayer.h b/components/libComponents/include/components/TracePlayer.h index b570811b..99c50058 100644 --- a/components/libComponents/include/components/TracePlayer.h +++ b/components/libComponents/include/components/TracePlayer.h @@ -11,10 +11,12 @@ #include #include +#include #include #include #include #include +#include class QJsonArray; class QJsonObject; @@ -99,13 +101,15 @@ class TracePlayer { bool isLastFrame() const; timeInMilliSeconds getNextTs(); void setState(PlayerState newState); - void addError(const std::string& msg, core::ErrorLevel level); + void addError(const std::string& msg, core::ErrorLevel level, bool callLogChange = true); void lockLua(); void makeFramesConsistent(); - QJsonValue deepAddMissingProperties(const QJsonValue& qjPrev, const QJsonValue& qjCurr, std::vector& propertyPath); + QJsonValue deepAddMissingProperties(const QJsonValue& qjPrev, const QJsonValue& qjCurr, std::vector& propertyPath, int index); QJsonValue buildFullFrameFromLua(std::unordered_set const& sceneLuaList); QJsonValue deepCopyFromLua(raco::core::ValueHandle const& luaValHandle); void rebuildFrameSceneData(int index, const QJsonValue& qjPrev, const QJsonValue& qjCurr); + void readLinesForNextFrame(); + int getPropertyLineNumber(const QString& propertyKey); class CodeControlledObjectExtension; std::unique_ptr racoCoreInterface_; @@ -125,6 +129,9 @@ class TracePlayer { bool looping_{false}; std::string filePath_{}; std::vector framesTsList_{}; + int keyLineNumber_{0}; + QTextStream textStream_; + std::forward_list> traceFileLines_; }; } // namespace raco::components diff --git a/components/libComponents/src/TracePlayer.cpp b/components/libComponents/src/TracePlayer.cpp index 4ab1f13b..0f6a5db4 100644 --- a/components/libComponents/src/TracePlayer.cpp +++ b/components/libComponents/src/TracePlayer.cpp @@ -163,9 +163,15 @@ QJsonArray const* const TracePlayer::loadTrace(const std::string& fileName) { return nullptr; } + /// prepare text stream to extract line numbers + const QByteArray jsonByteArray = qTraceFile.readAll(); + QString jsonString(jsonByteArray); + textStream_.setString(&jsonString); + keyLineNumber_ = 0; + /// parse trace QFile QJsonParseError qjParseError{}; - const auto qjDocument{QJsonDocument::fromJson(qTraceFile.readAll(), &qjParseError)}; + const auto qjDocument{QJsonDocument::fromJson(jsonByteArray, &qjParseError)}; qTraceFile.close(); /// validate trace QJsonDocument format @@ -258,7 +264,7 @@ void TracePlayer::qjParseErrMsg(const QJsonParseError& qjParseError, const std:: addError(errorMsg, core::ErrorLevel::ERROR); } -QJsonValue TracePlayer::deepAddMissingProperties(const QJsonValue& qjPrev, const QJsonValue& qjCurrent, std::vector& propertyPath) { +QJsonValue TracePlayer::deepAddMissingProperties(const QJsonValue& qjPrev, const QJsonValue& qjCurrent, std::vector& propertyPath, int index) { if (qjCurrent.type() == QJsonValue::Object) { auto qjCurrObj{qjCurrent.toObject()}; const auto qjPrevObj{qjPrev.toObject()}; @@ -269,7 +275,7 @@ QJsonValue TracePlayer::deepAddMissingProperties(const QJsonValue& qjPrev, const const auto qjCurrItr{qjCurrObj.find(propName)}; propertyPath.push_back(propName.toStdString()); if (qjCurrItr != qjCurrObj.end()) { - qjCurrObj[qjCurrItr.key()] = deepAddMissingProperties(qjPrevItr.value(), qjCurrItr.value(), propertyPath); + qjCurrObj[qjCurrItr.key()] = deepAddMissingProperties(qjPrevItr.value(), qjCurrItr.value(), propertyPath, index); } else { qjCurrObj.insert(propName, qjPrevItr.value()); } @@ -281,12 +287,14 @@ QJsonValue TracePlayer::deepAddMissingProperties(const QJsonValue& qjPrev, const while (qjCurrItr != qjCurrObj.end()) { if (qjPrevObj.find(qjCurrItr.key()) == qjPrevObj.constEnd()) { const auto propPathStream{streamKeysChain(propertyPath)}; - if (propertyPath.size() == 0) { - addError("Lua is not available in the scene! Lua and its properties are disregarded from trace ( luaObjName: " + propPathStream + qjCurrItr.key().toStdString() + " )", core::ErrorLevel::WARNING); + const auto timestamp = parseTimestamp(parseTracePlayerData(parseFrame(index))); + const int lineNumber = getPropertyLineNumber(qjCurrItr.key()); + if (propertyPath.empty()) { + addError("Step " + std::to_string(index) + ", Timestamp " + std::to_string(timestamp) + ", Trace line " + std::to_string(lineNumber) + ": Lua is not available in the scene! Lua and its properties are disregarded from trace ( luaObjName: " + propPathStream + qjCurrItr.key().toStdString() + " )", core::ErrorLevel::WARNING, false); } else if (!qjCurrItr->isUndefined() && !qjCurrItr->isNull()) { - addError("Lua property was not found in the scene! Property is disregarded from trace ( propName: " + propPathStream + "->" + qjCurrItr.key().toStdString() + " )", core::ErrorLevel::WARNING); + addError("Step " + std::to_string(index) + ", Timestamp " + std::to_string(timestamp) + ", Trace line " + std::to_string(lineNumber) + ": Lua property was not found in the scene! Property is disregarded from trace ( propName: " + propPathStream + "->" + qjCurrItr.key().toStdString() + " )", core::ErrorLevel::WARNING, false); } else { - addError("Unexpected JSON type! ( propName: " + qjCurrItr.key().toStdString() + " )", core::ErrorLevel::ERROR); + addError("Step " + std::to_string(index) + ", Timestamp " + std::to_string(timestamp) + ", Trace line " + std::to_string(lineNumber) + ": Unexpected JSON type! ( propName: " + qjCurrItr.key().toStdString() + " )", core::ErrorLevel::ERROR, false); } qjCurrItr = qjCurrObj.erase(qjCurrItr); } else { @@ -320,15 +328,51 @@ void TracePlayer::makeFramesConsistent() { for (int frameIndex{1}; frameIndex < getTraceLen(); ++frameIndex) { rebuildFrameSceneData(frameIndex, parseSceneData(parseFrame(frameIndex - 1)), parseSceneData(parseFrame(frameIndex))); } + + if (onLogChange_) { + onLogChange_(logReport_, highestCriticality_); + } + traceFileLines_.clear(); } void TracePlayer::rebuildFrameSceneData(int index, const QJsonValue& qjPrev, const QJsonValue& qjCurr) { auto qjFrameObj{parseFrame(index)}; std::vector propertyPath; - qjFrameObj["SceneData"] = deepAddMissingProperties(qjPrev, qjCurr, propertyPath); + readLinesForNextFrame(); + qjFrameObj["SceneData"] = deepAddMissingProperties(qjPrev, qjCurr, propertyPath, index); (*qjRoot_)[index] = qjFrameObj; } +void TracePlayer::readLinesForNextFrame() { + auto line = textStream_.readLine(); + + traceFileLines_.push_front({0, line}); + keyLineNumber_++; + + while (!textStream_.atEnd()) { + line = textStream_.readLine(); + keyLineNumber_++; + traceFileLines_.push_front({keyLineNumber_, line}); + if (line.contains("TracePlayerData")) { + break; + } + } +} + +int TracePlayer::getPropertyLineNumber(const QString& propertyKey) { + int propertyLine = -1; + auto itToErase = traceFileLines_.before_begin(); + + for (auto it = traceFileLines_.begin(); it != traceFileLines_.end(); ++it, ++itToErase) { + if (it->second.contains("\"" + propertyKey + "\"")) { + propertyLine = it->first; + traceFileLines_.erase_after(itToErase); + break; + } + } + return propertyLine; +} + QJsonValue TracePlayer::deepCopyFromLua(raco::core::ValueHandle const& luaValHandle) { switch (luaValHandle.type()) { case data_storage::PrimitiveType::Struct: @@ -482,6 +526,7 @@ QJsonObject TracePlayer::parseFrame(int frameIndex) { addError("Frame entry was not found! ( frameNr: " + std::to_string(frameIndex) + " )", core::ErrorLevel::ERROR); return QJsonObject(); } + const QJsonObject qjFrame{qjFrameVal.toObject()}; if (qjFrame.isEmpty()) { addError("Invalid frame >> Empty frame! ( frameNr: " + std::to_string(frameIndex) + " )", core::ErrorLevel::ERROR); @@ -711,7 +756,7 @@ void TracePlayer::updateLuaProperty(const core::SEditorObject& lua, const std::v } } -void TracePlayer::addError(const std::string& msg, core::ErrorLevel level) { +void TracePlayer::addError(const std::string& msg, core::ErrorLevel level, bool callLogChange) { if (tracePlayerLog_.insert(std::make_pair(msg, level)).second) { const std::string errLvl = "[" + std::string(CriticalToString(level)) + "] "; logReport_.push_back(errLvl + msg); @@ -733,7 +778,7 @@ void TracePlayer::addError(const std::string& msg, core::ErrorLevel level) { if (highestCriticality_ < level) { highestCriticality_ = level; } - if (onLogChange_) { + if (onLogChange_ && callLogChange) { onLogChange_(logReport_, highestCriticality_); } } diff --git a/components/libComponents/tests/TracePlayer_test.cpp b/components/libComponents/tests/TracePlayer_test.cpp index 56084206..a25b1000 100644 --- a/components/libComponents/tests/TracePlayer_test.cpp +++ b/components/libComponents/tests/TracePlayer_test.cpp @@ -1034,10 +1034,11 @@ TEST_F(TracePlayerTest, TF104_Disregarded_Properties) { { loadTrace("raco_traces/g05_demo_withExtras.rctrace"); const auto& log{player_->getLog()}; - ASSERT_EQ(log.size(), 3); - ASSERT_NE(log.find("Lua is not available in the scene! Lua and its properties are disregarded from trace ( luaObjName: ExtraLuaScript )"), log.cend()); - ASSERT_NE(log.find("Lua property was not found in the scene! Property is disregarded from trace ( propName: SceneControls->Extra_Struct )"), log.cend()); - ASSERT_NE(log.find("Lua property was not found in the scene! Property is disregarded from trace ( propName: SceneControls->extra_property )"), log.cend()); + ASSERT_EQ(log.size(), 4); + ASSERT_NE(log.find("Step 0, Timestamp 1000, Trace line 12: Lua property was not found in the scene! Property is disregarded from trace ( propName: SceneControls->extra_property )"), log.cend()); + ASSERT_NE(log.find("Step 4, Timestamp 1400, Trace line 55: Lua is not available in the scene! Lua and its properties are disregarded from trace ( luaObjName: ExtraLuaScript )"), log.cend()); + ASSERT_NE(log.find("Step 21, Timestamp 3100, Trace line 240: Lua property was not found in the scene! Property is disregarded from trace ( propName: SceneControls->extra_property )"), log.cend()); + ASSERT_NE(log.find("Step 22, Timestamp 3200, Trace line 251: Lua property was not found in the scene! Property is disregarded from trace ( propName: SceneControls->Extra_Struct )"), log.cend()); isStopped(); } diff --git a/components/libPythonAPI/src/PythonAPI.cpp b/components/libPythonAPI/src/PythonAPI.cpp index 6de7f483..d8591e76 100644 --- a/components/libPythonAPI/src/PythonAPI.cpp +++ b/components/libPythonAPI/src/PythonAPI.cpp @@ -29,10 +29,12 @@ #include "core/ExternalReferenceAnnotation.h" #include "core/PrefabOperations.h" + +#include "user_types/Enumerations.h" #include "user_types/Mesh.h" #include "user_types/Prefab.h" #include "user_types/PrefabInstance.h" -#include "user_types/Enumerations.h" +#include "user_types/RenderLayer.h" #include #include @@ -121,6 +123,14 @@ void checkObject(raco::core::SEditorObject object) { } } +template +void checkTypedObject(raco::core::SEditorObject object) { + checkObject(object); + if (!object->isType()) { + throw std::runtime_error(fmt::format("Object '{}' is not of expected type '{}'", object->objectName(), UserType::typeDescription.typeName)); + } +} + void checkProperty(const raco::core::PropertyDescriptor& desc) { checkObject(desc.object()); raco::core::ValueHandle handle{desc}; @@ -132,6 +142,17 @@ void checkProperty(const raco::core::PropertyDescriptor& desc) { } } +void checkHiddenProperty(const raco::core::PropertyDescriptor& desc) { + checkObject(desc.object()); + raco::core::ValueHandle handle{desc}; + if (!handle) { + throw std::runtime_error(fmt::format("Property '{}' doesn't exist in object '{}'", desc.getPropertyPath(), desc.object()->objectName())); + } + if (auto anno = handle.query(); anno && *anno->featureLevel_ > app->activeRaCoProject().project()->featureLevel()) { + throw std::runtime_error(fmt::format("Property {} inaccessible at feature level {}", handle.getPropertyPath(), app->activeRaCoProject().project()->featureLevel())); + } +} + template void set_scalar_value_typed(const raco::core::ValueHandle& handle, py::object value, std::string_view typeName) { try { @@ -192,9 +213,7 @@ void python_load_project(std::string& path, int featureLevel) { void python_import_gltf(const std::string path, const raco::core::SEditorObject parent) { auto fileLocation = raco::utils::u8path(path); - if (!fileLocation.is_absolute()) { - fileLocation = fileLocation.normalizedAbsolutePath(raco::core::PathManager::getCachedPath(raco::core::PathManager::FolderTypeKeys::Mesh, app->activeProjectFolder())); - } + fileLocation = fileLocation.normalizedAbsolutePath(raco::core::PathManager::getCachedPath(raco::core::PathManager::FolderTypeKeys::Mesh, app->activeProjectFolder())); raco::core::MeshDescriptor meshDesc; meshDesc.absPath = fileLocation.string(); @@ -212,6 +231,42 @@ void python_import_gltf(const std::string path, const raco::core::SEditorObject app->doOneLoop(); } +raco::core::Project* python_add_external_project(const std::string& path) { + auto projectFileLocation = raco::utils::u8path(path); + projectFileLocation = projectFileLocation.normalizedAbsolutePath(app->activeProjectFolder()); + + raco::core::LoadContext loadContext; + loadContext.featureLevel = app->activeRaCoProject().project()->featureLevel(); + loadContext.pathStack.emplace_back(app->activeRaCoProject().project()->currentPath()); + auto* project = app->externalProjects()->addExternalProject(projectFileLocation.string(), loadContext); + if (project == nullptr) { + throw std::invalid_argument("Unable to add external project from " + projectFileLocation.string()); + } + + return project; +} + +py::object python_add_external_references(const std::string& path, const std::vector& types) { + auto* project = python_add_external_project(path); + if (project == nullptr) { + return py::cast(std::vector()); + } + std::vector editorObjects; + for (const auto& type : types) { + for (const auto& item : project->instances()) { + if (item->getTypeDescription().typeName == type) { + editorObjects.push_back(item); + } + } + } + + auto* projectInterface = app->externalProjects()->getExternalProjectCommandInterface(project->currentPath()); + auto value = projectInterface->copyObjects(editorObjects, false); + auto result = app->activeRaCoProject().commandInterface()->pasteObjects(value, nullptr, true); + + return py::cast(result); +} + py::object getPropertyChildProperties(const raco::core::PropertyDescriptor& desc) { checkProperty(desc); auto handle = raco::core::ValueHandle(desc); @@ -238,6 +293,30 @@ py::object getObjectChildProperties(raco::core::SEditorObject obj) { return py::cast(propNames); } +std::vector getTags(raco::core::SEditorObject obj, std::string tagPropertyName) { + raco::core::PropertyDescriptor desc(obj, {tagPropertyName}); + checkHiddenProperty(desc); + raco::core::ValueHandle handle(desc); + if (handle.query()) { + return handle.constValueRef()->asTable().asVector(); + } else { + throw std::runtime_error(fmt::format("Property '{}' is not a tag container.", desc.getPropertyPath())); + } + return std::vector(); +} + +void setTags(raco::core::SEditorObject obj, std::vector tags, std::string tagPropertyName) { + raco::core::PropertyDescriptor desc(obj, {tagPropertyName}); + checkHiddenProperty(desc); + raco::core::ValueHandle handle(desc); + if (handle.query()) { + app->activeRaCoProject().commandInterface()->setTags(handle, tags); + app->doOneLoop(); + } else { + throw std::runtime_error(fmt::format("Property '{}' is not a tag container.", desc.getPropertyPath())); + } +} + } // namespace PYBIND11_EMBEDDED_MODULE(raco_py_io, m) { @@ -422,6 +501,17 @@ PYBIND11_EMBEDDED_MODULE(raco, m) { } }); + m.def("save", [](std::string path, bool setNewID) { + if (app->canSaveActiveProject()) { + std::string errorMsg; + if (!app->activeRaCoProject().saveAs(QString::fromStdString(path), errorMsg, app->activeProjectPath().empty(), setNewID)) { + throw std::runtime_error(fmt::format("Saving project to '{}' failed with error '{}'.", path, errorMsg)); + } + } else { + throw std::runtime_error(fmt::format("Can not save project: externally referenced projects not clean.")); + } + }); + m.def("projectPath", []() { return app->activeProjectPath(); }); @@ -463,7 +553,19 @@ PYBIND11_EMBEDDED_MODULE(raco, m) { m.def("importGLTF", [](const std::string path, const raco::core::SEditorObject parent) { checkObject(parent); python_import_gltf(path, parent); - }); + }); + + m.def("addExternalProject", [](const std::string& path) { + python_add_external_project(path); + }); + + m.def("addExternalReferences", [](const std::string& path, const std::vector types) -> py::object { + return python_add_external_references(path, types); + }); + + m.def("addExternalReferences", [](const std::string& path, const std::string& type) -> py::object { + return python_add_external_references(path, {type}); + }); py::class_(m, "PropertyDescriptor") .def("__repr__", [](const raco::core::PropertyDescriptor& desc) { @@ -607,6 +709,35 @@ PYBIND11_EMBEDDED_MODULE(raco, m) { } } return py::cast(nullptr); + }) + .def("getTags", [](raco::core::SEditorObject obj) -> std::vector { + return getTags(obj, "tags"); + }) + .def("setTags", [](raco::core::SEditorObject obj, std::vector tags) { + setTags(obj, tags, "tags"); + }) + .def("getMaterialFilterTags", [](raco::core::SEditorObject obj) -> std::vector { + return getTags(obj, "materialFilterTags"); + }) + .def("setMaterialFilterTags", [](raco::core::SEditorObject obj, std::vector tags) { + setTags(obj, tags, "materialFilterTags"); + }) + .def("getRenderableTags", [](raco::core::SEditorObject obj) -> std::vector> { + checkTypedObject(obj); + raco::core::ValueHandle handle(obj, &raco::user_types::RenderLayer::renderableTags_); + std::vector> renderables; + for (size_t index = 0; index < handle.size(); index++) { + renderables.emplace_back(handle[index].getPropName(), handle[index].asInt()); + } + return renderables; + }) + .def("setRenderableTags", [](raco::core::SEditorObject obj, std::vector> renderables) { + checkTypedObject(obj); + raco::core::ValueHandle handle(obj, &raco::user_types::RenderLayer::renderableTags_); + if (handle) { + app->activeRaCoProject().commandInterface()->setRenderableTags(handle, renderables); + app->doOneLoop(); + } }); py::class_(m, "LinkDescriptor") @@ -761,9 +892,9 @@ bool preparePythonEnvironment(std::wstring argv0, const std::vector editorObject, ramses::Camera* ramsesCamera, rlogic::RamsesCameraBinding* cameraBinding); + static void sync(std::shared_ptr editorObject, ramses::Camera* ramsesCamera, rlogic::RamsesCameraBinding* cameraBinding, core::Errors* errors); static const rlogic::Property* getProperty(rlogic::RamsesCameraBinding* cameraBinding, const std::vector& propertyNamesVector); }; diff --git a/components/libRamsesBase/include/ramses_adaptor/BlitPassAdaptor.h b/components/libRamsesBase/include/ramses_adaptor/BlitPassAdaptor.h new file mode 100644 index 00000000..be949ab9 --- /dev/null +++ b/components/libRamsesBase/include/ramses_adaptor/BlitPassAdaptor.h @@ -0,0 +1,36 @@ +/* + * SPDX-License-Identifier: MPL-2.0 + * + * This file is part of Ramses Composer + * (see https://github.com/bmwcarit/ramses-composer). + * + * This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. + * If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +#pragma once + +#include "components/DataChangeDispatcher.h" +#include "ramses_adaptor/ObjectAdaptor.h" +#include "ramses_base/RamsesHandles.h" +#include "user_types/BlitPass.h" + +namespace raco::ramses_adaptor { + +class SceneAdaptor; + +class BlitPassAdaptor final : public UserTypeObjectAdaptor { +public: + BlitPassAdaptor(SceneAdaptor* sceneAdaptor, user_types::SBlitPass blitPass); + + bool sync(core::Errors* errors) override; + std::vector getExportInformation() const override; + +private: + std::array subscriptions_; + + raco::ramses_base::RamsesBlitPass blitPass_; + bool recreate_; + bool update_; +}; + +}; // namespace raco::ramses_adaptor diff --git a/components/libRamsesBase/include/ramses_adaptor/RenderBufferMSAdaptor.h b/components/libRamsesBase/include/ramses_adaptor/RenderBufferMSAdaptor.h new file mode 100644 index 00000000..61f04b7c --- /dev/null +++ b/components/libRamsesBase/include/ramses_adaptor/RenderBufferMSAdaptor.h @@ -0,0 +1,34 @@ +/* + * SPDX-License-Identifier: MPL-2.0 + * + * This file is part of Ramses Composer + * (see https://github.com/bmwcarit/ramses-composer). + * + * This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. + * If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +#pragma once + +#include "ramses_adaptor/ObjectAdaptor.h" +#include "user_types/RenderBufferMS.h" +#include +#include + +namespace raco::ramses_adaptor { + + class RenderBufferMSAdaptor : public TypedObjectAdaptor { + public: + explicit RenderBufferMSAdaptor(SceneAdaptor* sceneAdaptor, std::shared_ptr editorObject); + + bool sync(core::Errors* errors) override; + std::vector getExportInformation() const override; + + ramses_base::RamsesRenderBuffer buffer() const; + + private: + ramses_base::RamsesRenderBuffer buffer_; + + std::array subscriptions_; + }; + +}; \ No newline at end of file diff --git a/components/libRamsesBase/include/ramses_adaptor/RenderLayerAdaptor.h b/components/libRamsesBase/include/ramses_adaptor/RenderLayerAdaptor.h index 9bf227ea..b539f28a 100644 --- a/components/libRamsesBase/include/ramses_adaptor/RenderLayerAdaptor.h +++ b/components/libRamsesBase/include/ramses_adaptor/RenderLayerAdaptor.h @@ -16,17 +16,23 @@ namespace raco::ramses_adaptor { -class RenderLayerAdaptor : public TypedObjectAdaptor { +class RenderLayerAdaptor : public TypedObjectAdaptor, public ILogicPropertyProvider { public: explicit RenderLayerAdaptor(SceneAdaptor* sceneAdaptor, std::shared_ptr editorObject); bool sync(core::Errors* errors) override; std::vector getExportInformation() const override; + void getLogicNodes(std::vector& logicNodes) const override; + const rlogic::Property* getProperty(const std::vector& propertyNamesVector) override; + void onRuntimeError(core::Errors& errors, std::string const& message, core::ErrorLevel level) override; + private: void buildRenderGroup(core::Errors* errors); - void buildRenderableOrder(core::Errors* errors, std::vector& objs, const std::string& tag, bool parentActive, const std::set& materialFilterTags, bool materialFilterExclusive, int32_t& orderIndex, bool sceneGraphOrder); - void addNestedLayers(core::Errors* errors, const std::vector& layers, const std::string& tag, int32_t orderIndex, bool sceneGraphOrder); + void buildRenderableOrder(core::Errors* errors, ramses_base::RamsesRenderGroup container, std::vector& objs, const std::string& tag, bool parentActive, const std::set& materialFilterTags, bool materialFilterExclusive, int32_t& orderIndex, bool sceneGraphOrder); + void addNestedLayers(core::Errors* errors, ramses_base::RamsesRenderGroup container, const std::vector& layers, const std::string& tag, int32_t orderIndex, bool sceneGraphOrder); + + raco::ramses_base::RamsesRenderGroupBinding binding_; std::array subscriptions_; }; diff --git a/components/libRamsesBase/include/ramses_adaptor/RenderTargetAdaptor.h b/components/libRamsesBase/include/ramses_adaptor/RenderTargetAdaptor.h index ecec57f1..f05ed43a 100644 --- a/components/libRamsesBase/include/ramses_adaptor/RenderTargetAdaptor.h +++ b/components/libRamsesBase/include/ramses_adaptor/RenderTargetAdaptor.h @@ -23,7 +23,10 @@ class RenderTargetAdaptor : public TypedObjectAdaptor getExportInformation() const override; private: - std::array subscriptions_; + template + bool collectBuffers(std::vector& buffers, const std::initializer_list& userTypeBuffers, ramses::RenderTargetDescription& rtDesc, core::Errors* errors); + + std::array subscriptions_; }; }; // namespace raco::ramses_adaptor diff --git a/components/libRamsesBase/include/ramses_adaptor/SceneBackend.h b/components/libRamsesBase/include/ramses_adaptor/SceneBackend.h index b2ff0a3d..1503aa91 100644 --- a/components/libRamsesBase/include/ramses_adaptor/SceneBackend.h +++ b/components/libRamsesBase/include/ramses_adaptor/SceneBackend.h @@ -47,14 +47,21 @@ class SceneBackend : public raco::core::SceneBackendInterface { SceneAdaptor* sceneAdaptor() const { return scene_.get(); } - - bool sceneValid() const override; + + core::ErrorLevel sceneValid() const override; std::string getValidationReport(core::ErrorLevel minLevel) const override; uint64_t currentSceneIdValue() const override; std::vector getSceneItemDescriptions() const override; std::string getExportedObjectNames(SEditorObject editorObject) const override; private: + /** + * @brief call LogicEngine validate() and filter out warnings that RamsesComposer is + * deliberately ignoring. + */ + std::vector logicEngineFilteredValidation() const; + std::string ramsesFilteredValidationReport(core::ErrorLevel minLevel) const; + ramses::RamsesClient* client() const; ramses_base::LogicEngine* logicEngine() const; diff --git a/components/libRamsesBase/include/ramses_adaptor/utilities.h b/components/libRamsesBase/include/ramses_adaptor/utilities.h index ed27b7c1..453c896b 100644 --- a/components/libRamsesBase/include/ramses_adaptor/utilities.h +++ b/components/libRamsesBase/include/ramses_adaptor/utilities.h @@ -19,6 +19,7 @@ #include "data_storage/Value.h" #include "ramses_adaptor/BuildOptions.h" #include "ramses_base/RamsesHandles.h" +#include "user_types/EngineTypeAnnotation.h" #include "user_types/Material.h" #include "user_types/Node.h" #include "utils/MathUtils.h" @@ -320,36 +321,123 @@ inline void getOutputFromEngine(const rlogic::Property& property, const core::Va } } +template +std::vector flattenUniformArrayOfVector(const core::ValueHandle& handle, int numComponents) { + std::vector values; + for (size_t index = 0; index < handle.size(); index++) { + for (size_t component = 0; component < numComponents; component++) { + values.emplace_back(handle[index][component].as()); + } + } + return values; +} + inline void setUniform(ramses::Appearance* appearance, const core::ValueHandle& valueHandle) { using core::PrimitiveType; ramses::UniformInput input; appearance->getEffect().findUniformInput(valueHandle.getPropName().c_str(), input); - switch (valueHandle.type()) { - case PrimitiveType::Double: + auto engineTypeAnno = valueHandle.query(); + switch (engineTypeAnno->type()) { + case core::EnginePrimitive::Double: appearance->setInputValueFloat(input, valueHandle.as()); break; - case PrimitiveType::Int: + case core::EnginePrimitive::Int32: + case core::EnginePrimitive::UInt16: + case core::EnginePrimitive::UInt32: appearance->setInputValueInt32(input, static_cast(valueHandle.as())); break; - case PrimitiveType::Struct: { - auto typeDesc = &valueHandle.constValueRef()->asStruct().getTypeDescription(); - if (typeDesc == &core::Vec2f::typeDescription) { - appearance->setInputValueVector2f(input, valueHandle[0].as(), valueHandle[1].as()); - } else if (typeDesc == &core::Vec3f::typeDescription) { - appearance->setInputValueVector3f(input, valueHandle[0].as(), valueHandle[1].as(), valueHandle[2].as()); - } else if (typeDesc == &core::Vec4f::typeDescription) { - appearance->setInputValueVector4f(input, valueHandle[0].as(), valueHandle[1].as(), valueHandle[2].as(), valueHandle[3].as()); - } else if (typeDesc == &core::Vec2i::typeDescription) { - appearance->setInputValueVector2i(input, static_cast(valueHandle[0].as()), static_cast(valueHandle[1].as())); - } else if (typeDesc == &core::Vec3i::typeDescription) { - appearance->setInputValueVector3i(input, static_cast(valueHandle[0].as()), static_cast(valueHandle[1].as()), static_cast(valueHandle[2].as())); - } else if (typeDesc == &core::Vec4i::typeDescription) { - appearance->setInputValueVector4i(input, static_cast(valueHandle[0].as()), static_cast(valueHandle[1].as()), static_cast(valueHandle[2].as()), static_cast(valueHandle[3].as())); - } + case core::EnginePrimitive::Vec2f: + appearance->setInputValueVector2f(input, valueHandle[0].as(), valueHandle[1].as()); + break; + + case core::EnginePrimitive::Vec3f: + appearance->setInputValueVector3f(input, valueHandle[0].as(), valueHandle[1].as(), valueHandle[2].as()); break; + + case core::EnginePrimitive::Vec4f: + appearance->setInputValueVector4f(input, valueHandle[0].as(), valueHandle[1].as(), valueHandle[2].as(), valueHandle[3].as()); + break; + + case core::EnginePrimitive::Vec2i: + appearance->setInputValueVector2i(input, static_cast(valueHandle[0].as()), static_cast(valueHandle[1].as())); + break; + + case core::EnginePrimitive::Vec3i: + appearance->setInputValueVector3i(input, static_cast(valueHandle[0].as()), static_cast(valueHandle[1].as()), static_cast(valueHandle[2].as())); + break; + + case core::EnginePrimitive::Vec4i: + appearance->setInputValueVector4i(input, static_cast(valueHandle[0].as()), static_cast(valueHandle[1].as()), static_cast(valueHandle[2].as()), static_cast(valueHandle[3].as())); + break; + + case core::EnginePrimitive::Array: { + assert(valueHandle.size() >= 1); + + auto elementAnno = valueHandle[0].query(); + switch (elementAnno->type()) { + case core::EnginePrimitive::Double: { + std::vector values; + for (size_t index = 0; index < valueHandle.size(); index++) { + values.emplace_back(valueHandle[index].as()); + } + appearance->setInputValueFloat(input, values.size(), values.data()); + break; + } + + case core::EnginePrimitive::Int32: + case core::EnginePrimitive::UInt16: + case core::EnginePrimitive::UInt32: { + std::vector values; + for (size_t index = 0; index < valueHandle.size(); index++) { + values.emplace_back(valueHandle[index].as()); + } + appearance->setInputValueInt32(input, values.size(), values.data()); + break; + } + + case core::EnginePrimitive::Vec2f: { + auto values{flattenUniformArrayOfVector(valueHandle, 2)}; + appearance->setInputValueVector2f(input, valueHandle.size(), values.data()); + break; + } + + case core::EnginePrimitive::Vec3f: { + auto values{flattenUniformArrayOfVector(valueHandle, 3)}; + appearance->setInputValueVector3f(input, valueHandle.size(), values.data()); + break; + } + + case core::EnginePrimitive::Vec4f: { + auto values{flattenUniformArrayOfVector(valueHandle, 4)}; + appearance->setInputValueVector4f(input, valueHandle.size(), values.data()); + break; + } + + case core::EnginePrimitive::Vec2i: { + auto values{flattenUniformArrayOfVector(valueHandle, 2)}; + appearance->setInputValueVector2i(input, valueHandle.size(), values.data()); + break; + } + + case core::EnginePrimitive::Vec3i: { + auto values{flattenUniformArrayOfVector(valueHandle, 3)}; + appearance->setInputValueVector3i(input, valueHandle.size(), values.data()); + break; + } + + case core::EnginePrimitive::Vec4i: { + auto values{flattenUniformArrayOfVector(valueHandle, 4)}; + appearance->setInputValueVector4i(input, valueHandle.size(), values.data()); + break; + } + + default: + break; + } } + default: break; } diff --git a/components/libRamsesBase/include/ramses_base/BaseEngineBackend.h b/components/libRamsesBase/include/ramses_base/BaseEngineBackend.h index 2f7d1fe6..5987f66e 100644 --- a/components/libRamsesBase/include/ramses_base/BaseEngineBackend.h +++ b/components/libRamsesBase/include/ramses_base/BaseEngineBackend.h @@ -27,7 +27,7 @@ class BaseEngineBackend { Q_DISABLE_COPY(BaseEngineBackend) public: static const rlogic::EFeatureLevel minFeatureLevel = rlogic::EFeatureLevel::EFeatureLevel_01; - static const rlogic::EFeatureLevel maxFeatureLevel = rlogic::EFeatureLevel::EFeatureLevel_02; + static const rlogic::EFeatureLevel maxFeatureLevel = rlogic::EFeatureLevel::EFeatureLevel_03; static const std::string featureLevelDescriptions; typedef std::unique_ptr> UniqueClient; diff --git a/components/libRamsesBase/include/ramses_base/RamsesFormatter.h b/components/libRamsesBase/include/ramses_base/RamsesFormatter.h index 721253cb..bb564e4d 100644 --- a/components/libRamsesBase/include/ramses_base/RamsesFormatter.h +++ b/components/libRamsesBase/include/ramses_base/RamsesFormatter.h @@ -210,6 +210,8 @@ struct fmt::formatter : formatter { return format_to(ctx.out(), "BlitPass"); case ramses::ERamsesObjectType::ERamsesObjectType_TextureSampler: return format_to(ctx.out(), "TextureSampler"); + case ramses::ERamsesObjectType::ERamsesObjectType_TextureSamplerMS: + return format_to(ctx.out(), "TextureSamplerMS"); case ramses::ERamsesObjectType::ERamsesObjectType_RenderBuffer: return format_to(ctx.out(), "RenderBuffer"); case ramses::ERamsesObjectType::ERamsesObjectType_RenderTarget: diff --git a/components/libRamsesBase/include/ramses_base/RamsesHandles.h b/components/libRamsesBase/include/ramses_base/RamsesHandles.h index 31d4e7f5..8f41ead3 100644 --- a/components/libRamsesBase/include/ramses_base/RamsesHandles.h +++ b/components/libRamsesBase/include/ramses_base/RamsesHandles.h @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -38,6 +39,7 @@ #include #include #include +#include #include #include @@ -52,6 +54,8 @@ #include #include #include +#include +#include #include #include @@ -68,17 +72,19 @@ using RamsesScene = RamsesHandle; using RamsesRenderBuffer = RamsesHandle; using RamsesRenderTarget = RamsesHandle; using RamsesTextureSampler = RamsesHandle; +using RamsesTextureSamplerMS = RamsesHandle; using RamsesTimerNode = RamsesHandle; using RamsesLuaModule = RamsesHandle; using RamsesLuaScript = RamsesHandle; using RamsesLuaInterface = RamsesHandle; using RamsesAnchorPoint = RamsesHandle; -/** RESOURCE HANDLES */ + /** RESOURCE HANDLES */ using RamsesEffect = RamsesHandle; using RamsesArrayResource = RamsesHandle; using RamsesTexture2D = RamsesHandle; using RamsesTextureCube = RamsesHandle; +using RamsesBlitPass = RamsesHandle; template void destroyRamsesObject(OwnerType* owner, RamsesType* obj) { @@ -132,8 +138,9 @@ struct RamsesAppearanceHandle { return appearance_; } - void replaceTrackedSamplers(std::vector& newSamplers) { + void replaceTrackedSamplers(std::vector& newSamplers, std::vector& newSamplersMS) { trackedSamplers_ = newSamplers; + trackedSamplersMS_ = newSamplersMS; } RamsesEffect effect() { @@ -152,6 +159,7 @@ struct RamsesAppearanceHandle { // Samplers currently in use by the appearance_. Needed to keep the samplers alive in ramses. std::vector trackedSamplers_; + std::vector trackedSamplersMS_; }; using RamsesAppearance = std::shared_ptr; @@ -410,8 +418,16 @@ struct RamsesRenderGroupHandle { return 0; } + const std::map& containedRenderGroups() const { + return trackedGroups_; + } + + bool empty() const { + return trackedRenderables_.empty() && trackedGroups_.empty(); + } + private: - // The appearance is owned by this class. + // The rendergroup is owned by this class. ramses::RenderGroup* renderGroup_; // Kept for reference; needed to destroy the render group pointer in ramses. @@ -525,6 +541,15 @@ inline RamsesTextureSampler ramsesTextureSampler(ramses::Scene* scene, ramses::E }}; } +inline RamsesTextureSamplerMS ramsesTextureSamplerMS(ramses::Scene* scene, RamsesRenderBuffer buffer, const char* name = nullptr) { + auto ptr = scene->createTextureSamplerMS(*buffer, name); + return { + ptr, + [scene, forceCopy = buffer](ramses::RamsesObject* buffer) { + destroyRamsesObject(scene, static_cast(buffer)); + }}; +} + /** RESOURCE FACTORIES */ inline RamsesEffect ramsesEffect(ramses::Scene* scene, const ramses::EffectDescription& description, const char* name = nullptr) { @@ -567,11 +592,22 @@ inline RamsesTextureCube ramsesTextureCube(ramses::Scene* scene, createRamsesObjectDeleter(scene)}; } +inline RamsesBlitPass ramsesBlitPass(ramses::Scene* scene, RamsesRenderBuffer source, RamsesRenderBuffer dest, + const char* name = nullptr) { + return { + scene->createBlitPass(*source, *dest, name), + [scene, forceSourceCopy = source, forceDestCopy = dest](ramses::RamsesObject* object) { + destroyRamsesObject(scene, static_cast(object)); + }}; +} + using UniqueRamsesAppearanceBinding = std::unique_ptr>; using UniqueRamsesDataArray = std::unique_ptr>; using RamsesNodeBinding = std::shared_ptr; using RamsesCameraBinding = std::shared_ptr; using UniqueRamsesRenderPassBinding = std::unique_ptr>; +using RamsesRenderGroupBinding = std::shared_ptr; + inline UniqueRamsesAppearanceBinding ramsesAppearanceBinding(ramses::Appearance& appearance, rlogic::LogicEngine* logicEngine, const std::string& name, const std::pair &objectID) { UniqueRamsesAppearanceBinding binding{logicEngine->createRamsesAppearanceBinding(appearance, name), @@ -778,5 +814,18 @@ inline RamsesAnchorPoint ramsesAnchorPoint(rlogic::LogicEngine* logicEngine, Ram } +inline RamsesRenderGroupBinding ramsesRenderGroupBinding(rlogic::LogicEngine* logicEngine, RamsesRenderGroup renderGroup, const rlogic::RamsesRenderGroupBindingElements& elements, std::vector nestedGroups, const std::string& name, const std::pair& objectID) { + RamsesRenderGroupBinding binding{logicEngine->createRamsesRenderGroupBinding(**renderGroup, elements, name), + [logicEngine, forceRenderGroupCopy = renderGroup, forceNestedGroupCopy = nestedGroups](rlogic::RamsesRenderGroupBinding* binding) { + destroyLogicObject(logicEngine, binding); + }}; + + if (binding) { + binding->setUserId(objectID.first, objectID.second); + } + + return binding; +} + }; // namespace raco::ramses_base diff --git a/components/libRamsesBase/include/ramses_base/Utils.h b/components/libRamsesBase/include/ramses_base/Utils.h index 83cd098c..e9affce2 100644 --- a/components/libRamsesBase/include/ramses_base/Utils.h +++ b/components/libRamsesBase/include/ramses_base/Utils.h @@ -72,4 +72,9 @@ int ramsesTextureFormatToChannelAmount(ramses::ETextureFormat textureFormat); void normalize16BitColorData(std::vector& data); std::vector generateColorDataWithoutBlueChannel(const std::vector& data); std::vector decodeMipMapData(core::Errors* errors, core::Project& project, core::SEditorObject obj, const std::string& uriPropName, int level, PngDecodingInfo& decodingInfo); + +int clipAndCheckIntProperty(const raco::core::ValueHandle value, core::Errors* errors, bool* allValid); + +ramses::ERenderBufferType ramsesRenderBufferTypeFromFormat(ramses::ERenderBufferFormat format); + }; // namespace raco::ramses_base diff --git a/components/libRamsesBase/src/ramses_adaptor/BaseCameraAdaptorHelpers.cpp b/components/libRamsesBase/src/ramses_adaptor/BaseCameraAdaptorHelpers.cpp index 50d2de80..07ec9375 100644 --- a/components/libRamsesBase/src/ramses_adaptor/BaseCameraAdaptorHelpers.cpp +++ b/components/libRamsesBase/src/ramses_adaptor/BaseCameraAdaptorHelpers.cpp @@ -20,14 +20,22 @@ namespace raco::ramses_adaptor { -void BaseCameraAdaptorHelpers::sync(std::shared_ptr editorObject, ramses::Camera* ramsesCamera, rlogic::RamsesCameraBinding* cameraBinding) { - ramsesCamera->setViewport(*editorObject->viewport_->offsetX_, *editorObject->viewport_->offsetY_, *editorObject->viewport_->width_, *editorObject->viewport_->height_); +void BaseCameraAdaptorHelpers::sync(std::shared_ptr editorObject, ramses::Camera* ramsesCamera, rlogic::RamsesCameraBinding* cameraBinding, core::Errors* errors) { + bool allValid = true; + int clippedWidth = raco::ramses_base::clipAndCheckIntProperty({editorObject, {"viewport", "width"}}, errors, &allValid); + int clippedHeight = raco::ramses_base::clipAndCheckIntProperty({editorObject, {"viewport", "height"}}, errors, &allValid); + + if (allValid) { + ramsesCamera->setViewport(*editorObject->viewport_->offsetX_, *editorObject->viewport_->offsetY_, clippedWidth, clippedHeight); + } + cameraBinding->setName(std::string(editorObject->objectName() + "_CameraBinding").c_str()); - cameraBinding->getInputs()->getChild("viewport")->getChild("offsetX")->set(static_cast(*editorObject->viewport_->offsetX_)); - cameraBinding->getInputs()->getChild("viewport")->getChild("offsetY")->set(static_cast(*editorObject->viewport_->offsetY_)); - // Ramses asserts if the viewport width/height <=0. Unfortunately we cannot prevent a link from setting the value to <=0? - cameraBinding->getInputs()->getChild("viewport")->getChild("width")->set(std::max(1, static_cast(*editorObject->viewport_->width_))); - cameraBinding->getInputs()->getChild("viewport")->getChild("height")->set(std::max(1, static_cast(*editorObject->viewport_->height_))); + cameraBinding->getInputs()->getChild("viewport")->getChild("offsetX")->set(*editorObject->viewport_->offsetX_); + cameraBinding->getInputs()->getChild("viewport")->getChild("offsetY")->set(*editorObject->viewport_->offsetY_); + if (allValid) { + cameraBinding->getInputs()->getChild("viewport")->getChild("width")->set(clippedWidth); + cameraBinding->getInputs()->getChild("viewport")->getChild("height")->set(clippedHeight); + } } const rlogic::Property* BaseCameraAdaptorHelpers::getProperty(rlogic::RamsesCameraBinding* cameraBinding, const std::vector& propertyNamesVector) { diff --git a/components/libRamsesBase/src/ramses_adaptor/BlitPassAdaptor.cpp b/components/libRamsesBase/src/ramses_adaptor/BlitPassAdaptor.cpp new file mode 100644 index 00000000..3deef6fb --- /dev/null +++ b/components/libRamsesBase/src/ramses_adaptor/BlitPassAdaptor.cpp @@ -0,0 +1,145 @@ +/* + * SPDX-License-Identifier: MPL-2.0 + * + * This file is part of Ramses Composer + * (see https://github.com/bmwcarit/ramses-composer). + * + * This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. + * If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +#include "ramses_adaptor/BlitPassAdaptor.h" + +#include "ramses_adaptor/RenderBufferAdaptor.h" +#include "ramses_adaptor/RenderBufferMSAdaptor.h" +#include "ramses_adaptor/SceneAdaptor.h" +#include "ramses_base/Utils.h" + +namespace raco::ramses_adaptor { + +BlitPassAdaptor::BlitPassAdaptor(SceneAdaptor* sceneAdaptor, user_types::SBlitPass blitPass) + : UserTypeObjectAdaptor{sceneAdaptor, blitPass}, + subscriptions_{sceneAdaptor->dispatcher()->registerOn(core::ValueHandle{blitPass, &user_types::BlitPass::sourceRenderBuffer_}, [this]() { + recreate_ = true; + tagDirty(); + }), + sceneAdaptor->dispatcher()->registerOn(core::ValueHandle{blitPass, &user_types::BlitPass::targetRenderBuffer_}, [this]() { + recreate_ = true; + tagDirty(); + }), + sceneAdaptor->dispatcher()->registerOn(core::ValueHandle{blitPass, &user_types::BlitPass::sourceRenderBufferMS_}, [this]() { + recreate_ = true; + tagDirty(); + }), + sceneAdaptor->dispatcher()->registerOn(core::ValueHandle{blitPass, &user_types::BlitPass::targetRenderBufferMS_}, [this]() { + recreate_ = true; + tagDirty(); + }), + sceneAdaptor->dispatcher()->registerOn(core::ValueHandle{blitPass, &user_types::BlitPass::sourceX_}, [this]() { + update_ = true; + tagDirty(); + }), + sceneAdaptor->dispatcher()->registerOn(core::ValueHandle{blitPass, &user_types::BlitPass::sourceY_}, [this]() { + update_ = true; + tagDirty(); + }), + sceneAdaptor->dispatcher()->registerOn(core::ValueHandle{blitPass, &user_types::BlitPass::destinationX_}, [this]() { + update_ = true; + tagDirty(); + }), + sceneAdaptor->dispatcher()->registerOn(core::ValueHandle{blitPass, &user_types::BlitPass::destinationY_}, [this]() { + update_ = true; + tagDirty(); + }), + sceneAdaptor->dispatcher()->registerOn(core::ValueHandle{blitPass, &user_types::BlitPass::width_}, [this]() { + update_ = true; + tagDirty(); + }), + sceneAdaptor->dispatcher()->registerOn(core::ValueHandle{blitPass, &user_types::BlitPass::height_}, [this]() { + update_ = true; + tagDirty(); + }), + sceneAdaptor->dispatcher()->registerOn(core::ValueHandle{blitPass, &user_types::BlitPass::enabled_}, [this]() { + update_ = true; + tagDirty(); + }), + sceneAdaptor->dispatcher()->registerOn(core::ValueHandle{blitPass, &user_types::BlitPass::renderOrder_}, [this]() { + update_ = true; + tagDirty(); + }), + sceneAdaptor_->dispatcher()->registerOnPreviewDirty(blitPass, [this]() { + recreate_ = true; + tagDirty(); + })} { + recreate_ = true; + update_ = false; +} + +bool BlitPassAdaptor::sync(core::Errors* errors) { + ObjectAdaptor::sync(errors); + + if (recreate_) { + errors->removeError({editorObject()->shared_from_this()}); + blitPass_.reset(); + raco::ramses_base::RamsesRenderBuffer startBuffer; + raco::ramses_base::RamsesRenderBuffer endBuffer; + if (auto srcBuffer = sceneAdaptor_->lookup(*editorObject_->sourceRenderBuffer_)) { + startBuffer = srcBuffer->buffer(); + } else if (auto srcBufferMS = sceneAdaptor_->lookup(*editorObject_->sourceRenderBufferMS_)) { + startBuffer = srcBufferMS->buffer(); + } + + if (auto destBuffer = sceneAdaptor_->lookup(*editorObject_->targetRenderBuffer_)) { + endBuffer = destBuffer->buffer(); + } else if (auto destBufferMS = sceneAdaptor_->lookup(*editorObject_->targetRenderBufferMS_)) { + endBuffer = destBufferMS->buffer(); + } + + if (startBuffer && endBuffer) { + blitPass_ = raco::ramses_base::ramsesBlitPass(sceneAdaptor_->scene(), startBuffer, endBuffer, editorObject_->objectName().c_str()); + + if (!blitPass_) { + errors->addError(core::ErrorCategory::GENERAL, core::ErrorLevel::ERROR, {editorObject()->shared_from_this()}, + "BlitPass could not be created, check Ramses logs."); + } + } + recreate_ = false; + update_ = true; + } + + if (blitPass_ && update_) { + errors->removeError({editorObject()->shared_from_this()}); + blitPass_->setEnabled(*editorObject()->enabled_); + blitPass_->setRenderOrder(*editorObject()->renderOrder_); + + bool allValid = true; + uint32_t clippedSourceX = raco::ramses_base::clipAndCheckIntProperty({editorObject_, &raco::user_types::BlitPass::sourceX_}, errors, &allValid); + uint32_t clippedSourceY = raco::ramses_base::clipAndCheckIntProperty({editorObject_, &raco::user_types::BlitPass::sourceY_}, errors, &allValid); + uint32_t clippedDestinationX = raco::ramses_base::clipAndCheckIntProperty({editorObject_, &raco::user_types::BlitPass::destinationX_}, errors, &allValid); + uint32_t clippedDestinationY = raco::ramses_base::clipAndCheckIntProperty({editorObject_, &raco::user_types::BlitPass::destinationY_}, errors, &allValid); + uint32_t clippedWidth = raco::ramses_base::clipAndCheckIntProperty({editorObject_, &raco::user_types::BlitPass::width_}, errors, &allValid); + uint32_t clippedHeight = raco::ramses_base::clipAndCheckIntProperty({editorObject_, &raco::user_types::BlitPass::height_}, errors, &allValid); + + if (allValid) { + auto updateStatus = blitPass_->setBlittingRegion(clippedSourceX, clippedSourceY, clippedDestinationX, clippedDestinationY, clippedWidth, clippedHeight); + + if (updateStatus != ramses::StatusOK) { + errors->addError(core::ErrorCategory::GENERAL, core::ErrorLevel::ERROR, {editorObject()->shared_from_this()}, + blitPass_->getStatusMessage(updateStatus)); + } + } + } + update_ = false; + + return true; +} + +std::vector BlitPassAdaptor::getExportInformation() const { + auto result = std::vector(); + if (blitPass_ != nullptr) { + result.emplace_back(blitPass_->getType(), blitPass_->getName()); + } + + return result; +} + +} // namespace raco::ramses_adaptor diff --git a/components/libRamsesBase/src/ramses_adaptor/Factories.cpp b/components/libRamsesBase/src/ramses_adaptor/Factories.cpp index fa4400aa..7b7b512b 100644 --- a/components/libRamsesBase/src/ramses_adaptor/Factories.cpp +++ b/components/libRamsesBase/src/ramses_adaptor/Factories.cpp @@ -12,6 +12,7 @@ #include "ramses_adaptor/AnchorPointAdaptor.h" #include "ramses_adaptor/AnimationAdaptor.h" #include "ramses_adaptor/AnimationChannelAdaptor.h" +#include "ramses_adaptor/BlitPassAdaptor.h" #include "ramses_adaptor/CubeMapAdaptor.h" #include "ramses_adaptor/LuaScriptAdaptor.h" #include "ramses_adaptor/LuaInterfaceAdaptor.h" @@ -23,6 +24,7 @@ #include "ramses_adaptor/OrthographicCameraAdaptor.h" #include "ramses_adaptor/PerspectiveCameraAdaptor.h" #include "ramses_adaptor/RenderBufferAdaptor.h" +#include "ramses_adaptor/RenderBufferMSAdaptor.h" #include "ramses_adaptor/RenderTargetAdaptor.h" #include "ramses_adaptor/RenderPassAdaptor.h" #include "ramses_adaptor/RenderLayerAdaptor.h" @@ -62,10 +64,12 @@ UniqueObjectAdaptor Factories::createAdaptor(SceneAdaptor* sceneAdaptor, core::S {user_types::LuaScriptModule::typeDescription.typeName, [](SceneAdaptor* sceneAdaptor, core::SEditorObject obj) { return std::make_unique(sceneAdaptor, std::dynamic_pointer_cast(obj)); }}, {user_types::RenderBuffer::typeDescription.typeName, [](SceneAdaptor* sceneAdaptor, core::SEditorObject obj) { return std::make_unique(sceneAdaptor, std::dynamic_pointer_cast(obj)); }}, + {user_types::RenderBufferMS::typeDescription.typeName, [](SceneAdaptor* sceneAdaptor, core::SEditorObject obj) { return std::make_unique(sceneAdaptor, std::dynamic_pointer_cast(obj)); }}, {user_types::RenderTarget::typeDescription.typeName, [](SceneAdaptor* sceneAdaptor, core::SEditorObject obj) { return std::make_unique(sceneAdaptor, std::dynamic_pointer_cast(obj)); }}, {user_types::RenderPass::typeDescription.typeName, [](SceneAdaptor* sceneAdaptor, core::SEditorObject obj) { return std::make_unique(sceneAdaptor, std::dynamic_pointer_cast(obj)); }}, {user_types::RenderLayer::typeDescription.typeName, [](SceneAdaptor* sceneAdaptor, core::SEditorObject obj) { return std::make_unique(sceneAdaptor, std::dynamic_pointer_cast(obj)); }}, {user_types::Timer::typeDescription.typeName, [](SceneAdaptor* sceneAdaptor, core::SEditorObject obj) { return std::make_unique(sceneAdaptor, std::dynamic_pointer_cast(obj)); }}, + {user_types::BlitPass::typeDescription.typeName, [](SceneAdaptor* sceneAdaptor, core::SEditorObject obj) { return std::make_unique(sceneAdaptor, std::dynamic_pointer_cast(obj)); }}, // LOGIC ENGINE {user_types::LuaScript::typeDescription.typeName, [](SceneAdaptor* sceneAdaptor, core::SEditorObject obj) { return std::make_unique(sceneAdaptor, std::dynamic_pointer_cast(obj)); }}, diff --git a/components/libRamsesBase/src/ramses_adaptor/MaterialAdaptor.cpp b/components/libRamsesBase/src/ramses_adaptor/MaterialAdaptor.cpp index 8ef7ba29..cd659462 100644 --- a/components/libRamsesBase/src/ramses_adaptor/MaterialAdaptor.cpp +++ b/components/libRamsesBase/src/ramses_adaptor/MaterialAdaptor.cpp @@ -13,13 +13,15 @@ #include "ramses_adaptor/CubeMapAdaptor.h" #include "ramses_adaptor/ObjectAdaptor.h" +#include "ramses_adaptor/RenderBufferAdaptor.h" +#include "ramses_adaptor/RenderBufferMSAdaptor.h" #include "ramses_adaptor/SceneAdaptor.h" #include "ramses_adaptor/TextureSamplerAdaptor.h" -#include "ramses_adaptor/RenderBufferAdaptor.h" #include "ramses_base/Utils.h" #include "user_types/EngineTypeAnnotation.h" #include "user_types/Material.h" #include "user_types/RenderBuffer.h" +#include "user_types/RenderBufferMS.h" #include "utils/FileUtils.h" namespace raco::ramses_adaptor { @@ -136,11 +138,10 @@ std::vector MaterialAdaptor::getExportInformation() const { } void updateAppearance(core::Errors* errors, SceneAdaptor* sceneAdaptor, raco::ramses_base::RamsesAppearance appearance, const core::ValueHandle& optionsHandle, const core::ValueHandle& uniformsHandle) { - int colorOp = static_cast(optionsHandle.get("blendOperationColor").as()); int alphaOp = static_cast(optionsHandle.get("blendOperationAlpha").as()); if (colorOp == ramses::EBlendOperation::EBlendOperation_Disabled && alphaOp != ramses::EBlendOperation::EBlendOperation_Disabled) { - errors->addError(raco::core::ErrorCategory::GENERAL, raco::core::ErrorLevel::ERROR, optionsHandle.get("blendOperationAlpha"), + errors->addError(raco::core::ErrorCategory::GENERAL, raco::core::ErrorLevel::ERROR, optionsHandle.get("blendOperationAlpha"), "Inconsistent Blend Operation settings: Color disabled while Alpha is not."); } else { errors->removeError(optionsHandle.get("blendOperationAlpha")); @@ -152,7 +153,6 @@ void updateAppearance(core::Errors* errors, SceneAdaptor* sceneAdaptor, raco::ra errors->removeError(optionsHandle.get("blendOperationColor")); } - setDepthWrite(appearance->get(), optionsHandle.get("depthwrite")); setDepthFunction(appearance->get(), optionsHandle.get("depthFunction")); setBlendMode(appearance->get(), optionsHandle); @@ -160,6 +160,7 @@ void updateAppearance(core::Errors* errors, SceneAdaptor* sceneAdaptor, raco::ra setCullMode(appearance->get(), optionsHandle.get("cullmode")); std::vector newSamplers; + std::vector newSamplersMS; for (size_t i{0}; i < uniformsHandle.size(); i++) { setUniform(appearance->get(), uniformsHandle[i]); @@ -168,38 +169,58 @@ void updateAppearance(core::Errors* errors, SceneAdaptor* sceneAdaptor, raco::ra auto anno = uniformsHandle[i].query(); auto engineType = anno->type(); - raco::ramses_base::RamsesTextureSampler sampler = nullptr; - if (engineType == raco::core::EnginePrimitive::TextureSampler2D) { - if (auto texture = uniformsHandle[i].asTypedRef()) { - if (auto adaptor = sceneAdaptor->lookup(texture)) { - sampler = adaptor->getRamsesObjectPointer(); - } - } else if (auto buffer = uniformsHandle[i].asTypedRef()) { - if (auto adaptor = sceneAdaptor->lookup(buffer)) { - sampler = adaptor->getRamsesObjectPointer(); + if (engineType == raco::core::EnginePrimitive::TextureSampler2DMS) { + if (auto buffer = uniformsHandle[i].asTypedRef()) { + if (auto adaptor = sceneAdaptor->lookup(buffer)) { + if (auto samplerMS = adaptor->getRamsesObjectPointer()) { + ramses::UniformInput input; + (*appearance)->getEffect().findUniformInput(uniformsHandle[i].getPropName().c_str(), input); + (*appearance)->setInputTexture(input, *samplerMS); + newSamplersMS.emplace_back(samplerMS); + } else { + errors->addError(raco::core::ErrorCategory::GENERAL, raco::core::ErrorLevel::ERROR, uniformsHandle[i], "Sampler for this RenderBufferMS not available."); + } } } else { - errors->addError(raco::core::ErrorCategory::GENERAL, raco::core::ErrorLevel::ERROR, uniformsHandle[i], "Texture or RenderBuffer needed for this uniform."); + errors->addError(raco::core::ErrorCategory::GENERAL, raco::core::ErrorLevel::ERROR, uniformsHandle[i], "RenderBufferMS needed for this uniform."); } - } else if (engineType == raco::core::EnginePrimitive::TextureSamplerCube) { - if (auto texture = uniformsHandle[i].asTypedRef()) { - if (auto adaptor = sceneAdaptor->lookup(texture)) { - sampler = adaptor->getRamsesObjectPointer(); + } else { + raco::ramses_base::RamsesTextureSampler sampler = nullptr; + if (engineType == raco::core::EnginePrimitive::TextureSampler2D) { + if (auto texture = uniformsHandle[i].asTypedRef()) { + if (auto adaptor = sceneAdaptor->lookup(texture)) { + sampler = adaptor->getRamsesObjectPointer(); + } + } else if (auto buffer = uniformsHandle[i].asTypedRef()) { + if (auto adaptor = sceneAdaptor->lookup(buffer)) { + sampler = adaptor->getRamsesObjectPointer(); + if (!sampler) { + errors->addError(raco::core::ErrorCategory::GENERAL, raco::core::ErrorLevel::ERROR, uniformsHandle[i], "Sampler for this RenderBuffer not available."); + } + } + } else { + errors->addError(raco::core::ErrorCategory::GENERAL, raco::core::ErrorLevel::ERROR, uniformsHandle[i], "Texture or RenderBuffer needed for this uniform."); + } + } else if (engineType == raco::core::EnginePrimitive::TextureSamplerCube) { + if (auto texture = uniformsHandle[i].asTypedRef()) { + if (auto adaptor = sceneAdaptor->lookup(texture)) { + sampler = adaptor->getRamsesObjectPointer(); + } + } else { + errors->addError(raco::core::ErrorCategory::GENERAL, raco::core::ErrorLevel::ERROR, uniformsHandle[i], "CubeMap needed for this uniform."); } - } else { - errors->addError(raco::core::ErrorCategory::GENERAL, raco::core::ErrorLevel::ERROR, uniformsHandle[i], "CubeMap needed for this uniform."); } - } - if (sampler) { - ramses::UniformInput input; - (*appearance)->getEffect().findUniformInput(uniformsHandle[i].getPropName().c_str(), input); - (*appearance)->setInputTexture(input, *sampler); - newSamplers.emplace_back(sampler); + if (sampler) { + ramses::UniformInput input; + (*appearance)->getEffect().findUniformInput(uniformsHandle[i].getPropName().c_str(), input); + (*appearance)->setInputTexture(input, *sampler); + newSamplers.emplace_back(sampler); + } } } } - appearance->replaceTrackedSamplers(newSamplers); + appearance->replaceTrackedSamplers(newSamplers, newSamplersMS); } }; // namespace raco::ramses_adaptor diff --git a/components/libRamsesBase/src/ramses_adaptor/OrthographicCameraAdaptor.cpp b/components/libRamsesBase/src/ramses_adaptor/OrthographicCameraAdaptor.cpp index ad38581d..586481b3 100644 --- a/components/libRamsesBase/src/ramses_adaptor/OrthographicCameraAdaptor.cpp +++ b/components/libRamsesBase/src/ramses_adaptor/OrthographicCameraAdaptor.cpp @@ -33,7 +33,7 @@ OrthographicCameraAdaptor::~OrthographicCameraAdaptor() { bool OrthographicCameraAdaptor::sync(core::Errors* errors) { SpatialAdaptor::sync(errors); - BaseCameraAdaptorHelpers::sync(editorObject(), ramsesObject().get(), cameraBinding_.get()); + BaseCameraAdaptorHelpers::sync(editorObject(), ramsesObject().get(), cameraBinding_.get(), errors); (*ramsesObject()).setFrustum(static_cast(*editorObject()->frustum_->left_), static_cast(*editorObject()->frustum_->right_), static_cast(*editorObject()->frustum_->bottom_), static_cast(*editorObject()->frustum_->top_), static_cast(*editorObject()->frustum_->near_), static_cast(*editorObject()->frustum_->far_)); // The logic engine will always set the entire struct even if there is a link for only one of the values, and use the default values in the binding // for the non-linked elements in the struct - so we need to also set the default values for the bindings. diff --git a/components/libRamsesBase/src/ramses_adaptor/PerspectiveCameraAdaptor.cpp b/components/libRamsesBase/src/ramses_adaptor/PerspectiveCameraAdaptor.cpp index 9ecfa1b0..17183fee 100644 --- a/components/libRamsesBase/src/ramses_adaptor/PerspectiveCameraAdaptor.cpp +++ b/components/libRamsesBase/src/ramses_adaptor/PerspectiveCameraAdaptor.cpp @@ -51,7 +51,7 @@ bool PerspectiveCameraAdaptor::sync(core::Errors* errors) { } cameraBinding_ = raco::ramses_base::ramsesCameraBinding(getRamsesObjectPointer(), &sceneAdaptor_->logicEngine(), editorObject_->objectIDAsRamsesLogicID(), *editorObject_->frustumType_ == static_cast(raco::user_types::EFrustumType::Planes)); - BaseCameraAdaptorHelpers::sync(editorObject(), ramsesObject().get(), cameraBinding_.get()); + BaseCameraAdaptorHelpers::sync(editorObject(), ramsesObject().get(), cameraBinding_.get(), errors); for (size_t index = 0; index < editorObject_->frustum_->size(); index++) { auto& propName = editorObject_->frustum_->name(index); diff --git a/components/libRamsesBase/src/ramses_adaptor/RenderBufferAdaptor.cpp b/components/libRamsesBase/src/ramses_adaptor/RenderBufferAdaptor.cpp index 28521db6..186cfc0d 100644 --- a/components/libRamsesBase/src/ramses_adaptor/RenderBufferAdaptor.cpp +++ b/components/libRamsesBase/src/ramses_adaptor/RenderBufferAdaptor.cpp @@ -10,6 +10,7 @@ #include "ramses_adaptor/RenderBufferAdaptor.h" #include "ramses_base/RamsesHandles.h" +#include "ramses_base/Utils.h" #include "core/BasicAnnotations.h" namespace raco::ramses_adaptor { @@ -43,44 +44,30 @@ RenderBufferAdaptor::RenderBufferAdaptor(SceneAdaptor* sceneAdaptor, std::shared } bool RenderBufferAdaptor::sync(core::Errors* errors) { - using namespace ramses; - std::map bufferTypeFromFormat = { - {ERenderBufferFormat_RGBA4, ERenderBufferType_Color}, - {ERenderBufferFormat_R8, ERenderBufferType_Color}, - {ERenderBufferFormat_RG8, ERenderBufferType_Color}, - {ERenderBufferFormat_RGB8, ERenderBufferType_Color}, - {ERenderBufferFormat_RGBA8, ERenderBufferType_Color}, - {ERenderBufferFormat_R16F, ERenderBufferType_Color}, - {ERenderBufferFormat_R32F, ERenderBufferType_Color}, - {ERenderBufferFormat_RG16F, ERenderBufferType_Color}, - {ERenderBufferFormat_RG32F, ERenderBufferType_Color}, - {ERenderBufferFormat_RGB16F, ERenderBufferType_Color}, - {ERenderBufferFormat_RGB32F, ERenderBufferType_Color}, - {ERenderBufferFormat_RGBA16F, ERenderBufferType_Color}, - {ERenderBufferFormat_RGBA32F, ERenderBufferType_Color}, - {ERenderBufferFormat_Depth24, ERenderBufferType_Depth}, - {ERenderBufferFormat_Depth24_Stencil8, ERenderBufferType_DepthStencil}}; + buffer_.reset(); ramses::ERenderBufferFormat format = static_cast(*editorObject()->format_); - ramses::ERenderBufferType type = bufferTypeFromFormat.at(format); + ramses::ERenderBufferType type = raco::ramses_base::ramsesRenderBufferTypeFromFormat(format); - const auto& widthRange = editorObject()->width_.staticQuery>(); - const auto& heightRange = editorObject()->height_.staticQuery>(); + bool allValid = true; + uint32_t clippedWidth = raco::ramses_base::clipAndCheckIntProperty({editorObject_, &raco::user_types::RenderBuffer::width_}, errors, &allValid); + uint32_t clippedHeight = raco::ramses_base::clipAndCheckIntProperty({editorObject_, &raco::user_types::RenderBuffer::height_}, errors, &allValid); - buffer_ = raco::ramses_base::ramsesRenderBuffer(sceneAdaptor_->scene(), - std::min(std::max(*widthRange.min_, *editorObject()->width_), *widthRange.max_), - std::min(std::max(*heightRange.min_, *editorObject()->height_), *heightRange.max_), - type, - format, - ramses::ERenderBufferAccessMode_ReadWrite, - 0U, - (editorObject()->objectName() + "_Buffer").c_str()); + if (allValid) { + buffer_ = raco::ramses_base::ramsesRenderBuffer(sceneAdaptor_->scene(), + clippedWidth, clippedHeight, + type, + format, + ramses::ERenderBufferAccessMode_ReadWrite, + 0U, + (editorObject()->objectName() + "_Buffer").c_str()); + } if (buffer_) { // For depth buffers, the UI does not display the sampler parameters - so force them to default. // Using depth buffers directly as textures is not recommended. ramses_base::RamsesTextureSampler textureSampler; - if (type == ERenderBufferType_Color) { + if (type == ramses::ERenderBufferType_Color) { textureSampler = ramses_base::ramsesTextureSampler(sceneAdaptor_->scene(), static_cast(*editorObject()->wrapUMode_), static_cast(*editorObject()->wrapVMode_), diff --git a/components/libRamsesBase/src/ramses_adaptor/RenderBufferMSAdaptor.cpp b/components/libRamsesBase/src/ramses_adaptor/RenderBufferMSAdaptor.cpp new file mode 100644 index 00000000..12bafe9d --- /dev/null +++ b/components/libRamsesBase/src/ramses_adaptor/RenderBufferMSAdaptor.cpp @@ -0,0 +1,87 @@ +/* + * SPDX-License-Identifier: MPL-2.0 + * + * This file is part of Ramses Composer + * (see https://github.com/bmwcarit/ramses-composer). + * + * This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. + * If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include "ramses_adaptor/RenderBufferMSAdaptor.h" +#include "ramses_base/RamsesHandles.h" +#include "core/BasicAnnotations.h" + +namespace raco::ramses_adaptor { + +RenderBufferMSAdaptor::RenderBufferMSAdaptor(SceneAdaptor* sceneAdaptor, std::shared_ptr editorObject) + : TypedObjectAdaptor(sceneAdaptor, editorObject, {}), + subscriptions_{ + sceneAdaptor->dispatcher()->registerOn(core::ValueHandle{editorObject, &user_types::RenderBufferMS::width_}, [this]() { + tagDirty(); + }), + sceneAdaptor->dispatcher()->registerOn(core::ValueHandle{editorObject, &user_types::RenderBufferMS::height_}, [this]() { + tagDirty(); + }), + sceneAdaptor->dispatcher()->registerOn(core::ValueHandle{editorObject, &user_types::RenderBufferMS::format_}, [this]() { + tagDirty(); + }), + sceneAdaptor->dispatcher()->registerOn(core::ValueHandle{editorObject, &user_types::RenderBufferMS::sampleCount_}, [this]() { + tagDirty(); + })} { +} + +bool RenderBufferMSAdaptor::sync(core::Errors* errors) { + buffer_.reset(); + + auto sampleCount = *editorObject()->sampleCount_; + if (sampleCount < raco::user_types::RenderBufferMS::SAMPLE_COUNT_MIN || sampleCount > raco::user_types::RenderBufferMS::SAMPLE_COUNT_MAX) { + reset(nullptr); + tagDirty(false); + return true; + } + + ramses::ERenderBufferFormat format = static_cast(*editorObject()->format_); + ramses::ERenderBufferType type = raco::ramses_base::ramsesRenderBufferTypeFromFormat(format); + + bool allValid = true; + uint32_t clippedWidth = raco::ramses_base::clipAndCheckIntProperty({editorObject_, &raco::user_types::RenderBufferMS::width_}, errors, &allValid); + uint32_t clippedHeight = raco::ramses_base::clipAndCheckIntProperty({editorObject_, &raco::user_types::RenderBufferMS::height_}, errors, &allValid); + + if (allValid) { + buffer_ = raco::ramses_base::ramsesRenderBuffer(sceneAdaptor_->scene(), + clippedWidth, clippedHeight, + type, + format, + ramses::ERenderBufferAccessMode_ReadWrite, + sampleCount, + (editorObject()->objectName() + "_BufferMS").c_str()); + } + + if (buffer_) { + auto textureSampler = ramses_base::ramsesTextureSamplerMS(sceneAdaptor_->scene(), buffer_, (editorObject()->objectName() + "_TextureSamplerMS").c_str()); + reset(std::move(textureSampler)); + } else { + reset(nullptr); + } + + tagDirty(false); + return true; +} + +ramses_base::RamsesRenderBuffer RenderBufferMSAdaptor::buffer() const { + return buffer_; +} + +std::vector RenderBufferMSAdaptor::getExportInformation() const { + if (buffer_ == nullptr) { + return {}; + } + + return { + ExportInformation{ramsesObject().getType(), ramsesObject().getName()}, + ExportInformation{buffer_->getType(), buffer_->getName()}, + }; +} + +} // namespace raco::ramses_adaptor diff --git a/components/libRamsesBase/src/ramses_adaptor/RenderLayerAdaptor.cpp b/components/libRamsesBase/src/ramses_adaptor/RenderLayerAdaptor.cpp index 17964488..43e870d7 100644 --- a/components/libRamsesBase/src/ramses_adaptor/RenderLayerAdaptor.cpp +++ b/components/libRamsesBase/src/ramses_adaptor/RenderLayerAdaptor.cpp @@ -16,7 +16,6 @@ #include "user_types/Enumerations.h" #include "user_types/MeshNode.h" -#include "user_types/Prefab.h" #include "core/Queries_Tags.h" @@ -43,11 +42,12 @@ void RenderLayerAdaptor::buildRenderGroup(core::Errors* errors) { ramsesObject().setName(editorObject()->objectName().c_str()); + binding_.reset(); std::vector topLevelNodes; std::vector layers; for (auto const& child : sceneAdaptor_->project().instances()) { - if (!child->getParent() && !child->isType()) { + if (!child->getParent() && child->as()) { topLevelNodes.emplace_back(child); } if (auto layer = child->as()) { @@ -58,24 +58,46 @@ void RenderLayerAdaptor::buildRenderGroup(core::Errors* errors) { std::set materialTags{editorObject()->materialFilterTags()}; raco::user_types::ERenderLayerOrder sortOrder = static_cast(*editorObject()->sortOrder_); + bool sceneGraphOrder = sortOrder == raco::user_types::ERenderLayerOrder::SceneGraph; int32_t orderIndex = 0; + rlogic::RamsesRenderGroupBindingElements bindingElements; + std::vector nestedGroups; + for (size_t index = 0; index < editorObject()->renderableTags_->size(); index++) { auto const& renderableTag = editorObject()->renderableTags_->name(index); - if (sortOrder == raco::user_types::ERenderLayerOrder::Manual) { + ramses_base::RamsesRenderGroup container; + + if (sceneGraphOrder) { + // we iterate the scene graph several times, make sure the mesh nodes are + // sorted by scene graph even if they are from different tags. + orderIndex = 0; + container = getRamsesObjectPointer(); + } else { orderIndex = editorObject()->renderableTags_->get(index)->asInt(); - } else if (sortOrder == raco::user_types::ERenderLayerOrder::SceneGraph) { - orderIndex = 0; // we iterate the scene graph several times, make sure the mesh nodes are sorted by scene graph even if they are from different tags. + container = raco::ramses_base::ramsesRenderGroup(sceneAdaptor_->scene()); + container->setName((editorObject()->objectName() + "." + renderableTag).c_str()); } - bool sceneGraphOrder = sortOrder == raco::user_types::ERenderLayerOrder::SceneGraph; - buildRenderableOrder(errors, topLevelNodes, renderableTag, false, materialTags, *editorObject()->materialFilterMode_ == static_cast(user_types::ERenderLayerMaterialFilterMode::Exclusive), orderIndex, sceneGraphOrder); - addNestedLayers(errors, layers, renderableTag, orderIndex, sceneGraphOrder); + buildRenderableOrder(errors, container, topLevelNodes, renderableTag, false, materialTags, *editorObject()->materialFilterMode_ == static_cast(user_types::ERenderLayerMaterialFilterMode::Exclusive), orderIndex, sceneGraphOrder); + addNestedLayers(errors, container, layers, renderableTag, orderIndex, sceneGraphOrder); + + if (!sceneGraphOrder && (!sceneAdaptor_->optimizeForExport() || !container->empty())) { + ramsesObject().addRenderGroup(container, orderIndex); + bindingElements.addElement(**container, renderableTag); + nestedGroups.emplace_back(container); + } + } + + if (!sceneGraphOrder && this->sceneAdaptor_->featureLevel() >= rlogic::EFeatureLevel::EFeatureLevel_03 && !getRamsesObjectPointer()->empty()) { + binding_ = ramses_base::ramsesRenderGroupBinding(&sceneAdaptor_->logicEngine(), getRamsesObjectPointer(), bindingElements, nestedGroups, + editorObject()->objectName() + "_Binding", + editorObject()->objectIDAsRamsesLogicID()); } } -void RenderLayerAdaptor::buildRenderableOrder(core::Errors* errors, std::vector& objs, const std::string& tag, bool parentActive, const std::set& materialFilterTags, bool materialFilterExclusive, int32_t& orderIndex, bool sceneGraphOrder) { +void RenderLayerAdaptor::buildRenderableOrder(core::Errors* errors, ramses_base::RamsesRenderGroup container, std::vector& objs, const std::string& tag, bool parentActive, const std::set& materialFilterTags, bool materialFilterExclusive, int32_t& orderIndex, bool sceneGraphOrder) { for (const auto& obj : objs) { bool currentActive = parentActive || core::Queries::hasObjectTag(obj->as(), tag); @@ -84,14 +106,26 @@ void RenderLayerAdaptor::buildRenderableOrder(core::Errors* errors, std::vector< if (obj->isType()) { if (core::Queries::isMeshNodeInMaterialFilter(obj->as(), materialFilterTags, materialFilterExclusive)) { auto adaptor = sceneAdaptor_->lookup(obj); - if (ramsesObject().containsMeshNode(adaptor->getRamsesObjectPointer())) { - if (ramsesObject().getMeshNodeOrder(adaptor->getRamsesObjectPointer()) != orderIndex) { - const auto errorMsg = fmt::format("Mesh node '{}' has been added to render layer '{}' more than once with different priorities.", obj->objectName(), editorObject()->objectName()); - errors->addError(core::ErrorCategory::GENERAL, core::ErrorLevel::WARNING, {editorObject()->shared_from_this(), &user_types::RenderLayer::renderableTags_}, errorMsg); - LOG_WARNING(raco::log_system::RAMSES_ADAPTOR, errorMsg); + if (sceneGraphOrder) { + if (ramsesObject().containsMeshNode(adaptor->getRamsesObjectPointer())) { + if (ramsesObject().getMeshNodeOrder(adaptor->getRamsesObjectPointer()) != orderIndex) { + const auto errorMsg = fmt::format("Mesh node '{}' has been added to render layer '{}' more than once with different priorities.", obj->objectName(), editorObject()->objectName()); + errors->addError(core::ErrorCategory::GENERAL, core::ErrorLevel::WARNING, {editorObject()->shared_from_this(), &user_types::RenderLayer::renderableTags_}, errorMsg); + LOG_WARNING(raco::log_system::RAMSES_ADAPTOR, errorMsg); + } + } else { + container->addMeshNode(adaptor->getRamsesObjectPointer(), orderIndex); } } else { - ramsesObject().addMeshNode(adaptor->getRamsesObjectPointer(), orderIndex); + if (std::any_of(getRamsesObjectPointer()->containedRenderGroups().begin(), getRamsesObjectPointer()->containedRenderGroups().end(), [adaptor, container](const auto& item) { + return item.first->containsMeshNode(adaptor->getRamsesObjectPointer()) && item.first != container; + })) { + const auto errorMsg = fmt::format("Mesh node '{}' has been added to render layer '{}' via multiple tags.", obj->objectName(), editorObject()->objectName()); + errors->addError(core::ErrorCategory::GENERAL, core::ErrorLevel::ERROR, {editorObject()->shared_from_this(), &user_types::RenderLayer::renderableTags_}, errorMsg); + LOG_ERROR(raco::log_system::RAMSES_ADAPTOR, errorMsg); + } else { + container->addMeshNode(adaptor->getRamsesObjectPointer(), orderIndex); + } } } } @@ -104,7 +138,7 @@ void RenderLayerAdaptor::buildRenderableOrder(core::Errors* errors, std::vector< if (obj->children_->size() > 0) { auto vec = obj->children_->asVector(); - buildRenderableOrder(errors, vec, tag, currentActive, materialFilterTags, materialFilterExclusive, orderIndex, sceneGraphOrder); + buildRenderableOrder(errors, container, vec, tag, currentActive, materialFilterTags, materialFilterExclusive, orderIndex, sceneGraphOrder); } } } @@ -132,7 +166,7 @@ bool containsLayer(const std::vector& allLayers, user_ } } -void RenderLayerAdaptor::addNestedLayers(core::Errors* errors, const std::vector& layers, const std::string& tag, int32_t orderIndex, bool sceneGraphOrder) { +void RenderLayerAdaptor::addNestedLayers(core::Errors* errors, ramses_base::RamsesRenderGroup container, const std::vector& layers, const std::string& tag, int32_t orderIndex, bool sceneGraphOrder) { for (auto layer : layers) { if (core::Queries::hasObjectTag(layer, tag)) { if (sceneGraphOrder) { @@ -147,14 +181,26 @@ void RenderLayerAdaptor::addNestedLayers(core::Errors* errors, const std::vector LOG_WARNING(raco::log_system::RAMSES_ADAPTOR, errorMsg); } else { if (auto adaptor = sceneAdaptor_->lookup(layer); adaptor != nullptr) { - if (ramsesObject().containsRenderGroup(adaptor->getRamsesObjectPointer())) { - if (ramsesObject().getRenderGroupOrder(adaptor->getRamsesObjectPointer()) != orderIndex) { - const auto errorMsg = fmt::format("Render layer '{}' has been added to render layer '{}' more than once with different priorities.", layer->objectName(), editorObject()->objectName()); - errors->addError(core::ErrorCategory::GENERAL, core::ErrorLevel::WARNING, {editorObject()->shared_from_this(), &user_types::RenderLayer::renderableTags_}, errorMsg); - LOG_WARNING(raco::log_system::RAMSES_ADAPTOR, errorMsg); + if (sceneGraphOrder) { + if (ramsesObject().containsRenderGroup(adaptor->getRamsesObjectPointer())) { + if (ramsesObject().getRenderGroupOrder(adaptor->getRamsesObjectPointer()) != orderIndex) { + const auto errorMsg = fmt::format("Render layer '{}' has been added to render layer '{}' more than once with different priorities.", layer->objectName(), editorObject()->objectName()); + errors->addError(core::ErrorCategory::GENERAL, core::ErrorLevel::WARNING, {editorObject()->shared_from_this(), &user_types::RenderLayer::renderableTags_}, errorMsg); + LOG_WARNING(raco::log_system::RAMSES_ADAPTOR, errorMsg); + } + } else { + container->addRenderGroup(adaptor->getRamsesObjectPointer(), orderIndex); } } else { - ramsesObject().addRenderGroup(adaptor->getRamsesObjectPointer(), orderIndex); + if (std::any_of(getRamsesObjectPointer()->containedRenderGroups().begin(), getRamsesObjectPointer()->containedRenderGroups().end(), [adaptor, container](const auto& item) { + return item.first->containsRenderGroup(adaptor->getRamsesObjectPointer()) && item.first != container; + })) { + const auto errorMsg = fmt::format("Render layer '{}' has been added to render layer '{}' via multiple tags.", layer->objectName(), editorObject()->objectName()); + errors->addError(core::ErrorCategory::GENERAL, core::ErrorLevel::ERROR, {editorObject()->shared_from_this(), &user_types::RenderLayer::renderableTags_}, errorMsg); + LOG_ERROR(raco::log_system::RAMSES_ADAPTOR, errorMsg); + } else { + container->addRenderGroup(adaptor->getRamsesObjectPointer(), orderIndex); + } } } } @@ -177,5 +223,25 @@ std::vector RenderLayerAdaptor::getExportInformation() const return {ExportInformation{ramses::ERamsesObjectType_RenderGroup, getRamsesObjectPointer()->getName()}}; } +void RenderLayerAdaptor::getLogicNodes(std::vector& logicNodes) const { + if (binding_) { + logicNodes.push_back(binding_.get()); + } +} + +const rlogic::Property* RenderLayerAdaptor::getProperty(const std::vector& propertyNamesVector) { + if (binding_ && propertyNamesVector.size() >= 2) { + return binding_->getInputs()->getChild("renderOrders")->getChild(propertyNamesVector[1]); + } + return nullptr; +} + +void RenderLayerAdaptor::onRuntimeError(core::Errors& errors, std::string const& message, core::ErrorLevel level) { + core::ValueHandle const valueHandle{editorObject_}; + if (errors.hasError(valueHandle)) { + return; + } + errors.addError(core::ErrorCategory::RAMSES_LOGIC_RUNTIME, level, valueHandle, message); +} } // namespace raco::ramses_adaptor diff --git a/components/libRamsesBase/src/ramses_adaptor/RenderTargetAdaptor.cpp b/components/libRamsesBase/src/ramses_adaptor/RenderTargetAdaptor.cpp index c1099aab..e9fedb96 100644 --- a/components/libRamsesBase/src/ramses_adaptor/RenderTargetAdaptor.cpp +++ b/components/libRamsesBase/src/ramses_adaptor/RenderTargetAdaptor.cpp @@ -10,6 +10,7 @@ #include "ramses_adaptor/RenderTargetAdaptor.h" #include "ramses_adaptor/RenderBufferAdaptor.h" +#include "ramses_adaptor/RenderBufferMSAdaptor.h" #include "ramses_base/RamsesHandles.h" @@ -17,25 +18,54 @@ namespace raco::ramses_adaptor { RenderTargetAdaptor::RenderTargetAdaptor(SceneAdaptor* sceneAdaptor, std::shared_ptr editorObject) : TypedObjectAdaptor(sceneAdaptor, editorObject, {}), - subscriptions_{sceneAdaptor->dispatcher()->registerOn(core::ValueHandle{editorObject, &user_types::RenderTarget::buffer0_}, [this]() { - tagDirty(); - }), + subscriptions_{sceneAdaptor->dispatcher()->registerOn(core::ValueHandle{editorObject, &user_types::RenderTarget::buffer0_}, [this]() { tagDirty(); }), sceneAdaptor->dispatcher()->registerOn(core::ValueHandle{editorObject, &user_types::RenderTarget::buffer1_}, [this]() { tagDirty(); }), sceneAdaptor->dispatcher()->registerOn(core::ValueHandle{editorObject, &user_types::RenderTarget::buffer2_}, [this]() { tagDirty(); }), sceneAdaptor->dispatcher()->registerOn(core::ValueHandle{editorObject, &user_types::RenderTarget::buffer3_}, [this]() { tagDirty(); }), sceneAdaptor->dispatcher()->registerOn(core::ValueHandle{editorObject, &user_types::RenderTarget::buffer4_}, [this]() { tagDirty(); }), sceneAdaptor->dispatcher()->registerOn(core::ValueHandle{editorObject, &user_types::RenderTarget::buffer5_}, [this]() { tagDirty(); }), sceneAdaptor->dispatcher()->registerOn(core::ValueHandle{editorObject, &user_types::RenderTarget::buffer6_}, [this]() { tagDirty(); }), - sceneAdaptor->dispatcher()->registerOn(core::ValueHandle{editorObject, &user_types::RenderTarget::buffer7_}, [this]() { tagDirty(); })} { + sceneAdaptor->dispatcher()->registerOn(core::ValueHandle{editorObject, &user_types::RenderTarget::buffer7_}, [this]() { tagDirty(); }), + sceneAdaptor->dispatcher()->registerOn(core::ValueHandle{editorObject, &user_types::RenderTarget::bufferMS0_}, [this]() { tagDirty(); }), + sceneAdaptor->dispatcher()->registerOn(core::ValueHandle{editorObject, &user_types::RenderTarget::bufferMS1_}, [this]() { tagDirty(); }), + sceneAdaptor->dispatcher()->registerOn(core::ValueHandle{editorObject, &user_types::RenderTarget::bufferMS2_}, [this]() { tagDirty(); }), + sceneAdaptor->dispatcher()->registerOn(core::ValueHandle{editorObject, &user_types::RenderTarget::bufferMS3_}, [this]() { tagDirty(); }), + sceneAdaptor->dispatcher()->registerOn(core::ValueHandle{editorObject, &user_types::RenderTarget::bufferMS4_}, [this]() { tagDirty(); }), + sceneAdaptor->dispatcher()->registerOn(core::ValueHandle{editorObject, &user_types::RenderTarget::bufferMS5_}, [this]() { tagDirty(); }), + sceneAdaptor->dispatcher()->registerOn(core::ValueHandle{editorObject, &user_types::RenderTarget::bufferMS6_}, [this]() { tagDirty(); }), + sceneAdaptor->dispatcher()->registerOn(core::ValueHandle{editorObject, &user_types::RenderTarget::bufferMS7_}, [this]() { tagDirty(); })} { +} + +template +bool RenderTargetAdaptor::collectBuffers(std::vector& buffers, const std::initializer_list& userTypeBuffers, ramses::RenderTargetDescription& rtDesc, core::Errors* errors) { + bool hasEmptySlots = false; + for (int bufferSlotIndex = 0; bufferSlotIndex < userTypeBuffers.size(); ++bufferSlotIndex) { + const auto& buffer = userTypeBuffers.begin()[bufferSlotIndex]; + if (auto adaptor = sceneAdaptor_->lookup(buffer)) { + if (auto ramsesBuffer = adaptor->buffer()) { + auto status = rtDesc.addRenderBuffer(*ramsesBuffer); + if (status != ramses::StatusOK) { + LOG_ERROR(raco::log_system::RAMSES_ADAPTOR, rtDesc.getStatusMessage(status)); + errors->addError(core::ErrorCategory::PARSING, core::ErrorLevel::ERROR, {editorObject()->shared_from_this()}, + rtDesc.getStatusMessage(status)); + } else { + hasEmptySlots = buffers.size() != bufferSlotIndex; + buffers.emplace_back(ramsesBuffer); + } + } + } + } + return hasEmptySlots; } bool RenderTargetAdaptor::sync(core::Errors* errors) { errors->removeError({editorObject()}); std::vector buffers; + std::vector buffersMS; ramses::RenderTargetDescription rtDesc; - auto usertypebuffers = { + std::initializer_list usertypebuffers = { *editorObject().get()->user_types::RenderTarget::buffer0_, *editorObject().get()->user_types::RenderTarget::buffer1_, *editorObject().get()->user_types::RenderTarget::buffer2_, @@ -44,6 +74,17 @@ bool RenderTargetAdaptor::sync(core::Errors* errors) { *editorObject().get()->user_types::RenderTarget::buffer5_, *editorObject().get()->user_types::RenderTarget::buffer6_, *editorObject().get()->user_types::RenderTarget::buffer7_}; + + std::initializer_list usertypebuffersMS = { + *editorObject().get()->user_types::RenderTarget::bufferMS0_, + *editorObject().get()->user_types::RenderTarget::bufferMS1_, + *editorObject().get()->user_types::RenderTarget::bufferMS2_, + *editorObject().get()->user_types::RenderTarget::bufferMS3_, + *editorObject().get()->user_types::RenderTarget::bufferMS4_, + *editorObject().get()->user_types::RenderTarget::bufferMS5_, + *editorObject().get()->user_types::RenderTarget::bufferMS6_, + *editorObject().get()->user_types::RenderTarget::bufferMS7_}; + // We cannot have any empty slots before color buffers in Ramses - // the RenderTargetDescription::addRenderBuffer does not allow for it. // But if we only add valid render buffers to the list of render buffers, @@ -51,37 +92,34 @@ bool RenderTargetAdaptor::sync(core::Errors* errors) { // (which is not what the shader will expect). // This is a Ramses bug - see https://github.com/bmwcarit/ramses/issues/52 // If that occurs, refuse to create the render target to avoid any surprises for the user. - bool hasEmptySlots = false; - for (int bufferSlotIndex = 0; bufferSlotIndex < usertypebuffers.size(); ++bufferSlotIndex) { - const auto& buffer = usertypebuffers.begin()[bufferSlotIndex]; - if (auto adaptor = sceneAdaptor_->lookup(buffer)) { - if (auto ramsesBuffer = adaptor->buffer()) { - auto status = rtDesc.addRenderBuffer(*ramsesBuffer); - if (status != ramses::StatusOK) { - LOG_ERROR(raco::log_system::RAMSES_ADAPTOR, rtDesc.getStatusMessage(status)); - errors->addError(core::ErrorCategory::PARSING, core::ErrorLevel::ERROR, {editorObject()->shared_from_this()}, - rtDesc.getStatusMessage(status)); - } else { - hasEmptySlots = buffers.size() != bufferSlotIndex; - buffers.emplace_back(ramsesBuffer); - } - } - } + + bool hasEmptySlots = collectBuffers(buffers, usertypebuffers, rtDesc, errors) || + collectBuffers(buffersMS, usertypebuffersMS, rtDesc, errors); + + if (!buffers.empty() && !buffersMS.empty()) { + auto errorMsg = fmt::format("Cannot create render target '{}' - all buffers need to be either single-sample or multi-sample only.", editorObject()->objectName()); + reset(nullptr); + LOG_ERROR(raco::log_system::RAMSES_ADAPTOR, errorMsg); + errors->addError(core::ErrorCategory::GENERAL, core::ErrorLevel::ERROR, {editorObject()}, errorMsg); + + tagDirty(false); + return true; } + buffers.insert(buffers.end(), buffersMS.begin(), buffersMS.end()); + if (!buffers.empty() && !hasEmptySlots) { reset(ramses_base::ramsesRenderTarget(sceneAdaptor_->scene(), rtDesc, buffers)); } else if (!errors->hasError({editorObject()})) { std::string errorMsg; if (buffers.empty()) { errorMsg = fmt::format("Cannot create render target '{}' - its first buffer is not set or not valid.", editorObject()->objectName()); - } - else { + } else { errorMsg = fmt::format("Cannot create render target '{}' - all buffers in it must be consecutive and valid buffers.", editorObject()->objectName()); } reset(nullptr); LOG_ERROR(raco::log_system::RAMSES_ADAPTOR, errorMsg); - errors->addError(core::ErrorCategory::PARSING, core::ErrorLevel::ERROR, {editorObject()}, errorMsg); + errors->addError(core::ErrorCategory::GENERAL, core::ErrorLevel::ERROR, {editorObject()}, errorMsg); } tagDirty(false); diff --git a/components/libRamsesBase/src/ramses_adaptor/SceneAdaptor.cpp b/components/libRamsesBase/src/ramses_adaptor/SceneAdaptor.cpp index d7f112b7..40b86d2a 100644 --- a/components/libRamsesBase/src/ramses_adaptor/SceneAdaptor.cpp +++ b/components/libRamsesBase/src/ramses_adaptor/SceneAdaptor.cpp @@ -25,6 +25,7 @@ #include "ramses_adaptor/TimerAdaptor.h" #include "ramses_base/RamsesHandles.h" #include "user_types/Animation.h" +#include "user_types/BlitPass.h" #include "user_types/MeshNode.h" #include "user_types/Prefab.h" #include "user_types/RenderPass.h" @@ -463,19 +464,21 @@ void SceneAdaptor::performBulkEngineUpdate(const core::SEditorObjectSet& changed rebuildSortedDependencyGraph(SEditorObjectSet(project_->instances().begin(), project_->instances().end())); // Check if all render passes have a unique order index, otherwise Ramses renders them in arbitrary order. errors_->removeIf([](core::ErrorItem const& error) { - return error.valueHandle().isRefToProp(&user_types::RenderPass::renderOrder_); + return error.valueHandle().isRefToProp(&user_types::RenderPass::renderOrder_) || error.valueHandle().isRefToProp(&user_types::BlitPass::renderOrder_); }); - auto renderPasses = core::Queries::filterByTypeName(project_->instances(), {user_types::RenderPass::typeDescription.typeName}); - std::map> orderIndices; - for (auto const& rpObj : renderPasses) { - auto rp = rpObj->as(); - orderIndices[rp->renderOrder_.asInt()].emplace_back(rp); + + std::map> orderIndices; + for (auto const& obj : project_->instances()) { + if (obj->isType() || obj->isType()) { + int order = obj->get("renderOrder")->asInt(); + orderIndices[order].emplace_back(obj); + } } for (auto const& oi : orderIndices) { if (oi.second.size() > 1) { - auto errorMsg = fmt::format("The render passes {} have the same order index and will be rendered in arbitrary order.", oi.second); - for (auto const& rp : oi.second) { - errors_->addError(core::ErrorCategory::GENERAL, core::ErrorLevel::WARNING, ValueHandle{rp, &user_types::RenderPass::renderOrder_}, errorMsg); + auto errorMsg = fmt::format("The render/blit passes {} have the same order index and will be rendered in arbitrary order.", oi.second); + for (auto const& obj : oi.second) { + errors_->addError(core::ErrorCategory::GENERAL, core::ErrorLevel::WARNING, ValueHandle{obj, {"renderOrder"}}, errorMsg); } } } diff --git a/components/libRamsesBase/src/ramses_adaptor/SceneBackend.cpp b/components/libRamsesBase/src/ramses_adaptor/SceneBackend.cpp index fc07d900..c0a6c95b 100644 --- a/components/libRamsesBase/src/ramses_adaptor/SceneBackend.cpp +++ b/components/libRamsesBase/src/ramses_adaptor/SceneBackend.cpp @@ -78,19 +78,62 @@ void SceneBackend::readDataFromEngine(core::DataChangeRecorder& recorder) { } } +std::vector SceneBackend::logicEngineFilteredValidation() const { + std::vector filteredWarnings; + std::vector warnings = logicEngine()->validate(); -bool SceneBackend::sceneValid() const { - return currentScene()->validate() == ramses::StatusOK && logicEngine()->validate().empty(); + for (auto& warning : warnings) { + if (warning.message.find("has no ingoing links! Node should be deleted or properly linked!") != std::string::npos) { + continue; + } + if (warning.message.find("has no outgoing links! Node should be deleted or properly linked!") != std::string::npos) { + continue; + } + filteredWarnings.emplace_back(warning); + } + return filteredWarnings; +} + + +std::string SceneBackend::ramsesFilteredValidationReport(core::ErrorLevel minLevel) const { + auto report = currentScene()->getValidationReport(minLevel == core::ErrorLevel::ERROR ? ramses::EValidationSeverity_Error : ramses::EValidationSeverity_Warning); + + std::istringstream stream(report); + std::string line; + std::string filtered; + while (std::getline(stream, line)) { + if (line.find("rendergroup does not contain any meshes") != std::string::npos) { + continue; + } + filtered.append(line).append("\n"); + } + return filtered; +} + +core::ErrorLevel SceneBackend::sceneValid() const { + auto status = currentScene()->validate(); + if (status != ramses::StatusOK) { + std::string msg = currentScene()->getStatusMessage(status); + if (msg == "Validation error" && !ramsesFilteredValidationReport(core::ErrorLevel::ERROR).empty()) { + return core::ErrorLevel::ERROR; + } else if (msg == "Validation warning" && !ramsesFilteredValidationReport(core::ErrorLevel::WARNING).empty()) { + return core::ErrorLevel::WARNING; + } + } + if (!logicEngineFilteredValidation().empty()) { + return core::ErrorLevel::ERROR; + } + return core::ErrorLevel::NONE; } std::string SceneBackend::getValidationReport(core::ErrorLevel minLevel) const { - auto logicErrors = logicEngine()->validate(); + auto logicErrors = logicEngineFilteredValidation(); std::string logicErrorMessages; for (const auto& logicError : logicErrors) { logicErrorMessages.append(logicError.message).append("\n"); } - return currentScene()->getValidationReport(minLevel == core::ErrorLevel::ERROR ? ramses::EValidationSeverity_Error : ramses::EValidationSeverity_Warning) + logicErrorMessages; + return ramsesFilteredValidationReport(minLevel) + logicErrorMessages; } uint64_t SceneBackend::currentSceneIdValue() const { @@ -211,6 +254,11 @@ std::vector SceneBackend::getSceneItemDescriptions( sceneItems.emplace_back("AnchorPoint", anchorPoint->getName().data(), -1); } + for (const auto* binding : logicEngine()->getCollection()) { + auto parentIdx = parents[&binding->getRamsesRenderGroup()]; + sceneItems.emplace_back("RenderGroupBinding", binding->getName().data(), parentIdx); + } + return sceneItems; } diff --git a/components/libRamsesBase/src/ramses_base/BaseEngineBackend.cpp b/components/libRamsesBase/src/ramses_base/BaseEngineBackend.cpp index 2789d97b..65ced06c 100644 --- a/components/libRamsesBase/src/ramses_base/BaseEngineBackend.cpp +++ b/components/libRamsesBase/src/ramses_base/BaseEngineBackend.cpp @@ -20,7 +20,9 @@ R"(1 - LogicEngine v1.0.3 - Added AnchorPoint - Added RamsesRenderPassBinding - Added 'enabled' input property to RamsesNodeBinding - - Added createRamsesCameraBindingWithFrustumPlanes + - Added createRamsesCameraBindingWithFrustumPlanes +3 - LogicEngine v1.2.0 + - Added RenderGroupBinding )"; BaseEngineBackend::BaseEngineBackend( diff --git a/components/libRamsesBase/src/ramses_base/Utils.cpp b/components/libRamsesBase/src/ramses_base/Utils.cpp index 3a53ef76..78578f37 100644 --- a/components/libRamsesBase/src/ramses_base/Utils.cpp +++ b/components/libRamsesBase/src/ramses_base/Utils.cpp @@ -17,6 +17,7 @@ #include "user_types/LuaScriptModule.h" #include "utils/FileUtils.h" #include "utils/MathUtils.h" +#include "core/CoreFormatter.h" #include #include @@ -173,6 +174,7 @@ static std::map shade //{ramses::EEffectInputDataType_Matrix44F, }, {ramses::EEffectInputDataType_TextureSampler2D, raco::core::EnginePrimitive::TextureSampler2D}, + {ramses::EEffectInputDataType_TextureSampler2DMS, raco::core::EnginePrimitive::TextureSampler2DMS}, {ramses::EEffectInputDataType_TextureSampler3D, raco::core::EnginePrimitive::TextureSampler3D}, {ramses::EEffectInputDataType_TextureSamplerCube, raco::core::EnginePrimitive::TextureSamplerCube}}; @@ -216,9 +218,21 @@ bool parseShaderText(ramses::Scene &scene, const std::string &vertexShader, cons effect->getUniformInput(i, uniform); if (uniform.getSemantics() == ramses::EEffectUniformSemantic::Invalid) { if (shaderTypeMap.find(uniform.getDataType()) != shaderTypeMap.end()) { - outUniforms.emplace_back(std::string{uniform.getName()}, shaderTypeMap[uniform.getDataType()]); + if (uniform.getElementCount() > 1) { + auto engineType = shaderTypeMap[uniform.getDataType()]; + if (engineType >= core::EnginePrimitive::Int32 && engineType <= core::EnginePrimitive::Vec4i) { + auto &item = outUniforms.emplace_back(std::string{uniform.getName()}, raco::core::EnginePrimitive::Array); + for (int index = 0; index < uniform.getElementCount(); index++) { + item.children.emplace_back(std::string(), shaderTypeMap[uniform.getDataType()]); + } + } else { + outError += fmt::format("Uniform '{}' has unsupported array element type '{}'", uniform.getName(), engineType); + } + } else { + outUniforms.emplace_back(std::string{uniform.getName()}, shaderTypeMap[uniform.getDataType()]); + } } else { - outError += std::string(uniform.getName()) + " has unsupported type "; + outError += std::string(uniform.getName()) + " has unsupported type"; } } } @@ -455,7 +469,7 @@ PngCompatibilityInfo validateTextureColorTypeAndBitDepth(ramses::ETextureFormat if (downConvertableTextureFormat != downConvertableTextureFormats[pngFormat].end()) { return {fmt::format("Selected format {} is not equal to PNG color type {} - image will be converted.", ramsesTextureFormatToString(selectedTextureFormat), pngColorTypeToString(colorType)), raco::core::ErrorLevel::INFORMATION, true}; } - return {fmt::format("[Deprecated Warning - this will be an error in a future version] Selected format {} is not equal to PNG color type {} - empty channels will be created.", ramsesTextureFormatToString(selectedTextureFormat), pngColorTypeToString(colorType)), raco::core::ErrorLevel::WARNING, true}; + return {fmt::format("Selected format {} is not equal to PNG color type {} - empty channels will be created.", ramsesTextureFormatToString(selectedTextureFormat), pngColorTypeToString(colorType)), raco::core::ErrorLevel::WARNING, true}; } std::string ramsesTextureFormatToString(ramses::ETextureFormat textureFormat) { @@ -601,4 +615,40 @@ std::vector decodeMipMapData(core::Errors *errors, core::Project return data; } +int clipAndCheckIntProperty(const raco::core::ValueHandle value, core::Errors *errors, bool *allValid) { + auto range = value.constValueRef()->query>(); + int clippedValue = std::min(std::max(*range->min_, value.asInt()), *range->max_); + + if (clippedValue != value.asInt()) { + errors->addError(core::ErrorCategory::GENERAL, core::ErrorLevel::ERROR, value, + fmt::format("'{}' property outside valid range", value.getPropName())); + *allValid = false; + } else { + errors->removeError(value); + } + return clippedValue; +} + +ramses::ERenderBufferType ramsesRenderBufferTypeFromFormat(ramses::ERenderBufferFormat format) { + using namespace ramses; + std::map bufferTypeFromFormat = { + {ERenderBufferFormat_RGBA4, ERenderBufferType_Color}, + {ERenderBufferFormat_R8, ERenderBufferType_Color}, + {ERenderBufferFormat_RG8, ERenderBufferType_Color}, + {ERenderBufferFormat_RGB8, ERenderBufferType_Color}, + {ERenderBufferFormat_RGBA8, ERenderBufferType_Color}, + {ERenderBufferFormat_R16F, ERenderBufferType_Color}, + {ERenderBufferFormat_R32F, ERenderBufferType_Color}, + {ERenderBufferFormat_RG16F, ERenderBufferType_Color}, + {ERenderBufferFormat_RG32F, ERenderBufferType_Color}, + {ERenderBufferFormat_RGB16F, ERenderBufferType_Color}, + {ERenderBufferFormat_RGB32F, ERenderBufferType_Color}, + {ERenderBufferFormat_RGBA16F, ERenderBufferType_Color}, + {ERenderBufferFormat_RGBA32F, ERenderBufferType_Color}, + {ERenderBufferFormat_Depth24, ERenderBufferType_Depth}, + {ERenderBufferFormat_Depth24_Stencil8, ERenderBufferType_DepthStencil}}; + + return bufferTypeFromFormat.at(format); +} + } // namespace raco::ramses_base diff --git a/components/libRamsesBase/tests/BlitPassAdaptor_test.cpp b/components/libRamsesBase/tests/BlitPassAdaptor_test.cpp new file mode 100644 index 00000000..65f46ddb --- /dev/null +++ b/components/libRamsesBase/tests/BlitPassAdaptor_test.cpp @@ -0,0 +1,150 @@ +/* + * SPDX-License-Identifier: MPL-2.0 + * + * This file is part of Ramses Composer + * (see https://github.com/bmwcarit/ramses-composer). + * + * This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. + * If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +#include + +#include "RamsesBaseFixture.h" +#include "ramses_adaptor/BlitPassAdaptor.h" +#include "user_types/RenderBuffer.h" +#include "user_types/RenderBufferMS.h" + +using namespace raco::user_types; + +class BlitPassAdaptorTest : public RamsesBaseFixture<> {}; + +TEST_F(BlitPassAdaptorTest, defaultConstruction) { + auto blitPass = context.createObject(BlitPass::typeDescription.typeName, "BlitPass"); + + dispatch(); + + auto blitPasses{select(*sceneContext.scene(), ramses::ERamsesObjectType::ERamsesObjectType_BlitPass)}; + ASSERT_EQ(blitPasses.size(), 0); +} + +TEST_F(BlitPassAdaptorTest, validBlitPass_BlitPassObj_sourceSingleSample_targetSingleSample) { + auto blitPass = context.createObject(BlitPass::typeDescription.typeName, "BlitPass"); + auto sourceRenderBuffer = context.createObject(RenderBuffer::typeDescription.typeName, "RenderBuffer"); + auto targetRenderBuffer = context.createObject(RenderBuffer::typeDescription.typeName, "RenderBuffer"); + dispatch(); + + context.set({blitPass, &raco::user_types::BlitPass::sourceRenderBuffer_}, sourceRenderBuffer); + context.set({blitPass, &raco::user_types::BlitPass::targetRenderBuffer_}, targetRenderBuffer); + dispatch(); + + auto blitPasses{select(*sceneContext.scene(), ramses::ERamsesObjectType::ERamsesObjectType_BlitPass)}; + ASSERT_EQ(blitPasses.size(), 1); +} + +TEST_F(BlitPassAdaptorTest, validBlitPass_BlitPassObj_sourceMultiSample_targetMultiSample) { + auto blitPass = context.createObject(BlitPass::typeDescription.typeName, "BlitPass"); + auto sourceRenderBuffer = context.createObject(RenderBufferMS::typeDescription.typeName, "RenderBufferMS"); + auto targetRenderBuffer = context.createObject(RenderBufferMS::typeDescription.typeName, "RenderBufferMS"); + dispatch(); + + context.set({blitPass, &raco::user_types::BlitPass::sourceRenderBufferMS_}, sourceRenderBuffer); + context.set({blitPass, &raco::user_types::BlitPass::targetRenderBufferMS_}, targetRenderBuffer); + dispatch(); + + auto blitPasses{select(*sceneContext.scene(), ramses::ERamsesObjectType::ERamsesObjectType_BlitPass)}; + ASSERT_EQ(blitPasses.size(), 1); +} + +TEST_F(BlitPassAdaptorTest, validBlitPass_BlitPassObj_sourceSingleSample_targetMultiSample) { + auto blitPass = context.createObject(BlitPass::typeDescription.typeName, "BlitPass"); + auto sourceRenderBuffer = context.createObject(RenderBuffer::typeDescription.typeName, "RenderBuffer"); + auto targetRenderBuffer = context.createObject(RenderBufferMS::typeDescription.typeName, "RenderBufferMS"); + dispatch(); + + context.set({blitPass, &raco::user_types::BlitPass::sourceRenderBuffer_}, sourceRenderBuffer); + context.set({blitPass, &raco::user_types::BlitPass::targetRenderBufferMS_}, targetRenderBuffer); + dispatch(); + + auto blitPasses{select(*sceneContext.scene(), ramses::ERamsesObjectType::ERamsesObjectType_BlitPass)}; + ASSERT_EQ(blitPasses.size(), 1); +} + +TEST_F(BlitPassAdaptorTest, validBlitPass_BlitPassObj_sourceMultiSample_targetSingleSample) { + auto blitPass = context.createObject(BlitPass::typeDescription.typeName, "BlitPass"); + auto sourceRenderBuffer = context.createObject(RenderBufferMS::typeDescription.typeName, "RenderBufferMS"); + auto targetRenderBuffer = context.createObject(RenderBuffer::typeDescription.typeName, "RenderBuffer"); + dispatch(); + + context.set({blitPass, &raco::user_types::BlitPass::sourceRenderBufferMS_}, sourceRenderBuffer); + context.set({blitPass, &raco::user_types::BlitPass::targetRenderBuffer_}, targetRenderBuffer); + dispatch(); + + auto blitPasses{select(*sceneContext.scene(), ramses::ERamsesObjectType::ERamsesObjectType_BlitPass)}; + ASSERT_EQ(blitPasses.size(), 1); +} + +TEST_F(BlitPassAdaptorTest, validBlitPass_BlitPassObj_all_refs_filled) { + auto blitPass = context.createObject(BlitPass::typeDescription.typeName, "BlitPass"); + auto sourceRenderBuffer = context.createObject(RenderBuffer::typeDescription.typeName, "RenderBuffer"); + auto targetRenderBuffer = context.createObject(RenderBuffer::typeDescription.typeName, "RenderBuffer"); + auto sourceRenderBufferMS = context.createObject(RenderBufferMS::typeDescription.typeName, "RenderBufferMS"); + auto targetRenderBufferMS = context.createObject(RenderBufferMS::typeDescription.typeName, "RenderBufferMS"); + dispatch(); + + context.set({blitPass, &raco::user_types::BlitPass::sourceRenderBuffer_}, sourceRenderBuffer); + context.set({blitPass, &raco::user_types::BlitPass::targetRenderBuffer_}, targetRenderBuffer); + context.set({blitPass, &raco::user_types::BlitPass::sourceRenderBufferMS_}, sourceRenderBufferMS); + context.set({blitPass, &raco::user_types::BlitPass::targetRenderBufferMS_}, targetRenderBufferMS); + dispatch(); + + auto blitPasses{select(*sceneContext.scene(), ramses::ERamsesObjectType::ERamsesObjectType_BlitPass)}; + ASSERT_EQ(blitPasses.size(), 1); +} + +TEST_F(BlitPassAdaptorTest, validBlitPass_BlitPassObj_invalidate) { + auto blitPass = context.createObject(BlitPass::typeDescription.typeName, "BlitPass"); + auto sourceRenderBuffer = context.createObject(RenderBuffer::typeDescription.typeName, "RenderBuffer"); + auto targetRenderBuffer = context.createObject(RenderBuffer::typeDescription.typeName, "RenderBuffer"); + dispatch(); + + context.set({blitPass, &raco::user_types::BlitPass::sourceRenderBuffer_}, sourceRenderBuffer); + context.set({blitPass, &raco::user_types::BlitPass::targetRenderBuffer_}, targetRenderBuffer); + dispatch(); + + context.set({blitPass, &raco::user_types::BlitPass::width_}, 600); + dispatch(); + + auto blitPasses{select(*sceneContext.scene(), ramses::ERamsesObjectType::ERamsesObjectType_BlitPass)}; + ASSERT_EQ(blitPasses.size(), 1); + ASSERT_TRUE(context.errors().hasError(blitPass)); + + context.set({blitPass, &raco::user_types::BlitPass::width_}, 256); + dispatch(); + + blitPasses = select(*sceneContext.scene(), ramses::ERamsesObjectType::ERamsesObjectType_BlitPass); + ASSERT_EQ(blitPasses.size(), 1); + ASSERT_FALSE(context.errors().hasError(blitPass)); +} + +TEST_F(BlitPassAdaptorTest, validBlitPass_nameChange) { + auto blitPass = context.createObject(BlitPass::typeDescription.typeName, "MyBlitPass"); + auto sourceRenderBuffer = context.createObject(RenderBuffer::typeDescription.typeName, "RenderBuffer"); + auto targetRenderBuffer = context.createObject(RenderBuffer::typeDescription.typeName, "RenderBuffer"); + dispatch(); + + context.set({blitPass, &raco::user_types::BlitPass::sourceRenderBuffer_}, sourceRenderBuffer); + context.set({blitPass, &raco::user_types::BlitPass::targetRenderBuffer_}, targetRenderBuffer); + dispatch(); + + auto blitPasses{select(*sceneContext.scene(), ramses::ERamsesObjectType::ERamsesObjectType_BlitPass)}; + ASSERT_EQ(blitPasses.size(), 1); + ASSERT_STREQ(blitPasses.front()->getName(), "MyBlitPass"); + + context.set({blitPass, &raco::user_types::BlitPass::objectName_}, std::string("BlitPassRenamed")); + dispatch(); + + blitPasses = select(*sceneContext.scene(), ramses::ERamsesObjectType::ERamsesObjectType_BlitPass); + ASSERT_EQ(blitPasses.size(), 1); + ASSERT_STREQ(blitPasses.front()->getName(), "BlitPassRenamed"); + +} \ No newline at end of file diff --git a/components/libRamsesBase/tests/CMakeLists.txt b/components/libRamsesBase/tests/CMakeLists.txt index 8e02fb66..f181ed48 100644 --- a/components/libRamsesBase/tests/CMakeLists.txt +++ b/components/libRamsesBase/tests/CMakeLists.txt @@ -13,6 +13,7 @@ If a copy of the MPL was not distributed with this file, You can obtain one at h set(TEST_SOURCES AnimationAdaptor_test.cpp AnimationChannelAdaptor_test.cpp + BlitPassAdaptor_test.cpp CubeMapAdaptor_test.cpp EngineInterface_test.cpp RamsesBaseFixture.h @@ -67,6 +68,12 @@ raco_package_add_test_resources( shaders/basic.vert shaders/simple_texture.frag shaders/simple_texture.vert + shaders/multisampler.frag + shaders/multisampler.vert + shaders/uniform-array.vert + shaders/uniform-array.frag + shaders/uniform-scalar.vert + shaders/uniform-scalar.frag meshes/Duck.glb meshes/meshrefless.gltf meshes/meshless.gltf diff --git a/components/libRamsesBase/tests/MaterialAdaptor_test.cpp b/components/libRamsesBase/tests/MaterialAdaptor_test.cpp index d4a02e69..6b2611dd 100644 --- a/components/libRamsesBase/tests/MaterialAdaptor_test.cpp +++ b/components/libRamsesBase/tests/MaterialAdaptor_test.cpp @@ -38,3 +38,259 @@ TEST_F(MaterialAdaptorTest, context_scene_effect_name_change) { EXPECT_EQ(appearances.size(), 1); ASSERT_TRUE(isRamsesNameInArray("Changed_Appearance", appearances)); } + +TEST_F(MaterialAdaptorTest, set_get_scalar_uniforms) { + auto material = create_material("mat", "shaders/uniform-scalar.vert", "shaders/uniform-scalar.frag"); + + commandInterface.set({material, {"uniforms", "i"}}, 2); + commandInterface.set({material, {"uniforms", "f"}}, 3.0); + + commandInterface.set({material, {"uniforms", "v2", "y"}}, 4.0); + commandInterface.set({material, {"uniforms", "v3", "z"}}, 5.0); + commandInterface.set({material, {"uniforms", "v4", "w"}}, 6.0); + + commandInterface.set({material, {"uniforms", "iv2", "i1"}}, 7); + commandInterface.set({material, {"uniforms", "iv3", "i2"}}, 8); + commandInterface.set({material, {"uniforms", "iv4", "i3"}}, 9); + + dispatch(); + + auto appearance{select(*sceneContext.scene(), "mat_Appearance")}; + + ramses::UniformInput input; + + int32_t ivalue; + appearance->getEffect().findUniformInput("i", input); + appearance->getInputValueInt32(input, ivalue); + EXPECT_EQ(ivalue, 2); + + float fvalue; + appearance->getEffect().findUniformInput("f", input); + appearance->getInputValueFloat(input, fvalue); + EXPECT_EQ(fvalue, 3.0); + + std::array value2f; + appearance->getEffect().findUniformInput("v2", input); + appearance->getInputValueVector2f(input, value2f[0], value2f[1]); + EXPECT_EQ(value2f[1], 4.0); + + std::array value3f; + appearance->getEffect().findUniformInput("v3", input); + appearance->getInputValueVector3f(input, value3f[0], value3f[1], value3f[2]); + EXPECT_EQ(value3f[2], 5.0); + + std::array value4f; + appearance->getEffect().findUniformInput("v4", input); + appearance->getInputValueVector4f(input, value4f[0], value4f[1], value4f[2], value4f[3]); + EXPECT_EQ(value4f[3], 6.0); + + std::array value2i; + appearance->getEffect().findUniformInput("iv2", input); + appearance->getInputValueVector2i(input, value2i[0], value2i[1]); + EXPECT_EQ(value2i[0], 7); + + std::array value3i; + appearance->getEffect().findUniformInput("iv3", input); + appearance->getInputValueVector3i(input, value3i[0], value3i[1], value3i[2]); + EXPECT_EQ(value3i[1], 8); + + std::array value4i; + appearance->getEffect().findUniformInput("iv4", input); + appearance->getInputValueVector4i(input, value4i[0], value4i[1], value4i[2], value4i[3]); + EXPECT_EQ(value4i[2], 9); +} + +TEST_F(MaterialAdaptorTest, link_get_scalar_uniforms) { + auto material = create_material("mat", "shaders/uniform-scalar.vert", "shaders/uniform-scalar.frag"); + auto lua = create_lua("lua", "scripts/types-scalar.lua"); + + link(lua, {"outputs", "ointeger"}, material, {"uniforms", "i"}); + link(lua, {"outputs", "ofloat"}, material, {"uniforms", "f"}); + + link(lua, {"outputs", "ovector2f"}, material, {"uniforms", "v2"}); + link(lua, {"outputs", "ovector3f"}, material, {"uniforms", "v3"}); + link(lua, {"outputs", "ovector4f"}, material, {"uniforms", "v4"}); + + link(lua, {"outputs", "ovector2i"}, material, {"uniforms", "iv2"}); + link(lua, {"outputs", "ovector3i"}, material, {"uniforms", "iv3"}); + link(lua, {"outputs", "ovector4i"}, material, {"uniforms", "iv4"}); + + dispatch(); + + commandInterface.set({lua, {"inputs", "integer"}}, 7); + commandInterface.set({lua, {"inputs", "float"}}, 9.0); + + dispatch(); + + auto appearance{select(*sceneContext.scene(), "mat_Appearance")}; + + ramses::UniformInput input; + + int32_t ivalue; + appearance->getEffect().findUniformInput("i", input); + appearance->getInputValueInt32(input, ivalue); + EXPECT_EQ(ivalue, 14); + + float fvalue; + appearance->getEffect().findUniformInput("f", input); + appearance->getInputValueFloat(input, fvalue); + EXPECT_EQ(fvalue, 9.0); + + std::array value2f; + appearance->getEffect().findUniformInput("v2", input); + appearance->getInputValueVector2f(input, value2f[0], value2f[1]); + EXPECT_EQ(value2f[1], 18.0); + + std::array value3f; + appearance->getEffect().findUniformInput("v3", input); + appearance->getInputValueVector3f(input, value3f[0], value3f[1], value3f[2]); + EXPECT_EQ(value3f[2], 27.0); + + std::array value4f; + appearance->getEffect().findUniformInput("v4", input); + appearance->getInputValueVector4f(input, value4f[0], value4f[1], value4f[2], value4f[3]); + EXPECT_EQ(value4f[3], 36.0); + + std::array value2i; + appearance->getEffect().findUniformInput("iv2", input); + appearance->getInputValueVector2i(input, value2i[0], value2i[1]); + EXPECT_EQ(value2i[0], 7); + + std::array value3i; + appearance->getEffect().findUniformInput("iv3", input); + appearance->getInputValueVector3i(input, value3i[0], value3i[1], value3i[2]); + EXPECT_EQ(value3i[1], 14); + + std::array value4i; + appearance->getEffect().findUniformInput("iv4", input); + appearance->getInputValueVector4i(input, value4i[0], value4i[1], value4i[2], value4i[3]); + EXPECT_EQ(value4i[2], 21); +} + +TEST_F(MaterialAdaptorTest, set_get_array_uniforms) { + auto material = create_material("mat", "shaders/uniform-array.vert", "shaders/uniform-array.frag"); + + commandInterface.set({material, {"uniforms", "ivec", "2"}}, 2); + commandInterface.set({material, {"uniforms", "fvec", "3"}}, 3.0); + + commandInterface.set({material, {"uniforms", "avec2", "3", "y"}}, 4.0); + commandInterface.set({material, {"uniforms", "avec3", "3", "y"}}, 5.0); + commandInterface.set({material, {"uniforms", "avec4", "3", "y"}}, 6.0); + + commandInterface.set({material, {"uniforms", "aivec2", "3", "i1"}}, 7); + commandInterface.set({material, {"uniforms", "aivec3", "3", "i1"}}, 8); + commandInterface.set({material, {"uniforms", "aivec4", "3", "i1"}}, 9); + + dispatch(); + + auto appearance{select(*sceneContext.scene(), "mat_Appearance")}; + + ramses::UniformInput input; + + std::array ivalues; + appearance->getEffect().findUniformInput("ivec", input); + appearance->getInputValueInt32(input, 2, ivalues.data()); + EXPECT_EQ(ivalues[1], 2); + + std::array fvalues; + appearance->getEffect().findUniformInput("fvec", input); + appearance->getInputValueFloat(input, 5, fvalues.data()); + EXPECT_EQ(fvalues[2], 3.0); + + std::array a2fvalues; + appearance->getEffect().findUniformInput("avec2", input); + appearance->getInputValueVector2f(input, 4, a2fvalues.data()); + EXPECT_EQ(a2fvalues[5], 4.0); + + std::array a3fvalues; + appearance->getEffect().findUniformInput("avec3", input); + appearance->getInputValueVector3f(input, 5, a3fvalues.data()); + EXPECT_EQ(a3fvalues[7], 5.0); + + std::array a4fvalues; + appearance->getEffect().findUniformInput("avec4", input); + appearance->getInputValueVector4f(input, 6, a4fvalues.data()); + EXPECT_EQ(a4fvalues[9], 6.0); + + std::array a2ivalues; + appearance->getEffect().findUniformInput("aivec2", input); + appearance->getInputValueVector2i(input, 4, a2ivalues.data()); + EXPECT_EQ(a2ivalues[4], 7); + + std::array a3ivalues; + appearance->getEffect().findUniformInput("aivec3", input); + appearance->getInputValueVector3i(input, 5, a3ivalues.data()); + EXPECT_EQ(a3ivalues[6], 8); + + std::array a4ivalues; + appearance->getEffect().findUniformInput("aivec4", input); + appearance->getInputValueVector4i(input, 6, a4ivalues.data()); + EXPECT_EQ(a4ivalues[8], 9); +} + +TEST_F(MaterialAdaptorTest, link_get_array_uniforms) { + auto material = create_material("mat", "shaders/uniform-array.vert", "shaders/uniform-array.frag"); + auto lua = create_lua("lua", "scripts/types-scalar.lua"); + + link(lua, {"outputs", "ointeger"}, material, {"uniforms", "ivec", "2"}); + link(lua, {"outputs", "ofloat"}, material, {"uniforms", "fvec", "3"}); + + link(lua, {"outputs", "ovector2f"}, material, {"uniforms", "avec2", "3"}); + link(lua, {"outputs", "ovector3f"}, material, {"uniforms", "avec3", "3"}); + link(lua, {"outputs", "ovector4f"}, material, {"uniforms", "avec4", "3"}); + + link(lua, {"outputs", "ovector2i"}, material, {"uniforms", "aivec2", "3"}); + link(lua, {"outputs", "ovector3i"}, material, {"uniforms", "aivec3", "3"}); + link(lua, {"outputs", "ovector4i"}, material, {"uniforms", "aivec4", "3"}); + + dispatch(); + + commandInterface.set({lua, {"inputs", "integer"}}, 1); + commandInterface.set({lua, {"inputs", "float"}}, 1.0); + + dispatch(); + + auto appearance{select(*sceneContext.scene(), "mat_Appearance")}; + + ramses::UniformInput input; + + std::array ivalues; + appearance->getEffect().findUniformInput("ivec", input); + appearance->getInputValueInt32(input, 2, ivalues.data()); + EXPECT_EQ(ivalues[1], 2); + + std::array fvalues; + appearance->getEffect().findUniformInput("fvec", input); + appearance->getInputValueFloat(input, 5, fvalues.data()); + EXPECT_EQ(fvalues[2], 1.0); + + std::array a2fvalues; + appearance->getEffect().findUniformInput("avec2", input); + appearance->getInputValueVector2f(input, 4, a2fvalues.data()); + EXPECT_EQ(a2fvalues[5], 2.0); + + std::array a3fvalues; + appearance->getEffect().findUniformInput("avec3", input); + appearance->getInputValueVector3f(input, 5, a3fvalues.data()); + EXPECT_EQ(a3fvalues[7], 2.0); + + std::array a4fvalues; + appearance->getEffect().findUniformInput("avec4", input); + appearance->getInputValueVector4f(input, 6, a4fvalues.data()); + EXPECT_EQ(a4fvalues[9], 2.0); + + std::array a2ivalues; + appearance->getEffect().findUniformInput("aivec2", input); + appearance->getInputValueVector2i(input, 4, a2ivalues.data()); + EXPECT_EQ(a2ivalues[4], 1); + + std::array a3ivalues; + appearance->getEffect().findUniformInput("aivec3", input); + appearance->getInputValueVector3i(input, 5, a3ivalues.data()); + EXPECT_EQ(a3ivalues[6], 1); + + std::array a4ivalues; + appearance->getEffect().findUniformInput("aivec4", input); + appearance->getInputValueVector4i(input, 6, a4ivalues.data()); + EXPECT_EQ(a4ivalues[8], 1); +} \ No newline at end of file diff --git a/components/libRamsesBase/tests/MeshNodeAdaptor_test.cpp b/components/libRamsesBase/tests/MeshNodeAdaptor_test.cpp index 3486eca7..62e1a523 100644 --- a/components/libRamsesBase/tests/MeshNodeAdaptor_test.cpp +++ b/components/libRamsesBase/tests/MeshNodeAdaptor_test.cpp @@ -13,10 +13,12 @@ #include "ramses_adaptor/MeshNodeAdaptor.h" #include "ramses_adaptor/SceneAdaptor.h" #include "ramses_adaptor/utilities.h" +#include "user_types/RenderBufferMS.h" using namespace raco; using raco::ramses_adaptor::MeshNodeAdaptor; using raco::ramses_adaptor::SceneAdaptor; +using raco::user_types::RenderBufferMS; using raco::user_types::Material; using raco::user_types::Mesh; using raco::user_types::MeshNode; @@ -325,6 +327,24 @@ TEST_F(MeshNodeAdaptorFixture, inContext_userType_MeshNode_materialReset_and_dep EXPECT_STREQ(raco::ramses_adaptor::defaultEffectWithNormalsName, ramsesMeshNode->getAppearance()->getEffect().getName()); EXPECT_EQ(ramses::EDepthWrite_Enabled, raco::ramses_adaptor::getDepthWriteMode(ramsesMeshNode->getAppearance())); } + +TEST_F(MeshNodeAdaptorFixture, inContext_userType_MeshNode_multiSampledMaterial_invalidNoCrash) { + auto mesh = create_mesh("Mesh", "meshes/Duck.glb"); + auto material = create_material("Material", "shaders/multisampler.vert", "shaders/multisampler.frag"); + auto meshNode = create_meshnode("MeshNode", mesh, material); + auto renderBufferMS = create("RBMS", {}); + + context.set(ValueHandle{material}.get("uniforms").get("textureSampler"), renderBufferMS); + dispatch(); + + context.set({renderBufferMS, &RenderBufferMS::sampleCount_}, -1); + dispatch(); + ASSERT_TRUE(context.errors().hasError(ValueHandle{material}.get("uniforms").get("textureSampler"))); + + context.set({renderBufferMS, &RenderBufferMS::sampleCount_}, 2); + dispatch(); + ASSERT_FALSE(context.errors().hasError(ValueHandle{material}.get("uniforms").get("textureSampler"))); +} TEST_F(MeshNodeAdaptorFixture, inContext_userType_MeshNode_dynamicCreation_meshBeforeMeshNode) { auto mesh = context.createObject(Mesh::typeDescription.typeName, "Mesh"); dispatch(); diff --git a/components/libRamsesBase/tests/RenderLayerAdaptor_test.cpp b/components/libRamsesBase/tests/RenderLayerAdaptor_test.cpp index 894ca7d2..50cafa57 100644 --- a/components/libRamsesBase/tests/RenderLayerAdaptor_test.cpp +++ b/components/libRamsesBase/tests/RenderLayerAdaptor_test.cpp @@ -29,6 +29,13 @@ class RenderLayerAdaptorTest : public RamsesBaseFixture<> { return order; } + int32_t getGroupSortOrder(const ramses::RenderGroup& group, const ramses::RenderGroup& nestedGroup) { + int32_t order; + auto status = group.getRenderGroupOrder(nestedGroup, order); + EXPECT_EQ(status, ramses::StatusOK); + return order; + } + void set_renderables(raco::user_types::SRenderLayer layer, const std::vector>& renderables) { context.removeAllProperties({layer, {"renderableTags"}}); for (int index = 0; index < renderables.size(); index++) { @@ -46,8 +53,10 @@ TEST_F(RenderLayerAdaptorTest, renderables_meshnode_root) { auto engineMeshNode = select(*sceneContext.scene(), "meshnode"); auto engineGroup = select(*sceneContext.scene(), "layer"); + auto engineGroupNested = select(*sceneContext.scene(), "layer.render_main"); - ASSERT_TRUE(engineGroup->containsMeshNode(*engineMeshNode)); + ASSERT_FALSE(engineGroup->containsMeshNode(*engineMeshNode)); + ASSERT_TRUE(engineGroupNested->containsMeshNode(*engineMeshNode)); } TEST_F(RenderLayerAdaptorTest, renderables_meshnode_child) { @@ -59,8 +68,10 @@ TEST_F(RenderLayerAdaptorTest, renderables_meshnode_child) { auto engineMeshNode = select(*sceneContext.scene(), "meshnode"); auto engineGroup = select(*sceneContext.scene(), "layer"); + auto engineGroupNested = select(*sceneContext.scene(), "layer.render_main"); - ASSERT_TRUE(engineGroup->containsMeshNode(*engineMeshNode)); + ASSERT_FALSE(engineGroup->containsMeshNode(*engineMeshNode)); + ASSERT_TRUE(engineGroupNested->containsMeshNode(*engineMeshNode)); } TEST_F(RenderLayerAdaptorTest, renderables_meshnode_multi) { @@ -73,9 +84,13 @@ TEST_F(RenderLayerAdaptorTest, renderables_meshnode_multi) { auto engineMeshNode1 = select(*sceneContext.scene(), "meshnode1"); auto engineMeshNode2 = select(*sceneContext.scene(), "meshnode2"); auto engineGroup = select(*sceneContext.scene(), "layer"); + auto engineGroupNested = select(*sceneContext.scene(), "layer.render_main"); - ASSERT_TRUE(engineGroup->containsMeshNode(*engineMeshNode1)); + ASSERT_FALSE(engineGroup->containsMeshNode(*engineMeshNode1)); ASSERT_FALSE(engineGroup->containsMeshNode(*engineMeshNode2)); + + ASSERT_TRUE(engineGroupNested->containsMeshNode(*engineMeshNode1)); + ASSERT_FALSE(engineGroupNested->containsMeshNode(*engineMeshNode2)); } TEST_F(RenderLayerAdaptorTest, renderables_meshnode_root_add_node_tag) { @@ -86,12 +101,17 @@ TEST_F(RenderLayerAdaptorTest, renderables_meshnode_root_add_node_tag) { auto engineMeshNode = select(*sceneContext.scene(), "meshnode"); auto engineGroup = select(*sceneContext.scene(), "layer"); + auto engineGroupNested = select(*sceneContext.scene(), "layer.render_main"); ASSERT_FALSE(engineGroup->containsMeshNode(*engineMeshNode)); + ASSERT_FALSE(engineGroupNested->containsMeshNode(*engineMeshNode)); context.set({meshnode, {"tags"}}, std::vector({"render_main"})); dispatch(); - ASSERT_TRUE(engineGroup->containsMeshNode(*engineMeshNode)); + + engineGroupNested = select(*sceneContext.scene(), "layer.render_main"); + ASSERT_FALSE(engineGroup->containsMeshNode(*engineMeshNode)); + ASSERT_TRUE(engineGroupNested->containsMeshNode(*engineMeshNode)); } TEST_F(RenderLayerAdaptorTest, renderables_meshnode_root_add_layer_renderable) { @@ -104,10 +124,13 @@ TEST_F(RenderLayerAdaptorTest, renderables_meshnode_root_add_layer_renderable) { auto engineGroup = select(*sceneContext.scene(), "layer"); ASSERT_FALSE(engineGroup->containsMeshNode(*engineMeshNode)); + ASSERT_TRUE(sceneContext.scene()->findObjectByName("layer.render_main") == nullptr); context.addProperty({layer, {"renderableTags"}}, "render_main", std::make_unique>(0)); dispatch(); - ASSERT_TRUE(engineGroup->containsMeshNode(*engineMeshNode)); + auto engineGroupNested = select(*sceneContext.scene(), "layer.render_main"); + ASSERT_FALSE(engineGroup->containsMeshNode(*engineMeshNode)); + ASSERT_TRUE(engineGroupNested->containsMeshNode(*engineMeshNode)); } TEST_F(RenderLayerAdaptorTest, renderables_meshnode_move_scenegraph_child) { @@ -119,18 +142,24 @@ TEST_F(RenderLayerAdaptorTest, renderables_meshnode_move_scenegraph_child) { auto engineMeshNode = select(*sceneContext.scene(), "meshnode"); auto engineGroup = select(*sceneContext.scene(), "layer"); + auto engineGroupNested = select(*sceneContext.scene(), "layer.render_main"); - ASSERT_TRUE(engineGroup->containsMeshNode(*engineMeshNode)); + ASSERT_FALSE(engineGroup->containsMeshNode(*engineMeshNode)); + ASSERT_TRUE(engineGroupNested->containsMeshNode(*engineMeshNode)); context.moveScenegraphChildren({meshNode}, nullptr); dispatch(); + engineGroupNested = select(*sceneContext.scene(), "layer.render_main"); ASSERT_FALSE(engineGroup->containsMeshNode(*engineMeshNode)); + ASSERT_FALSE(engineGroupNested->containsMeshNode(*engineMeshNode)); context.moveScenegraphChildren({meshNode}, root); dispatch(); - ASSERT_TRUE(engineGroup->containsMeshNode(*engineMeshNode)); + engineGroupNested = select(*sceneContext.scene(), "layer.render_main"); + ASSERT_FALSE(engineGroup->containsMeshNode(*engineMeshNode)); + ASSERT_TRUE(engineGroupNested->containsMeshNode(*engineMeshNode)); } TEST_F(RenderLayerAdaptorTest, matfilter_toggle_invert) { @@ -150,17 +179,26 @@ TEST_F(RenderLayerAdaptorTest, matfilter_toggle_invert) { auto engineMeshNode_def = select(*sceneContext.scene(), "meshnode_def"); auto engineMeshNode_alt = select(*sceneContext.scene(), "meshnode_alt"); auto engineGroup = select(*sceneContext.scene(), "layer"); + auto engineGroupNested = select(*sceneContext.scene(), "layer.render_main"); ASSERT_FALSE(engineGroup->containsMeshNode(*engineMeshNode)); - ASSERT_TRUE(engineGroup->containsMeshNode(*engineMeshNode_def)); + ASSERT_FALSE(engineGroup->containsMeshNode(*engineMeshNode_def)); ASSERT_FALSE(engineGroup->containsMeshNode(*engineMeshNode_alt)); + ASSERT_FALSE(engineGroupNested->containsMeshNode(*engineMeshNode)); + ASSERT_TRUE(engineGroupNested->containsMeshNode(*engineMeshNode_def)); + ASSERT_FALSE(engineGroupNested->containsMeshNode(*engineMeshNode_alt)); context.set({layer, &raco::user_types::RenderLayer::materialFilterMode_}, static_cast(raco::user_types::ERenderLayerMaterialFilterMode::Exclusive)); dispatch(); + + engineGroupNested = select(*sceneContext.scene(), "layer.render_main"); - ASSERT_TRUE(engineGroup->containsMeshNode(*engineMeshNode)); + ASSERT_FALSE(engineGroup->containsMeshNode(*engineMeshNode)); ASSERT_FALSE(engineGroup->containsMeshNode(*engineMeshNode_def)); - ASSERT_TRUE(engineGroup->containsMeshNode(*engineMeshNode_alt)); + ASSERT_FALSE(engineGroup->containsMeshNode(*engineMeshNode_alt)); + ASSERT_TRUE(engineGroupNested->containsMeshNode(*engineMeshNode)); + ASSERT_FALSE(engineGroupNested->containsMeshNode(*engineMeshNode_def)); + ASSERT_TRUE(engineGroupNested->containsMeshNode(*engineMeshNode_alt)); } TEST_F(RenderLayerAdaptorTest, matfilter_include_change_mat_tags) { @@ -180,19 +218,27 @@ TEST_F(RenderLayerAdaptorTest, matfilter_include_change_mat_tags) { auto engineMeshNode_def = select(*sceneContext.scene(), "meshnode_def"); auto engineMeshNode_alt = select(*sceneContext.scene(), "meshnode_alt"); auto engineGroup = select(*sceneContext.scene(), "layer"); + auto engineGroupNested = select(*sceneContext.scene(), "layer.render_main"); ASSERT_FALSE(engineGroup->containsMeshNode(*engineMeshNode)); - ASSERT_TRUE(engineGroup->containsMeshNode(*engineMeshNode_def)); + ASSERT_FALSE(engineGroup->containsMeshNode(*engineMeshNode_def)); ASSERT_FALSE(engineGroup->containsMeshNode(*engineMeshNode_alt)); + ASSERT_FALSE(engineGroupNested->containsMeshNode(*engineMeshNode)); + ASSERT_TRUE(engineGroupNested->containsMeshNode(*engineMeshNode_def)); + ASSERT_FALSE(engineGroupNested->containsMeshNode(*engineMeshNode_alt)); context.set({material, {"tags"}}, std::vector({"mat_default"})); context.set({material_def, {"tags"}}, std::vector({"mat_alt"})); context.set({material_alt, {"tags"}}, std::vector({"mat_alt", "mat_default"})); dispatch(); + engineGroupNested = select(*sceneContext.scene(), "layer.render_main"); - ASSERT_TRUE(engineGroup->containsMeshNode(*engineMeshNode)); + ASSERT_FALSE(engineGroup->containsMeshNode(*engineMeshNode)); ASSERT_FALSE(engineGroup->containsMeshNode(*engineMeshNode_def)); - ASSERT_TRUE(engineGroup->containsMeshNode(*engineMeshNode_alt)); + ASSERT_FALSE(engineGroup->containsMeshNode(*engineMeshNode_alt)); + ASSERT_TRUE(engineGroupNested->containsMeshNode(*engineMeshNode)); + ASSERT_FALSE(engineGroupNested->containsMeshNode(*engineMeshNode_def)); + ASSERT_TRUE(engineGroupNested->containsMeshNode(*engineMeshNode_alt)); } TEST_F(RenderLayerAdaptorTest, matfilter_include_change_layer_matfilter) { @@ -212,17 +258,27 @@ TEST_F(RenderLayerAdaptorTest, matfilter_include_change_layer_matfilter) { auto engineMeshNode_def = select(*sceneContext.scene(), "meshnode_def"); auto engineMeshNode_alt = select(*sceneContext.scene(), "meshnode_alt"); auto engineGroup = select(*sceneContext.scene(), "layer"); + auto engineGroupNested = select(*sceneContext.scene(), "layer.render_main"); ASSERT_FALSE(engineGroup->containsMeshNode(*engineMeshNode)); - ASSERT_TRUE(engineGroup->containsMeshNode(*engineMeshNode_def)); + ASSERT_FALSE(engineGroup->containsMeshNode(*engineMeshNode_def)); ASSERT_FALSE(engineGroup->containsMeshNode(*engineMeshNode_alt)); + ASSERT_FALSE(engineGroupNested->containsMeshNode(*engineMeshNode)); + ASSERT_TRUE(engineGroupNested->containsMeshNode(*engineMeshNode_def)); + ASSERT_FALSE(engineGroupNested->containsMeshNode(*engineMeshNode_alt)); + context.set({layer, {"materialFilterTags"}}, std::vector({"mat_alt"})); dispatch(); + engineGroupNested = select(*sceneContext.scene(), "layer.render_main"); ASSERT_FALSE(engineGroup->containsMeshNode(*engineMeshNode)); ASSERT_FALSE(engineGroup->containsMeshNode(*engineMeshNode_def)); - ASSERT_TRUE(engineGroup->containsMeshNode(*engineMeshNode_alt)); + ASSERT_FALSE(engineGroup->containsMeshNode(*engineMeshNode_alt)); + + ASSERT_FALSE(engineGroupNested->containsMeshNode(*engineMeshNode)); + ASSERT_FALSE(engineGroupNested->containsMeshNode(*engineMeshNode_def)); + ASSERT_TRUE(engineGroupNested->containsMeshNode(*engineMeshNode_alt)); } TEST_F(RenderLayerAdaptorTest, matfilter_exclude_change_mat_tags) { @@ -242,19 +298,29 @@ TEST_F(RenderLayerAdaptorTest, matfilter_exclude_change_mat_tags) { auto engineMeshNode_def = select(*sceneContext.scene(), "meshnode_def"); auto engineMeshNode_alt = select(*sceneContext.scene(), "meshnode_alt"); auto engineGroup = select(*sceneContext.scene(), "layer"); + auto engineGroupNested = select(*sceneContext.scene(), "layer.render_main"); - ASSERT_TRUE(engineGroup->containsMeshNode(*engineMeshNode)); + ASSERT_FALSE(engineGroup->containsMeshNode(*engineMeshNode)); ASSERT_FALSE(engineGroup->containsMeshNode(*engineMeshNode_def)); - ASSERT_TRUE(engineGroup->containsMeshNode(*engineMeshNode_alt)); + ASSERT_FALSE(engineGroup->containsMeshNode(*engineMeshNode_alt)); + + ASSERT_TRUE(engineGroupNested->containsMeshNode(*engineMeshNode)); + ASSERT_FALSE(engineGroupNested->containsMeshNode(*engineMeshNode_def)); + ASSERT_TRUE(engineGroupNested->containsMeshNode(*engineMeshNode_alt)); context.set({material, {"tags"}}, std::vector({"mat_default"})); context.set({material_def, {"tags"}}, std::vector({"mat_alt"})); context.set({material_alt, {"tags"}}, std::vector({"mat_alt", "mat_default"})); dispatch(); + engineGroupNested = select(*sceneContext.scene(), "layer.render_main"); ASSERT_FALSE(engineGroup->containsMeshNode(*engineMeshNode)); - ASSERT_TRUE(engineGroup->containsMeshNode(*engineMeshNode_def)); + ASSERT_FALSE(engineGroup->containsMeshNode(*engineMeshNode_def)); ASSERT_FALSE(engineGroup->containsMeshNode(*engineMeshNode_alt)); + + ASSERT_FALSE(engineGroupNested->containsMeshNode(*engineMeshNode)); + ASSERT_TRUE(engineGroupNested->containsMeshNode(*engineMeshNode_def)); + ASSERT_FALSE(engineGroupNested->containsMeshNode(*engineMeshNode_alt)); } TEST_F(RenderLayerAdaptorTest, matfilter_exclude_change_layer_matfilter) { @@ -274,17 +340,27 @@ TEST_F(RenderLayerAdaptorTest, matfilter_exclude_change_layer_matfilter) { auto engineMeshNode_def = select(*sceneContext.scene(), "meshnode_def"); auto engineMeshNode_alt = select(*sceneContext.scene(), "meshnode_alt"); auto engineGroup = select(*sceneContext.scene(), "layer"); + auto engineGroupNested = select(*sceneContext.scene(), "layer.render_main"); - ASSERT_TRUE(engineGroup->containsMeshNode(*engineMeshNode)); + ASSERT_FALSE(engineGroup->containsMeshNode(*engineMeshNode)); ASSERT_FALSE(engineGroup->containsMeshNode(*engineMeshNode_def)); - ASSERT_TRUE(engineGroup->containsMeshNode(*engineMeshNode_alt)); + ASSERT_FALSE(engineGroup->containsMeshNode(*engineMeshNode_alt)); + + ASSERT_TRUE(engineGroupNested->containsMeshNode(*engineMeshNode)); + ASSERT_FALSE(engineGroupNested->containsMeshNode(*engineMeshNode_def)); + ASSERT_TRUE(engineGroupNested->containsMeshNode(*engineMeshNode_alt)); context.set({layer, {"materialFilterTags"}}, std::vector({"mat_alt"})); dispatch(); + engineGroupNested = select(*sceneContext.scene(), "layer.render_main"); - ASSERT_TRUE(engineGroup->containsMeshNode(*engineMeshNode)); - ASSERT_TRUE(engineGroup->containsMeshNode(*engineMeshNode_def)); + ASSERT_FALSE(engineGroup->containsMeshNode(*engineMeshNode)); + ASSERT_FALSE(engineGroup->containsMeshNode(*engineMeshNode_def)); ASSERT_FALSE(engineGroup->containsMeshNode(*engineMeshNode_alt)); + + ASSERT_TRUE(engineGroupNested->containsMeshNode(*engineMeshNode)); + ASSERT_TRUE(engineGroupNested->containsMeshNode(*engineMeshNode_def)); + ASSERT_FALSE(engineGroupNested->containsMeshNode(*engineMeshNode_alt)); } TEST_F(RenderLayerAdaptorTest, matfilter_nested_toggle_invert) { @@ -304,17 +380,27 @@ TEST_F(RenderLayerAdaptorTest, matfilter_nested_toggle_invert) { auto engineMeshNode_def = select(*sceneContext.scene(), "meshnode_def"); auto engineMeshNode_alt = select(*sceneContext.scene(), "meshnode_alt"); auto engineGroup = select(*sceneContext.scene(), "layer"); + auto engineGroupNested = select(*sceneContext.scene(), "layer.render_main"); - ASSERT_TRUE(engineGroup->containsMeshNode(*engineMeshNode)); + ASSERT_FALSE(engineGroup->containsMeshNode(*engineMeshNode)); ASSERT_FALSE(engineGroup->containsMeshNode(*engineMeshNode_def)); - ASSERT_TRUE(engineGroup->containsMeshNode(*engineMeshNode_alt)); + ASSERT_FALSE(engineGroup->containsMeshNode(*engineMeshNode_alt)); + + ASSERT_TRUE(engineGroupNested->containsMeshNode(*engineMeshNode)); + ASSERT_FALSE(engineGroupNested->containsMeshNode(*engineMeshNode_def)); + ASSERT_TRUE(engineGroupNested->containsMeshNode(*engineMeshNode_alt)); context.set({layer, &raco::user_types::RenderLayer::materialFilterMode_}, static_cast(raco::user_types::ERenderLayerMaterialFilterMode::Exclusive)); dispatch(); + engineGroupNested = select(*sceneContext.scene(), "layer.render_main"); - ASSERT_TRUE(engineGroup->containsMeshNode(*engineMeshNode)); + ASSERT_FALSE(engineGroup->containsMeshNode(*engineMeshNode)); ASSERT_FALSE(engineGroup->containsMeshNode(*engineMeshNode_def)); - ASSERT_TRUE(engineGroup->containsMeshNode(*engineMeshNode_alt)); + ASSERT_FALSE(engineGroup->containsMeshNode(*engineMeshNode_alt)); + + ASSERT_TRUE(engineGroupNested->containsMeshNode(*engineMeshNode)); + ASSERT_FALSE(engineGroupNested->containsMeshNode(*engineMeshNode_def)); + ASSERT_TRUE(engineGroupNested->containsMeshNode(*engineMeshNode_alt)); } TEST_F(RenderLayerAdaptorTest, nested_simple) { @@ -327,10 +413,14 @@ TEST_F(RenderLayerAdaptorTest, nested_simple) { auto engineMeshNode = select(*sceneContext.scene(), "meshnode"); auto engineGroup = select(*sceneContext.scene(), "layer"); + auto engineGroupNested = select(*sceneContext.scene(), "layer.render_main"); auto engineGroup_n = select(*sceneContext.scene(), "layer_n"); - ASSERT_TRUE(engineGroup->containsMeshNode(*engineMeshNode)); - ASSERT_TRUE(engineGroup->containsRenderGroup(*engineGroup_n)); + ASSERT_FALSE(engineGroup->containsMeshNode(*engineMeshNode)); + ASSERT_FALSE(engineGroup->containsRenderGroup(*engineGroup_n)); + + ASSERT_TRUE(engineGroupNested->containsMeshNode(*engineMeshNode)); + ASSERT_TRUE(engineGroupNested->containsRenderGroup(*engineGroup_n)); } TEST_F(RenderLayerAdaptorTest, nested_fail_self_loop) { @@ -342,9 +432,13 @@ TEST_F(RenderLayerAdaptorTest, nested_fail_self_loop) { auto engineMeshNode = select(*sceneContext.scene(), "meshnode"); auto engineGroup = select(*sceneContext.scene(), "layer"); + auto engineGroupNested = select(*sceneContext.scene(), "layer.render_main"); - ASSERT_TRUE(engineGroup->containsMeshNode(*engineMeshNode)); + ASSERT_FALSE(engineGroup->containsMeshNode(*engineMeshNode)); ASSERT_FALSE(engineGroup->containsRenderGroup(*engineGroup)); + + ASSERT_TRUE(engineGroupNested->containsMeshNode(*engineMeshNode)); + ASSERT_FALSE(engineGroupNested->containsRenderGroup(*engineGroup)); } TEST_F(RenderLayerAdaptorTest, nested_fail_child_direct_loop) { @@ -358,15 +452,23 @@ TEST_F(RenderLayerAdaptorTest, nested_fail_child_direct_loop) { auto engineMeshNode = select(*sceneContext.scene(), "meshnode"); auto engineGroup = select(*sceneContext.scene(), "layer"); auto engineGroup_n = select(*sceneContext.scene(), "layer_n"); + auto engineGroupNested = select(*sceneContext.scene(), "layer.render_main"); - ASSERT_TRUE(engineGroup->containsMeshNode(*engineMeshNode)); + ASSERT_FALSE(engineGroup->containsMeshNode(*engineMeshNode)); ASSERT_FALSE(engineGroup->containsRenderGroup(*engineGroup_n)); + ASSERT_TRUE(engineGroupNested->containsMeshNode(*engineMeshNode)); + ASSERT_FALSE(engineGroupNested->containsRenderGroup(*engineGroup_n)); + set_renderables(layer_n, {{"FOO", 0}}); dispatch(); + engineGroupNested = select(*sceneContext.scene(), "layer.render_main"); + + ASSERT_FALSE(engineGroup->containsMeshNode(*engineMeshNode)); + ASSERT_FALSE(engineGroup->containsRenderGroup(*engineGroup_n)); - ASSERT_TRUE(engineGroup->containsMeshNode(*engineMeshNode)); - ASSERT_TRUE(engineGroup->containsRenderGroup(*engineGroup_n)); + ASSERT_TRUE(engineGroupNested->containsMeshNode(*engineMeshNode)); + ASSERT_TRUE(engineGroupNested->containsRenderGroup(*engineGroup_n)); } TEST_F(RenderLayerAdaptorTest, nested_fail_child_indirect_loop) { @@ -382,17 +484,30 @@ TEST_F(RenderLayerAdaptorTest, nested_fail_child_indirect_loop) { auto engineGroup = select(*sceneContext.scene(), "layer"); auto engineGroup_a = select(*sceneContext.scene(), "layer_a"); auto engineGroup_b = select(*sceneContext.scene(), "layer_b"); + auto engineGroupNested = select(*sceneContext.scene(), "layer.render_main"); - ASSERT_TRUE(engineGroup->containsMeshNode(*engineMeshNode)); + ASSERT_FALSE(engineGroup->containsMeshNode(*engineMeshNode)); ASSERT_FALSE(engineGroup->containsRenderGroup(*engineGroup_a)); ASSERT_FALSE(engineGroup->containsRenderGroup(*engineGroup_b)); + ASSERT_TRUE(engineGroupNested->containsMeshNode(*engineMeshNode)); + ASSERT_FALSE(engineGroupNested->containsRenderGroup(*engineGroup_a)); + ASSERT_FALSE(engineGroupNested->containsRenderGroup(*engineGroup_b)); + set_renderables(layer_b, {{"FOO", 0}}); dispatch(); + engineGroupNested = select(*sceneContext.scene(), "layer.render_main"); + auto engineGroupNested_a = select(*sceneContext.scene(), "layer_a.render_nest"); - ASSERT_TRUE(engineGroup->containsMeshNode(*engineMeshNode)); - ASSERT_TRUE(engineGroup->containsRenderGroup(*engineGroup_a)); + ASSERT_FALSE(engineGroup->containsMeshNode(*engineMeshNode)); + ASSERT_FALSE(engineGroup->containsRenderGroup(*engineGroup_a)); ASSERT_FALSE(engineGroup->containsRenderGroup(*engineGroup_b)); + + ASSERT_TRUE(engineGroupNested->containsMeshNode(*engineMeshNode)); + ASSERT_TRUE(engineGroupNested->containsRenderGroup(*engineGroup_a)); + ASSERT_FALSE(engineGroupNested->containsRenderGroup(*engineGroup_b)); + + ASSERT_TRUE(engineGroupNested_a->containsRenderGroup(*engineGroup_b)); } TEST_F(RenderLayerAdaptorTest, sortorder_manual) { @@ -409,23 +524,43 @@ TEST_F(RenderLayerAdaptorTest, sortorder_manual) { auto engineMeshNode2 = select(*sceneContext.scene(), "meshnode2"); auto engineMeshNode3 = select(*sceneContext.scene(), "meshnode3"); auto engineGroup = select(*sceneContext.scene(), "layer"); + auto engineGroupNested_main = select(*sceneContext.scene(), "layer.render_main"); + auto engineGroupNested_alt = select(*sceneContext.scene(), "layer.render_alt"); - ASSERT_TRUE(engineGroup->containsMeshNode(*engineMeshNode1)); - ASSERT_TRUE(engineGroup->containsMeshNode(*engineMeshNode2)); - ASSERT_TRUE(engineGroup->containsMeshNode(*engineMeshNode3)); - ASSERT_EQ(getSortOrder(*engineGroup, *engineMeshNode1), 0); - ASSERT_EQ(getSortOrder(*engineGroup, *engineMeshNode2), 1); - ASSERT_EQ(getSortOrder(*engineGroup, *engineMeshNode3), 0); + ASSERT_FALSE(engineGroup->containsMeshNode(*engineMeshNode1)); + ASSERT_FALSE(engineGroup->containsMeshNode(*engineMeshNode2)); + ASSERT_FALSE(engineGroup->containsMeshNode(*engineMeshNode3)); + + ASSERT_TRUE(engineGroupNested_main->containsMeshNode(*engineMeshNode1)); + ASSERT_FALSE(engineGroupNested_main->containsMeshNode(*engineMeshNode2)); + ASSERT_TRUE(engineGroupNested_main->containsMeshNode(*engineMeshNode3)); + + ASSERT_FALSE(engineGroupNested_alt->containsMeshNode(*engineMeshNode1)); + ASSERT_TRUE(engineGroupNested_alt->containsMeshNode(*engineMeshNode2)); + ASSERT_FALSE(engineGroupNested_alt->containsMeshNode(*engineMeshNode3)); + + ASSERT_EQ(getGroupSortOrder(*engineGroup, *engineGroupNested_main), 0); + ASSERT_EQ(getGroupSortOrder(*engineGroup, *engineGroupNested_alt), 1); set_renderables(layer, {{"render_alt", 0}, {"render_main", 1}}); dispatch(); + engineGroupNested_main = select(*sceneContext.scene(), "layer.render_main"); + engineGroupNested_alt = select(*sceneContext.scene(), "layer.render_alt"); - ASSERT_TRUE(engineGroup->containsMeshNode(*engineMeshNode1)); - ASSERT_TRUE(engineGroup->containsMeshNode(*engineMeshNode2)); - ASSERT_TRUE(engineGroup->containsMeshNode(*engineMeshNode3)); - ASSERT_EQ(getSortOrder(*engineGroup, *engineMeshNode1), 1); - ASSERT_EQ(getSortOrder(*engineGroup, *engineMeshNode2), 0); - ASSERT_EQ(getSortOrder(*engineGroup, *engineMeshNode3), 1); + ASSERT_FALSE(engineGroup->containsMeshNode(*engineMeshNode1)); + ASSERT_FALSE(engineGroup->containsMeshNode(*engineMeshNode2)); + ASSERT_FALSE(engineGroup->containsMeshNode(*engineMeshNode3)); + + ASSERT_TRUE(engineGroupNested_main->containsMeshNode(*engineMeshNode1)); + ASSERT_FALSE(engineGroupNested_main->containsMeshNode(*engineMeshNode2)); + ASSERT_TRUE(engineGroupNested_main->containsMeshNode(*engineMeshNode3)); + + ASSERT_FALSE(engineGroupNested_alt->containsMeshNode(*engineMeshNode1)); + ASSERT_TRUE(engineGroupNested_alt->containsMeshNode(*engineMeshNode2)); + ASSERT_FALSE(engineGroupNested_alt->containsMeshNode(*engineMeshNode3)); + + ASSERT_EQ(getGroupSortOrder(*engineGroup, *engineGroupNested_main), 1); + ASSERT_EQ(getGroupSortOrder(*engineGroup, *engineGroupNested_alt), 0); } TEST_F(RenderLayerAdaptorTest, sortorder_scenegraph) { @@ -442,6 +577,8 @@ TEST_F(RenderLayerAdaptorTest, sortorder_scenegraph) { auto engineMeshNode2 = select(*sceneContext.scene(), "meshnode2"); auto engineMeshNode3 = select(*sceneContext.scene(), "meshnode3"); auto engineGroup = select(*sceneContext.scene(), "layer"); + ASSERT_TRUE(sceneContext.scene()->findObjectByName("layer.render_main") == nullptr); + ASSERT_TRUE(sceneContext.scene()->findObjectByName("layer.render_alt") == nullptr); ASSERT_TRUE(engineGroup->containsMeshNode(*engineMeshNode1)); ASSERT_TRUE(engineGroup->containsMeshNode(*engineMeshNode2)); @@ -452,6 +589,8 @@ TEST_F(RenderLayerAdaptorTest, sortorder_scenegraph) { set_renderables(layer, {{"render_alt", 0}, {"render_main", 1}}); dispatch(); + ASSERT_TRUE(sceneContext.scene()->findObjectByName("layer.render_main") == nullptr); + ASSERT_TRUE(sceneContext.scene()->findObjectByName("layer.render_alt") == nullptr); // Priorities are ignored for scene graph sorted render layers. ASSERT_TRUE(engineGroup->containsMeshNode(*engineMeshNode1)); @@ -463,15 +602,21 @@ TEST_F(RenderLayerAdaptorTest, sortorder_scenegraph) { context.set({layer, {"sortOrder"}}, static_cast(raco::user_types::ERenderLayerOrder::Manual)); dispatch(); + auto engineGroupNested_main = select(*sceneContext.scene(), "layer.render_main"); + auto engineGroupNested_alt = select(*sceneContext.scene(), "layer.render_alt"); - // Priorities are no longer ignored for manual sorted render layers. - ASSERT_TRUE(engineGroup->containsMeshNode(*engineMeshNode1)); - ASSERT_TRUE(engineGroup->containsMeshNode(*engineMeshNode2)); - ASSERT_TRUE(engineGroup->containsMeshNode(*engineMeshNode3)); - auto s1 = getSortOrder(*engineGroup, *engineMeshNode1); - auto s2 = getSortOrder(*engineGroup, *engineMeshNode2); - auto s3 = getSortOrder(*engineGroup, *engineMeshNode3); - ASSERT_EQ(getSortOrder(*engineGroup, *engineMeshNode1), 1); - ASSERT_EQ(getSortOrder(*engineGroup, *engineMeshNode2), 0); - ASSERT_EQ(getSortOrder(*engineGroup, *engineMeshNode3), 1); + ASSERT_FALSE(engineGroup->containsMeshNode(*engineMeshNode1)); + ASSERT_FALSE(engineGroup->containsMeshNode(*engineMeshNode2)); + ASSERT_FALSE(engineGroup->containsMeshNode(*engineMeshNode3)); + + ASSERT_TRUE(engineGroupNested_main->containsMeshNode(*engineMeshNode1)); + ASSERT_FALSE(engineGroupNested_main->containsMeshNode(*engineMeshNode2)); + ASSERT_TRUE(engineGroupNested_main->containsMeshNode(*engineMeshNode3)); + + ASSERT_FALSE(engineGroupNested_alt->containsMeshNode(*engineMeshNode1)); + ASSERT_TRUE(engineGroupNested_alt->containsMeshNode(*engineMeshNode2)); + ASSERT_FALSE(engineGroupNested_alt->containsMeshNode(*engineMeshNode3)); + + ASSERT_EQ(getGroupSortOrder(*engineGroup, *engineGroupNested_main), 1); + ASSERT_EQ(getGroupSortOrder(*engineGroup, *engineGroupNested_alt), 0); } diff --git a/datamodel/libCore/include/core/Context.h b/datamodel/libCore/include/core/Context.h index a1e3670c..0a256e8e 100644 --- a/datamodel/libCore/include/core/Context.h +++ b/datamodel/libCore/include/core/Context.h @@ -193,7 +193,7 @@ class BaseContext { void removeReferencesTo(SEditorObjectSet const& objects); template - static void callReferenceToThisHandlerForAllTableEntries(ValueHandle const& vh); + static void callReferenceToThisHandler(ValueHandle const& vh); ValueTreeIterator erase(const ValueTreeIterator& it); diff --git a/datamodel/libCore/include/core/CoreFormatter.h b/datamodel/libCore/include/core/CoreFormatter.h index 7f743283..4e39ef58 100644 --- a/datamodel/libCore/include/core/CoreFormatter.h +++ b/datamodel/libCore/include/core/CoreFormatter.h @@ -75,6 +75,7 @@ struct fmt::formatter : formatter { {raco::core::EnginePrimitive::Struct, "Struct"}, {raco::core::EnginePrimitive::Array, "Arrray"}, {raco::core::EnginePrimitive::TextureSampler2D, "TextureSampler2D"}, + {raco::core::EnginePrimitive::TextureSampler2DMS, "TextureSampler2DMS"}, {raco::core::EnginePrimitive::TextureSampler3D, "TextureSampler3D"}, {raco::core::EnginePrimitive::TextureSamplerCube, "TextureSamplerCube"}}; return formatter::format(nameMap.at(type), ctx); diff --git a/datamodel/libCore/include/core/EditorObject.h b/datamodel/libCore/include/core/EditorObject.h index 205a3df2..5b8532aa 100644 --- a/datamodel/libCore/include/core/EditorObject.h +++ b/datamodel/libCore/include/core/EditorObject.h @@ -60,6 +60,7 @@ class EditorObject : public ClassWithReflectedMembers, public std::enable_shared EditorObject(std::string name = std::string(), std::string id = std::string()); + virtual ~EditorObject() = default; template std::shared_ptr as() { diff --git a/datamodel/libCore/include/core/EngineInterface.h b/datamodel/libCore/include/core/EngineInterface.h index 542743e7..e78a94ec 100644 --- a/datamodel/libCore/include/core/EngineInterface.h +++ b/datamodel/libCore/include/core/EngineInterface.h @@ -60,7 +60,8 @@ enum class EnginePrimitive { TextureSampler3D, TextureSamplerCube, // Types added later, in the bottom of the enum to avoid file format changing - Int64 + Int64, + TextureSampler2DMS }; struct PropertyInterface; @@ -90,6 +91,7 @@ struct PropertyInterface { {EnginePrimitive::Array, data_storage::PrimitiveType::Table}, {EnginePrimitive::Struct, data_storage::PrimitiveType::Table}, {EnginePrimitive::TextureSampler2D, data_storage::PrimitiveType::Ref}, + {EnginePrimitive::TextureSampler2DMS, data_storage::PrimitiveType::Ref}, {EnginePrimitive::TextureSampler3D, data_storage::PrimitiveType::Ref}, {EnginePrimitive::TextureSamplerCube, data_storage::PrimitiveType::Ref}}; diff --git a/datamodel/libCore/include/core/Errors.h b/datamodel/libCore/include/core/Errors.h index 25587867..c00c0b79 100644 --- a/datamodel/libCore/include/core/Errors.h +++ b/datamodel/libCore/include/core/Errors.h @@ -39,7 +39,9 @@ class Errors { */ bool addError(ErrorCategory category, ErrorLevel level, const ValueHandle& handle, const std::string& message); - void logError(const ErrorItem& error) const; + static std::string formatError(const ErrorItem& error); + + static void logError(const ErrorItem& error); void logAllErrors() const; /** diff --git a/datamodel/libCore/include/core/Project.h b/datamodel/libCore/include/core/Project.h index 4d6a60ad..415a89a4 100644 --- a/datamodel/libCore/include/core/Project.h +++ b/datamodel/libCore/include/core/Project.h @@ -45,6 +45,7 @@ class Project { const std::vector& instances() const; std::string projectName() const; + std::string projectID() const; std::string getProjectNameForObject(SEditorObject const& object, bool fallbackToLocalProject = true) const; diff --git a/datamodel/libCore/include/core/ProjectMigration.h b/datamodel/libCore/include/core/ProjectMigration.h index faa4b28b..c5199246 100644 --- a/datamodel/libCore/include/core/ProjectMigration.h +++ b/datamodel/libCore/include/core/ProjectMigration.h @@ -84,9 +84,17 @@ namespace raco::serialization { * - renamed "order" -> "renderOrder" * - made 'enabled', 'renderOrder', and 'clearColor' linkable * Added "frustumType" property to PerspectiveCamera and changed frustum from static struct to dynamic Table. + * 45: RenderLayer changes +* - added LinkEndAnnotation to all properties in the renderableTags property +* - removed HiddenProperty from renderableTags property + * 46: Added BlitPass usertype. + * Added RenderBufferMS usertype. + * Added 8 RenderBufferMS reference properties to RenderTarget. + * Changed BaseCamera viewport width and height ranges. + * Added ExpectEmptyReference annotation to RenderTarget::buffer0 property. */ -constexpr int RAMSES_PROJECT_FILE_VERSION = 44; +constexpr int RAMSES_PROJECT_FILE_VERSION = 46; void migrateProject(ProjectDeserializationInfoIR& deserializedIR, raco::serialization::proxy::ProxyObjectFactory& factory); diff --git a/datamodel/libCore/include/core/ProxyObjectFactory.h b/datamodel/libCore/include/core/ProxyObjectFactory.h index 599b0331..eeb89d91 100644 --- a/datamodel/libCore/include/core/ProxyObjectFactory.h +++ b/datamodel/libCore/include/core/ProxyObjectFactory.h @@ -87,6 +87,7 @@ class ProxyObjectFactory : public raco::core::UserObjectFactoryInterface { Property, Property, Property, + Property, Property, Property, @@ -104,6 +105,7 @@ class ProxyObjectFactory : public raco::core::UserObjectFactoryInterface { Property, Property, Property, + Property, Property, Property, @@ -121,6 +123,7 @@ class ProxyObjectFactory : public raco::core::UserObjectFactoryInterface { Property, Property, Property, + Property, Property, Property, @@ -136,6 +139,7 @@ class ProxyObjectFactory : public raco::core::UserObjectFactoryInterface { Property, Property, Property, + Property, Property, // EditorObject @@ -206,10 +210,12 @@ class ProxyObjectFactory : public raco::core::UserObjectFactoryInterface { Property, Property, Property, + Property, // RenderTarget Property, Property, + Property, // Texture Property, diff --git a/datamodel/libCore/include/core/ProxyTypes.h b/datamodel/libCore/include/core/ProxyTypes.h index aab0ae37..bca2fb8a 100644 --- a/datamodel/libCore/include/core/ProxyTypes.h +++ b/datamodel/libCore/include/core/ProxyTypes.h @@ -80,10 +80,14 @@ extern const char textureTypeName[]; using Texture = Proxy; using STexture = std::shared_ptr; -// BaseTexture -> Texture +// BaseTexture -> CubeMap // BaseTexture -> TextureSampler2DBase -> Texture // -> RenderBuffer +extern const char blitPassTypeName[]; +using BlitPass = Proxy; +using SBlitPass = std::shared_ptr; + extern const char cubeMapTypeName[]; using CubeMap = Proxy; using SCubeMap = std::shared_ptr; @@ -104,6 +108,10 @@ extern const char renderBufferTypeName[]; using RenderBuffer = Proxy; using SRenderBuffer = std::shared_ptr; +extern const char renderBufferMSTypeName[]; +using RenderBufferMS = Proxy; +using SRenderBufferMS = std::shared_ptr; + extern const char renderLayerTypeName[]; using RenderLayer = Proxy; using SRenderLayer = std::shared_ptr; diff --git a/datamodel/libCore/include/core/SceneBackendInterface.h b/datamodel/libCore/include/core/SceneBackendInterface.h index f8fb5935..a1dc9b8f 100644 --- a/datamodel/libCore/include/core/SceneBackendInterface.h +++ b/datamodel/libCore/include/core/SceneBackendInterface.h @@ -26,7 +26,7 @@ namespace raco::core { int parentIndex_; }; - virtual bool sceneValid() const = 0; + virtual core::ErrorLevel sceneValid() const = 0; virtual std::string getValidationReport(core::ErrorLevel minLevel) const = 0; diff --git a/datamodel/libCore/src/CommandInterface.cpp b/datamodel/libCore/src/CommandInterface.cpp index b8b37ca7..44667fee 100644 --- a/datamodel/libCore/src/CommandInterface.cpp +++ b/datamodel/libCore/src/CommandInterface.cpp @@ -281,7 +281,8 @@ void CommandInterface::setRenderableTags(ValueHandle const& handle, std::vector< data_storage::Table table; for (auto const& p : renderableTags) { - table.addProperty(p.first, new Value(p.second)); + // Note: render order is only linkable starting at feature level 3. + table.addProperty(p.first, new Property(p.second, {3})); } if (!(handle.constValueRef()->asTable() == table)) { diff --git a/datamodel/libCore/src/Context.cpp b/datamodel/libCore/src/Context.cpp index 2cdaa40f..46e600b2 100644 --- a/datamodel/libCore/src/Context.cpp +++ b/datamodel/libCore/src/Context.cpp @@ -156,30 +156,39 @@ void BaseContext::setT(ValueHandle const& handle, SEditorObject c } template -void BaseContext::callReferenceToThisHandlerForAllTableEntries(ValueHandle const& vh) { - const ReflectionInterface& t = vh.valueRef()->getSubstructure(); - for (int i = 0; i < t.size(); ++i) { - auto v = t.get(i); - if (v->type() == PrimitiveType::Ref) { - auto vref = v->asRef(); - if (vref) { - (vref.get()->*Handler)(vh[i]); - } - } else if (hasTypeSubstructure(v->type())) { - callReferenceToThisHandlerForAllTableEntries(vh[i]); +void BaseContext::callReferenceToThisHandler(ValueHandle const& vh) { + if (vh.type() == PrimitiveType::Ref) { + auto vref = vh.asRef(); + if (vref) { + (vref.get()->*Handler)(vh); + } + } else if (vh.hasSubstructure()) { + for (int i = 0; i < vh.size(); ++i) { + callReferenceToThisHandler(vh[i]); } - } + } } template <> void BaseContext::setT(ValueHandle const& handle, Table const& value) { ValueBase* v = handle.valueRef(); - callReferenceToThisHandlerForAllTableEntries<&EditorObject::onBeforeRemoveReferenceToThis>(handle); + callReferenceToThisHandler<&EditorObject::onBeforeRemoveReferenceToThis>(handle); v->set(value); - callReferenceToThisHandlerForAllTableEntries<&EditorObject::onAfterAddReferenceToThis>(handle); + // Cache/Restore links starting or ending on parent properties: + // The structure on one side of the link has changed and links need to be revalidated. + for (auto link : Queries::getLinksConnectedToPropertyParents(*project_, handle, true)) { + updateLinkValidity(link); + } + + // Cache/Restore links starting or ending on the property or its child properties + for (auto link : Queries::getLinksConnectedToPropertySubtree(*project_, handle, true, true)) { + updateLinkValidity(link); + } + + callReferenceToThisHandler<&EditorObject::onAfterAddReferenceToThis>(handle); handle.object_->onAfterValueChanged(*this, handle); @@ -192,11 +201,11 @@ template <> void BaseContext::setT(ValueHandle const& handle, StructBase const& value) { ValueBase* v = handle.valueRef(); - callReferenceToThisHandlerForAllTableEntries<&EditorObject::onBeforeRemoveReferenceToThis>(handle); + callReferenceToThisHandler<&EditorObject::onBeforeRemoveReferenceToThis>(handle); v->setStruct(value); - callReferenceToThisHandlerForAllTableEntries<&EditorObject::onAfterAddReferenceToThis>(handle); + callReferenceToThisHandler<&EditorObject::onAfterAddReferenceToThis>(handle); handle.object_->onAfterValueChanged(*this, handle); @@ -286,6 +295,8 @@ ValueBase* BaseContext::addProperty(const ValueHandle& handle, std::string name, ValueBase* newValue = table.addProperty(name, std::move(newProperty), indexBefore); + ValueHandle newHandle = indexBefore == -1 ? handle[handle.size() - 1] : handle[indexBefore]; + // Cache/Restore links starting or ending on parent properties: // The structure on one side of the link has changed and links need to be revalidated. for (auto link : Queries::getLinksConnectedToPropertyParents(*project_, handle, true)) { @@ -293,25 +304,11 @@ ValueBase* BaseContext::addProperty(const ValueHandle& handle, std::string name, } // Cache/Restore links starting or ending on the property or its child properties - for (auto link : Queries::getLinksConnectedToPropertySubtree(*project_, handle.get(name), true, true)) { + for (auto link : Queries::getLinksConnectedToPropertySubtree(*project_, newHandle, true, true)) { updateLinkValidity(link); } - if (newValue->type() == PrimitiveType::Ref) { - auto refValue = newValue->asRef(); - if (refValue) { - refValue->onAfterAddReferenceToThis(handle.get(name)); - } - } else if (hasTypeSubstructure(newValue->type())) { - for (auto prop : ValueTreeIteratorAdaptor(handle.get(name))) { - if (prop.type() == PrimitiveType::Ref) { - auto refValue = prop.valueRef()->asRef(); - if (refValue) { - refValue->onAfterAddReferenceToThis(prop); - } - } - } - } + callReferenceToThisHandler<&EditorObject::onAfterAddReferenceToThis>(newHandle); callReferencedObjectChangedHandlers(handle.object_); @@ -339,21 +336,7 @@ void BaseContext::removeProperty(const ValueHandle& handle, size_t index) { updateLinkErrors.insert(*link->endObject_); } - if (propHandle.type() == PrimitiveType::Ref) { - auto refValue = propHandle.valueRef()->asRef(); - if (refValue) { - refValue->onBeforeRemoveReferenceToThis(propHandle); - } - } else if (propHandle.hasSubstructure()) { - for (auto prop : ValueTreeIteratorAdaptor(propHandle)) { - if (prop.type() == PrimitiveType::Ref) { - auto refValue = prop.valueRef()->asRef(); - if (refValue) { - refValue->onBeforeRemoveReferenceToThis(prop); - } - } - } - } + callReferenceToThisHandler<&EditorObject::onBeforeRemoveReferenceToThis>(propHandle); table.removeProperty(index); } @@ -761,7 +744,10 @@ void BaseContext::updateLinkValidity(SLink link) { link->isValid_ = false; changeMultiplexer_.recordChangeValidityOfLink(link->descriptor()); // recordValueChanged is needed to force the undo stack to save the current value of the endpoint property. - changeMultiplexer_.recordValueChanged(ValueHandle(link->endProp())); + ValueHandle handle(link->endProp()); + if (handle) { + changeMultiplexer_.recordValueChanged(handle); + } } updateBrokenLinkErrors(*link->endObject_); diff --git a/datamodel/libCore/src/Errors.cpp b/datamodel/libCore/src/Errors.cpp index 93e82d2a..4f5dbdb1 100644 --- a/datamodel/libCore/src/Errors.cpp +++ b/datamodel/libCore/src/Errors.cpp @@ -28,27 +28,27 @@ bool Errors::addError(ErrorCategory category, ErrorLevel level, const ValueHandl return true; } -void Errors::logError(const ErrorItem& error) const { +std::string Errors::formatError(const ErrorItem& error) { + auto handle = error.valueHandle(); + const auto& message = error.message(); + if (!handle) { + return fmt::format("Project-global: {}", message); + } else if (handle.isObject()) { + return fmt::format("{}[{}]: {}", handle.rootObject()->objectName(), handle.rootObject()->objectID(), message); + } else { + return fmt::format("{}[{}]#{}: {}", handle.rootObject()->objectName(), handle.rootObject()->objectID(), handle.getPropName(), message); + } +} + +void Errors::logError(const ErrorItem& error) { auto handle = error.valueHandle(); const auto& message = error.message(); switch (error.level()) { case ErrorLevel::ERROR: - if (!handle) { - LOG_ERROR(log_system::CONTEXT, "Project-global error: {}", message); - } else if (handle.isObject()) { - LOG_ERROR(log_system::CONTEXT, "{}[{}]: {}", handle.rootObject()->objectName(), handle.rootObject()->objectID(), message); - } else { - LOG_ERROR(log_system::CONTEXT, "{}[{}]#{}: {}", handle.rootObject()->objectName(), handle.rootObject()->objectID(), handle.getPropName(), message); - } + LOG_ERROR(log_system::CONTEXT, formatError(error)); break; case ErrorLevel::WARNING: - if (!handle) { - LOG_WARNING(log_system::CONTEXT, "Project-global warning: {}", message); - } else if (handle.isObject()) { - LOG_WARNING(log_system::CONTEXT, "{}[{}]: {}", handle.rootObject()->objectName(), handle.rootObject()->objectID(), message); - } else { - LOG_WARNING(log_system::CONTEXT, "{}[{}]#{}: {}", handle.rootObject()->objectName(), handle.rootObject()->objectID(), handle.getPropName(), message); - } + LOG_WARNING(log_system::CONTEXT, formatError(error)); break; default: break; diff --git a/datamodel/libCore/src/ProjectMigration.cpp b/datamodel/libCore/src/ProjectMigration.cpp index 6efcaf7e..a545d2da 100644 --- a/datamodel/libCore/src/ProjectMigration.cpp +++ b/datamodel/libCore/src/ProjectMigration.cpp @@ -458,10 +458,10 @@ void migrateProject(ProjectDeserializationInfoIR& deserializedIR, raco::serializ (*options)->addProperty("blendFactorDestColor", optionsCont.get("blendFactorDestColor")->clone({}), -1); (*options)->addProperty("blendFactorSrcAlpha", optionsCont.get("blendFactorSrcAlpha")->clone({}), -1); (*options)->addProperty("blendFactorDestAlpha", optionsCont.get("blendFactorDestAlpha")->clone({}), -1); - (*options)->addProperty("blendColor", optionsCont.get("blendColor"), -1); - (*options)->addProperty("depthwrite", optionsCont.get("depthwrite"), -1); - (*options)->addProperty("depthFunction", optionsCont.get("depthFunction"), -1); - (*options)->addProperty("cullmode", optionsCont.get("cullmode"), -1); + (*options)->addProperty("blendColor", optionsCont.get("blendColor")->clone({}), -1); + (*options)->addProperty("depthwrite", optionsCont.get("depthwrite")->clone({}), -1); + (*options)->addProperty("depthFunction", optionsCont.get("depthFunction")->clone({}), -1); + (*options)->addProperty("cullmode", optionsCont.get("cullmode")->clone({}), -1); matCont.replaceProperty("options", options); } @@ -1108,7 +1108,7 @@ void migrateProject(ProjectDeserializationInfoIR& deserializedIR, raco::serializ auto frustumTable = new Property{{}, {"Frustum"}, {}}; - (*frustumTable)->addProperty("nearPlane", new Property, LinkEndAnnotation> (frustumStruct->asStruct().get("nearPlane")->asDouble(), DisplayNameAnnotation("nearPlane"), RangeAnnotation(0.1, 1.0), {}), -1); + (*frustumTable)->addProperty("nearPlane", new Property, LinkEndAnnotation>(frustumStruct->asStruct().get("nearPlane")->asDouble(), DisplayNameAnnotation("nearPlane"), RangeAnnotation(0.1, 1.0), {}), -1); (*frustumTable)->addProperty("farPlane", new Property, LinkEndAnnotation>(frustumStruct->asStruct().get("farPlane")->asDouble(), DisplayNameAnnotation("farPlane"), RangeAnnotation(100.0, 10000.0), {}), -1); @@ -1139,6 +1139,50 @@ void migrateProject(ProjectDeserializationInfoIR& deserializedIR, raco::serializ } } } + + // File version 45 : Added LinkEndAnnotation to all properties in the RenderLayer::renderableTags property + if (deserializedIR.fileVersion < 45) { + for (const auto& dynObj : deserializedIR.objects) { + if (dynObj->serializationTypeName() == "RenderLayer") { + if (dynObj->hasProperty("renderableTags")) { + auto oldRenderables = dynObj->extractProperty("renderableTags"); + auto newRenderables = new Property{{}, {}, {"Renderable Tags"}}; + + Table& oldTable = oldRenderables->asTable(); + for (size_t i = 0; i < oldTable.size(); i++) { + int oldValue = oldTable.get(i)->asInt(); + (*newRenderables)->addProperty(oldTable.name(i), new Property(oldValue, {3}), -1); + } + + dynObj->addProperty("renderableTags", newRenderables, -1); + } + } + } + } + + // File version 46: changed BaseCamera viewport width and height ranges + if (deserializedIR.fileVersion < 46) { + for (const auto& dynObj : deserializedIR.objects) { + auto instanceType = dynObj->serializationTypeName(); + if (instanceType == "PerspectiveCamera" || instanceType == "OrthographicCamera") { + if (dynObj->hasProperty("viewport")) { + auto& viewportprop = dynObj->get("viewport")->asStruct(); + auto widthRange = viewportprop.get("width")->query>(); + widthRange->min_ = 1; + auto heightRange = viewportprop.get("height")->query>(); + heightRange->min_ = 1; + } + } + + if (instanceType == "RenderTarget") { + if (dynObj->hasProperty("buffer0")) { + auto oldProp = dynObj->extractProperty("buffer0"); + auto newProp = dynObj->addProperty("buffer0", new Property({}, {"Buffer 0"}, {}), -1); + *newProp = oldProp->asRef(); + } + } + } + } } } // namespace raco::serialization \ No newline at end of file diff --git a/datamodel/libCore/src/ProxyObjectFactory.cpp b/datamodel/libCore/src/ProxyObjectFactory.cpp index f29014cd..11871e6a 100644 --- a/datamodel/libCore/src/ProxyObjectFactory.cpp +++ b/datamodel/libCore/src/ProxyObjectFactory.cpp @@ -65,6 +65,7 @@ namespace raco::serialization::proxy { AnchorPoint, Animation, AnimationChannel, + BlitPass, CubeMap, Node, MeshNode, @@ -79,6 +80,7 @@ namespace raco::serialization::proxy { LuaScriptModule, Texture, RenderBuffer, + RenderBufferMS, RenderLayer, RenderTarget, RenderPass, diff --git a/datamodel/libCore/src/ProxyTypes.cpp b/datamodel/libCore/src/ProxyTypes.cpp index 88b4d0cc..b1327a3c 100644 --- a/datamodel/libCore/src/ProxyTypes.cpp +++ b/datamodel/libCore/src/ProxyTypes.cpp @@ -24,11 +24,13 @@ const char animationTypeName[] = "Animation"; const char animationChannelTypeName[] = "AnimationChannel"; const char textureSampler2DBaseTypeName[] = "TextureSampler2DBase"; const char textureTypeName[] = "Texture"; +const char blitPassTypeName[] = "BlitPass"; const char cubeMapTypeName[] = "CubeMap"; const char baseCameraTypeName[] = "BaseCamera"; const char perspectiveCameraTypeName[] = "PerspectiveCamera"; const char orthographicCameraTypeName[] = "OrthographicCamera"; const char renderBufferTypeName[] = "RenderBuffer"; +const char renderBufferMSTypeName[] = "RenderBufferMS"; const char renderLayerTypeName[] = "RenderLayer"; const char renderPassTypeName[] = "RenderPass"; const char renderTargetTypeName[] = "RenderTarget"; diff --git a/datamodel/libCore/src/Queries.cpp b/datamodel/libCore/src/Queries.cpp index e19c500c..902c0e7b 100644 --- a/datamodel/libCore/src/Queries.cpp +++ b/datamodel/libCore/src/Queries.cpp @@ -422,6 +422,14 @@ bool Queries::isReadOnly(const Project& project, const ValueHandle& handle, bool } } + auto renderlayer = handle.rootObject()->as(); + if (renderlayer) { + if (*renderlayer->sortOrder_ == static_cast(user_types::ERenderLayerOrder::SceneGraph) && + ValueHandle(renderlayer, &user_types::RenderLayer::renderableTags_).contains(handle)) { + return true; + } + } + if (handle.query() && !handle.query()) { return true; } diff --git a/datamodel/libCore/src/Undo.cpp b/datamodel/libCore/src/Undo.cpp index bb9758ee..97f1eb39 100644 --- a/datamodel/libCore/src/Undo.cpp +++ b/datamodel/libCore/src/Undo.cpp @@ -74,7 +74,7 @@ void UndoHelpers::callOnBeforeRemoveReferenceHandler(raco::data_storage::Table * oldObj->onBeforeRemoveReferenceToThis(destHandle[index]); } } else if (hasTypeSubstructure(oldValue->type())) { - BaseContext::callReferenceToThisHandlerForAllTableEntries<&EditorObject::onBeforeRemoveReferenceToThis>(destHandle); + BaseContext::callReferenceToThisHandler<&EditorObject::onBeforeRemoveReferenceToThis>(destHandle); } } diff --git a/datamodel/libCore/tests/CMakeLists.txt b/datamodel/libCore/tests/CMakeLists.txt index 2a632709..d8d60347 100644 --- a/datamodel/libCore/tests/CMakeLists.txt +++ b/datamodel/libCore/tests/CMakeLists.txt @@ -45,6 +45,8 @@ raco_package_add_test_resources( shaders/basic.vert shaders/simple_texture.frag shaders/simple_texture.vert + shaders/uniform-array.vert + shaders/uniform-array.frag meshes/Duck.glb meshes/defaultQuad.gltf meshes/InterpolationTest/InterpolationTest.gltf @@ -58,6 +60,7 @@ raco_package_add_test_resources( scripts/struct-nested.lua scripts/SimpleScript.lua scripts/interface-scalar-types.lua + scripts/array.lua ) @@ -147,6 +150,8 @@ raco_package_add_test_resources( migrationTestData/V40.rca migrationTestData/V41.rca migrationTestData/V43.rca + migrationTestData/V44.rca + migrationTestData/V45.rca migrationTestData/version-current.rca ) add_compile_definitions(libSerialization_test PRIVATE CMAKE_CURRENT_SOURCE_DIR="${CMAKE_CURRENT_SOURCE_DIR}") diff --git a/datamodel/libCore/tests/Context_test.cpp b/datamodel/libCore/tests/Context_test.cpp index de3709b8..803c0377 100644 --- a/datamodel/libCore/tests/Context_test.cpp +++ b/datamodel/libCore/tests/Context_test.cpp @@ -36,6 +36,8 @@ using namespace raco::user_types; class ContextTest : public TestEnvironmentCore { public: + ContextTest() : TestEnvironmentCore(&TestObjectFactory::getInstance()) { + } void checkedDeleteObjects(std::vector const& objects) { auto numObjects = project.instances().size(); @@ -982,3 +984,54 @@ TEST_F(ContextTest, meshnode_shared_mat_modify_material) { context.set({material, {"uniforms", "u_color", "x"}}, 0.5); ASSERT_EQ(meshnode->getUniformContainer(0)->size(), 0); } + + +TEST_F(ContextTest, link_validity_change_add_remove_property) { + auto source = context.createObject(Foo::typeDescription.typeName, "foo"); + auto target = context.createObject(ObjectWithTableProperty::typeDescription.typeName, "mock"); + + context.addProperty({target, &ObjectWithTableProperty::t_}, "f", std::make_unique>(1.0, LinkEndAnnotation())); + + context.addLink({source, &Foo::x_}, {target, {"t", "f"}}); + checkLinks({{{source, {"x"}}, {target, {"t", "f"}}, true, false}}); + + context.removeProperty({target, &ObjectWithTableProperty::t_}, "f"); + checkLinks({{{source, {"x"}}, {target, {"t", "f"}}, false, false}}); + + context.addProperty({target, &ObjectWithTableProperty::t_}, "f", std::make_unique>(2.0, LinkEndAnnotation())); + checkLinks({{{source, {"x"}}, {target, {"t", "f"}}, true, false}}); +} + +TEST_F(ContextTest, link_validity_change_set_table) { + auto lua = context.createObject(LuaScript::typeDescription.typeName, "lua_script"); + TextFile scriptFile = makeFile("script.lua", R"( +function interface(IN,OUT) + OUT.int = Type:Int32() +end +function run(IN,OUT) +end +)"); + context.set({lua, &LuaScript::uri_}, scriptFile); + + auto mock = context.createObject(ObjectWithTableProperty::typeDescription.typeName, "mock"); + + raco::data_storage::Table tags; + tags.addProperty("main", new Property(1, {})); + context.set({mock, &ObjectWithTableProperty::renderableTags_}, tags); + + context.addLink({lua, {"outputs", "int"}}, {mock, {"renderableTags", "main"}}); + + checkLinks({{{lua, {"outputs", "int"}}, {mock, {"renderableTags", "main"}}, true, false}}); + + tags.removeProperty("main"); + tags.addProperty("alt", new Property(2, {})); + context.set({mock, &ObjectWithTableProperty::renderableTags_}, tags); + + checkLinks({{{lua, {"outputs", "int"}}, {mock, {"renderableTags", "main"}}, false, false}}); + + tags.removeProperty("alt"); + tags.addProperty("main", new Property(3, {})); + context.set({mock, &ObjectWithTableProperty::renderableTags_}, tags); + + checkLinks({{{lua, {"outputs", "int"}}, {mock, {"renderableTags", "main"}}, true, false}}); +} \ No newline at end of file diff --git a/datamodel/libCore/tests/Link_test.cpp b/datamodel/libCore/tests/Link_test.cpp index f0ff7500..53b07cae 100644 --- a/datamodel/libCore/tests/Link_test.cpp +++ b/datamodel/libCore/tests/Link_test.cpp @@ -1569,4 +1569,15 @@ end cmd.set({luaScript, &LuaScript::uri_}, script); app.doOneLoop(); checkLinks(*app.activeRaCoProject().project(), {{sprop, eprop, true}}); -} \ No newline at end of file +} + +TEST_F(LinkTest, uniform_array) { + auto mat = create_material("material", "shaders/uniform-array.vert", "shaders/uniform-array.frag"); + auto lua = create_lua("lua", "scripts/array.lua"); + + auto [spropel, epropel] = link(lua, {"outputs", "float_array", "1"}, mat, {"uniforms", "fvec", "2"}); + checkLinks({{spropel, epropel, true}}); + + auto [sproparr, eproparr] = link(lua, {"outputs", "float_array"}, mat, {"uniforms", "fvec"}); + checkLinks({{sproparr, eproparr, true}}); +} diff --git a/datamodel/libCore/tests/ProjectMigration_test.cpp b/datamodel/libCore/tests/ProjectMigration_test.cpp index 5f6d1cc2..5b4d7f31 100644 --- a/datamodel/libCore/tests/ProjectMigration_test.cpp +++ b/datamodel/libCore/tests/ProjectMigration_test.cpp @@ -663,6 +663,37 @@ TEST_F(MigrationTest, migrate_from_V43) { checkLinks(*racoproject->project(), {{{{lua, {"outputs", "float"}}, {pcam, {"frustum", "farPlane"}}}}}); } +TEST_F(MigrationTest, migrate_from_V44) { + auto racoproject = loadAndCheckJson(QString::fromStdString((test_path() / "migrationTestData" / "V44.rca").string())); + + auto layer = raco::core::Queries::findByName(racoproject->project()->instances(), "MainRenderLayer")->as(); + + auto anno_red = layer->renderableTags_->get("red")->query(); + EXPECT_TRUE(anno_red != nullptr); + EXPECT_EQ(*anno_red->featureLevel_, 3); + auto anno_green = layer->renderableTags_->get("green")->query(); + EXPECT_TRUE(anno_green != nullptr); + EXPECT_EQ(*anno_green->featureLevel_, 3); +} + +TEST_F(MigrationTest, migrate_from_V45) { + auto racoproject = loadAndCheckJson(QString::fromStdString((test_path() / "migrationTestData" / "V45.rca").string())); + + auto perspCamera = raco::core::Queries::findByName(racoproject->project()->instances(), "PerspectiveCamera")->as(); + + EXPECT_EQ(*perspCamera->viewport_->get("width")->query>()->min_, 1); + EXPECT_EQ(*perspCamera->viewport_->get("height")->query>()->min_, 1); + + auto orthoCamera = raco::core::Queries::findByName(racoproject->project()->instances(), "OrthographicCamera")->as(); + + EXPECT_EQ(*orthoCamera->viewport_->get("width")->query>()->min_, 1); + EXPECT_EQ(*orthoCamera->viewport_->get("height")->query>()->min_, 1); + + auto renderTarget = raco::core::Queries::findByName(racoproject->project()->instances(), "RenderTarget")->as(); + + EXPECT_TRUE(renderTarget->buffer0_.query() != nullptr); +} + TEST_F(MigrationTest, migrate_from_current) { // Check for changes in serialized JSON in newest version. diff --git a/datamodel/libCore/tests/migrationTestData/V44.rca b/datamodel/libCore/tests/migrationTestData/V44.rca new file mode 100644 index 00000000..3f298a03 --- /dev/null +++ b/datamodel/libCore/tests/migrationTestData/V44.rca @@ -0,0 +1,1821 @@ +{ + "externalProjects": { + }, + "featureLevel": 2, + "fileVersion": 44, + "instances": [ + { + "properties": { + "objectID": "0513da41-3cb1-4b9c-8611-b3fd9f54b1e0", + "objectName": "Material", + "options": { + "blendColor": { + "w": { + "annotations": [ + { + "properties": { + "max": 1, + "min": 0 + }, + "typeName": "RangeAnnotationDouble" + } + ], + "value": 0 + }, + "x": { + "annotations": [ + { + "properties": { + "max": 1, + "min": 0 + }, + "typeName": "RangeAnnotationDouble" + } + ], + "value": 0 + }, + "y": { + "annotations": [ + { + "properties": { + "max": 1, + "min": 0 + }, + "typeName": "RangeAnnotationDouble" + } + ], + "value": 0 + }, + "z": { + "annotations": [ + { + "properties": { + "max": 1, + "min": 0 + }, + "typeName": "RangeAnnotationDouble" + } + ], + "value": 0 + } + }, + "blendFactorDestAlpha": 1, + "blendFactorDestColor": 3, + "blendFactorSrcAlpha": 1, + "blendFactorSrcColor": 2, + "blendOperationAlpha": 0, + "blendOperationColor": 0, + "cullmode": 2, + "depthFunction": 4, + "depthwrite": true + }, + "uniforms": { + "order": [ + "u_color" + ], + "properties": { + "u_color": { + "annotations": [ + { + "properties": { + "engineType": 9 + }, + "typeName": "EngineTypeAnnotation" + }, + { + "properties": { + "featureLevel": 1 + }, + "typeName": "LinkEndAnnotation" + } + ], + "properties": { + "w": { + "annotations": [ + { + "properties": { + "max": 1, + "min": 0 + }, + "typeName": "RangeAnnotationDouble" + } + ], + "value": 1 + }, + "x": { + "annotations": [ + { + "properties": { + "max": 1, + "min": 0 + }, + "typeName": "RangeAnnotationDouble" + } + ], + "value": 0 + }, + "y": { + "annotations": [ + { + "properties": { + "max": 1, + "min": 0 + }, + "typeName": "RangeAnnotationDouble" + } + ], + "value": 0 + }, + "z": { + "annotations": [ + { + "properties": { + "max": 1, + "min": 0 + }, + "typeName": "RangeAnnotationDouble" + } + ], + "value": 0 + } + }, + "typeName": "Vec4f::EngineTypeAnnotation::LinkEndAnnotation" + } + } + }, + "uriDefines": "", + "uriFragment": "../../../../resources/shaders/basic-alpha.frag", + "uriGeometry": "", + "uriVertex": "../../../../resources/shaders/basic-alpha.vert" + }, + "typeName": "Material" + }, + { + "properties": { + "children": { + "properties": [ + { + "typeName": "Ref", + "value": "ff40c4f6-0eda-457a-888d-d22cd13e3112" + }, + { + "typeName": "Ref", + "value": "4966054b-d04c-4aec-a382-4b435cd886f9" + } + ] + }, + "enabled": true, + "objectID": "0fa1723e-3d39-4fdd-b215-6c79273adf82", + "objectName": "Node", + "rotation": { + "x": { + "annotations": [ + { + "properties": { + "max": 360, + "min": -360 + }, + "typeName": "RangeAnnotationDouble" + } + ], + "value": 0 + }, + "y": { + "annotations": [ + { + "properties": { + "max": 360, + "min": -360 + }, + "typeName": "RangeAnnotationDouble" + } + ], + "value": 0 + }, + "z": { + "annotations": [ + { + "properties": { + "max": 360, + "min": -360 + }, + "typeName": "RangeAnnotationDouble" + } + ], + "value": 0 + } + }, + "scaling": { + "x": { + "annotations": [ + { + "properties": { + "max": 100, + "min": 0.1 + }, + "typeName": "RangeAnnotationDouble" + } + ], + "value": 1 + }, + "y": { + "annotations": [ + { + "properties": { + "max": 100, + "min": 0.1 + }, + "typeName": "RangeAnnotationDouble" + } + ], + "value": 1 + }, + "z": { + "annotations": [ + { + "properties": { + "max": 100, + "min": 0.1 + }, + "typeName": "RangeAnnotationDouble" + } + ], + "value": 1 + } + }, + "tags": { + "properties": [ + { + "typeName": "String", + "value": "render_main" + } + ] + }, + "translation": { + "x": { + "annotations": [ + { + "properties": { + "max": 100, + "min": -100 + }, + "typeName": "RangeAnnotationDouble" + } + ], + "value": 0 + }, + "y": { + "annotations": [ + { + "properties": { + "max": 100, + "min": -100 + }, + "typeName": "RangeAnnotationDouble" + } + ], + "value": 0 + }, + "z": { + "annotations": [ + { + "properties": { + "max": 100, + "min": -100 + }, + "typeName": "RangeAnnotationDouble" + } + ], + "value": 0 + } + }, + "visibility": true + }, + "typeName": "Node" + }, + { + "properties": { + "materialFilterMode": 1, + "objectID": "2668b58e-5cdd-417a-b94f-9dd03a8f92ea", + "objectName": "MainRenderLayer", + "renderableTags": { + "order": [ + "green", + "red" + ], + "properties": { + "green": { + "typeName": "Int", + "value": 1 + }, + "red": { + "typeName": "Int", + "value": 2 + } + } + }, + "sortOrder": 0 + }, + "typeName": "RenderLayer" + }, + { + "properties": { + "enabled": true, + "frustum": { + "order": [ + "nearPlane", + "farPlane", + "fieldOfView", + "aspectRatio" + ], + "properties": { + "aspectRatio": { + "annotations": [ + { + "properties": { + "name": "aspectRatio" + }, + "typeName": "DisplayNameAnnotation" + }, + { + "properties": { + "max": 4, + "min": 0.5 + }, + "typeName": "RangeAnnotationDouble" + }, + { + "properties": { + "featureLevel": 1 + }, + "typeName": "LinkEndAnnotation" + } + ], + "typeName": "Double::DisplayNameAnnotation::RangeAnnotationDouble::LinkEndAnnotation", + "value": 2 + }, + "farPlane": { + "annotations": [ + { + "properties": { + "name": "farPlane" + }, + "typeName": "DisplayNameAnnotation" + }, + { + "properties": { + "max": 10000, + "min": 100 + }, + "typeName": "RangeAnnotationDouble" + }, + { + "properties": { + "featureLevel": 1 + }, + "typeName": "LinkEndAnnotation" + } + ], + "typeName": "Double::DisplayNameAnnotation::RangeAnnotationDouble::LinkEndAnnotation", + "value": 1000 + }, + "fieldOfView": { + "annotations": [ + { + "properties": { + "name": "fieldOfView" + }, + "typeName": "DisplayNameAnnotation" + }, + { + "properties": { + "max": 120, + "min": 10 + }, + "typeName": "RangeAnnotationDouble" + }, + { + "properties": { + "featureLevel": 1 + }, + "typeName": "LinkEndAnnotation" + } + ], + "typeName": "Double::DisplayNameAnnotation::RangeAnnotationDouble::LinkEndAnnotation", + "value": 35 + }, + "nearPlane": { + "annotations": [ + { + "properties": { + "name": "nearPlane" + }, + "typeName": "DisplayNameAnnotation" + }, + { + "properties": { + "max": 1, + "min": 0.1 + }, + "typeName": "RangeAnnotationDouble" + }, + { + "properties": { + "featureLevel": 1 + }, + "typeName": "LinkEndAnnotation" + } + ], + "typeName": "Double::DisplayNameAnnotation::RangeAnnotationDouble::LinkEndAnnotation", + "value": 0.1 + } + } + }, + "frustumType": 0, + "objectID": "2ecc317a-b704-4ff0-8f21-a3caec1c6887", + "objectName": "PerspectiveCamera", + "rotation": { + "x": { + "annotations": [ + { + "properties": { + "max": 360, + "min": -360 + }, + "typeName": "RangeAnnotationDouble" + } + ], + "value": 0 + }, + "y": { + "annotations": [ + { + "properties": { + "max": 360, + "min": -360 + }, + "typeName": "RangeAnnotationDouble" + } + ], + "value": 0 + }, + "z": { + "annotations": [ + { + "properties": { + "max": 360, + "min": -360 + }, + "typeName": "RangeAnnotationDouble" + } + ], + "value": 0 + } + }, + "scaling": { + "x": { + "annotations": [ + { + "properties": { + "max": 100, + "min": 0.1 + }, + "typeName": "RangeAnnotationDouble" + } + ], + "value": 1 + }, + "y": { + "annotations": [ + { + "properties": { + "max": 100, + "min": 0.1 + }, + "typeName": "RangeAnnotationDouble" + } + ], + "value": 1 + }, + "z": { + "annotations": [ + { + "properties": { + "max": 100, + "min": 0.1 + }, + "typeName": "RangeAnnotationDouble" + } + ], + "value": 1 + } + }, + "translation": { + "x": { + "annotations": [ + { + "properties": { + "max": 100, + "min": -100 + }, + "typeName": "RangeAnnotationDouble" + } + ], + "value": 0 + }, + "y": { + "annotations": [ + { + "properties": { + "max": 100, + "min": -100 + }, + "typeName": "RangeAnnotationDouble" + } + ], + "value": 0 + }, + "z": { + "annotations": [ + { + "properties": { + "max": 100, + "min": -100 + }, + "typeName": "RangeAnnotationDouble" + } + ], + "value": 10 + } + }, + "viewport": { + "height": { + "annotations": [ + { + "properties": { + "max": 7680, + "min": 0 + }, + "typeName": "RangeAnnotationInt" + } + ], + "value": 720 + }, + "offsetX": { + "annotations": [ + { + "properties": { + "max": 7680, + "min": -7680 + }, + "typeName": "RangeAnnotationInt" + } + ], + "value": 0 + }, + "offsetY": { + "annotations": [ + { + "properties": { + "max": 7680, + "min": -7680 + }, + "typeName": "RangeAnnotationInt" + } + ], + "value": 0 + }, + "width": { + "annotations": [ + { + "properties": { + "max": 7680, + "min": 0 + }, + "typeName": "RangeAnnotationInt" + } + ], + "value": 1440 + } + }, + "visibility": true + }, + "typeName": "PerspectiveCamera" + }, + { + "properties": { + "enabled": true, + "instanceCount": { + "annotations": [ + { + "properties": { + "max": 20, + "min": 1 + }, + "typeName": "RangeAnnotationInt" + } + ], + "value": 1 + }, + "materials": { + "order": [ + "material" + ], + "properties": { + "material": { + "order": [ + "material", + "private", + "options", + "uniforms" + ], + "properties": { + "material": { + "typeName": "Material", + "value": "0513da41-3cb1-4b9c-8611-b3fd9f54b1e0" + }, + "options": { + "annotations": [ + { + "properties": { + "name": "Options" + }, + "typeName": "DisplayNameAnnotation" + } + ], + "properties": { + "blendColor": { + "w": { + "annotations": [ + { + "properties": { + "max": 1, + "min": 0 + }, + "typeName": "RangeAnnotationDouble" + } + ], + "value": 0 + }, + "x": { + "annotations": [ + { + "properties": { + "max": 1, + "min": 0 + }, + "typeName": "RangeAnnotationDouble" + } + ], + "value": 0 + }, + "y": { + "annotations": [ + { + "properties": { + "max": 1, + "min": 0 + }, + "typeName": "RangeAnnotationDouble" + } + ], + "value": 0 + }, + "z": { + "annotations": [ + { + "properties": { + "max": 1, + "min": 0 + }, + "typeName": "RangeAnnotationDouble" + } + ], + "value": 0 + } + }, + "blendFactorDestAlpha": 1, + "blendFactorDestColor": 3, + "blendFactorSrcAlpha": 1, + "blendFactorSrcColor": 2, + "blendOperationAlpha": 1, + "blendOperationColor": 1, + "cullmode": 2, + "depthFunction": 4, + "depthwrite": true + }, + "typeName": "BlendOptions::DisplayNameAnnotation" + }, + "private": { + "annotations": [ + { + "properties": { + "name": "Private Material" + }, + "typeName": "DisplayNameAnnotation" + } + ], + "typeName": "Bool::DisplayNameAnnotation", + "value": true + }, + "uniforms": { + "order": [ + "u_color" + ], + "properties": { + "u_color": { + "annotations": [ + { + "properties": { + "engineType": 9 + }, + "typeName": "EngineTypeAnnotation" + }, + { + "properties": { + "featureLevel": 1 + }, + "typeName": "LinkEndAnnotation" + } + ], + "properties": { + "w": { + "annotations": [ + { + "properties": { + "max": 1, + "min": 0 + }, + "typeName": "RangeAnnotationDouble" + } + ], + "value": 0.5 + }, + "x": { + "annotations": [ + { + "properties": { + "max": 1, + "min": 0 + }, + "typeName": "RangeAnnotationDouble" + } + ], + "value": 0 + }, + "y": { + "annotations": [ + { + "properties": { + "max": 1, + "min": 0 + }, + "typeName": "RangeAnnotationDouble" + } + ], + "value": 1 + }, + "z": { + "annotations": [ + { + "properties": { + "max": 1, + "min": 0 + }, + "typeName": "RangeAnnotationDouble" + } + ], + "value": 0 + } + }, + "typeName": "Vec4f::EngineTypeAnnotation::LinkEndAnnotation" + } + }, + "typeName": "Table" + } + }, + "typeName": "Table" + } + } + }, + "mesh": "8dcb406c-3056-4de0-94bc-8f64e8d05306", + "objectID": "4966054b-d04c-4aec-a382-4b435cd886f9", + "objectName": "green", + "rotation": { + "x": { + "annotations": [ + { + "properties": { + "max": 360, + "min": -360 + }, + "typeName": "RangeAnnotationDouble" + } + ], + "value": 0 + }, + "y": { + "annotations": [ + { + "properties": { + "max": 360, + "min": -360 + }, + "typeName": "RangeAnnotationDouble" + } + ], + "value": 0 + }, + "z": { + "annotations": [ + { + "properties": { + "max": 360, + "min": -360 + }, + "typeName": "RangeAnnotationDouble" + } + ], + "value": 0 + } + }, + "scaling": { + "x": { + "annotations": [ + { + "properties": { + "max": 100, + "min": 0.1 + }, + "typeName": "RangeAnnotationDouble" + } + ], + "value": 1 + }, + "y": { + "annotations": [ + { + "properties": { + "max": 100, + "min": 0.1 + }, + "typeName": "RangeAnnotationDouble" + } + ], + "value": 1 + }, + "z": { + "annotations": [ + { + "properties": { + "max": 100, + "min": 0.1 + }, + "typeName": "RangeAnnotationDouble" + } + ], + "value": 1 + } + }, + "tags": { + "properties": [ + { + "typeName": "String", + "value": "green" + } + ] + }, + "translation": { + "x": { + "annotations": [ + { + "properties": { + "max": 100, + "min": -100 + }, + "typeName": "RangeAnnotationDouble" + } + ], + "value": 1.5151515151515151 + }, + "y": { + "annotations": [ + { + "properties": { + "max": 100, + "min": -100 + }, + "typeName": "RangeAnnotationDouble" + } + ], + "value": 0 + }, + "z": { + "annotations": [ + { + "properties": { + "max": 100, + "min": -100 + }, + "typeName": "RangeAnnotationDouble" + } + ], + "value": 0 + } + }, + "visibility": true + }, + "typeName": "MeshNode" + }, + { + "properties": { + "backgroundColor": { + "w": { + "annotations": [ + { + "properties": { + "max": 1, + "min": 0 + }, + "typeName": "RangeAnnotationDouble" + } + ], + "value": 1 + }, + "x": { + "annotations": [ + { + "properties": { + "max": 1, + "min": 0 + }, + "typeName": "RangeAnnotationDouble" + } + ], + "value": 0 + }, + "y": { + "annotations": [ + { + "properties": { + "max": 1, + "min": 0 + }, + "typeName": "RangeAnnotationDouble" + } + ], + "value": 0 + }, + "z": { + "annotations": [ + { + "properties": { + "max": 1, + "min": 0 + }, + "typeName": "RangeAnnotationDouble" + } + ], + "value": 0 + } + }, + "defaultResourceFolders": { + "imageSubdirectory": "images", + "interfaceSubdirectory": "interfaces", + "meshSubdirectory": "meshes", + "scriptSubdirectory": "qwerty", + "shaderSubdirectory": "shaders" + }, + "featureLevel": 2, + "objectID": "56fba03b-175e-481a-94f8-504b157a164d", + "objectName": "test-rendering", + "saveAsZip": false, + "sceneId": { + "annotations": [ + { + "properties": { + "max": 1024, + "min": 1 + }, + "typeName": "RangeAnnotationInt" + } + ], + "value": 123 + }, + "viewport": { + "i1": { + "annotations": [ + { + "properties": { + "max": 4096, + "min": 0 + }, + "typeName": "RangeAnnotationInt" + } + ], + "value": 1440 + }, + "i2": { + "annotations": [ + { + "properties": { + "max": 4096, + "min": 0 + }, + "typeName": "RangeAnnotationInt" + } + ], + "value": 720 + } + } + }, + "typeName": "ProjectSettings" + }, + { + "properties": { + "bakeMeshes": true, + "materialNames": { + "properties": [ + { + "typeName": "String", + "value": "material" + } + ] + }, + "meshIndex": 0, + "objectID": "8dcb406c-3056-4de0-94bc-8f64e8d05306", + "objectName": "Mesh", + "uri": "../../../../../../OneDrive - Paradox Cat GmbH/Documents/RamsesComposer/meshes/cube.gltf" + }, + "typeName": "Mesh" + }, + { + "properties": { + "camera": "2ecc317a-b704-4ff0-8f21-a3caec1c6887", + "clearColor": { + "w": { + "annotations": [ + { + "properties": { + "max": 1, + "min": 0 + }, + "typeName": "RangeAnnotationDouble" + } + ], + "value": 0 + }, + "x": { + "annotations": [ + { + "properties": { + "max": 1, + "min": 0 + }, + "typeName": "RangeAnnotationDouble" + } + ], + "value": 0 + }, + "y": { + "annotations": [ + { + "properties": { + "max": 1, + "min": 0 + }, + "typeName": "RangeAnnotationDouble" + } + ], + "value": 0 + }, + "z": { + "annotations": [ + { + "properties": { + "max": 1, + "min": 0 + }, + "typeName": "RangeAnnotationDouble" + } + ], + "value": 0 + } + }, + "enableClearColor": true, + "enableClearDepth": true, + "enableClearStencil": true, + "enabled": true, + "layer0": "2668b58e-5cdd-417a-b94f-9dd03a8f92ea", + "layer1": null, + "layer2": null, + "layer3": null, + "layer4": null, + "layer5": null, + "layer6": null, + "layer7": null, + "objectID": "cb0ce8ab-ca18-4b13-9bd2-43895ef9d208", + "objectName": "MainRenderPass", + "renderOnce": false, + "renderOrder": 1, + "target": null + }, + "typeName": "RenderPass" + }, + { + "properties": { + "enabled": true, + "instanceCount": { + "annotations": [ + { + "properties": { + "max": 20, + "min": 1 + }, + "typeName": "RangeAnnotationInt" + } + ], + "value": 1 + }, + "materials": { + "order": [ + "material" + ], + "properties": { + "material": { + "order": [ + "material", + "private", + "options", + "uniforms" + ], + "properties": { + "material": { + "typeName": "Material", + "value": "0513da41-3cb1-4b9c-8611-b3fd9f54b1e0" + }, + "options": { + "annotations": [ + { + "properties": { + "name": "Options" + }, + "typeName": "DisplayNameAnnotation" + } + ], + "properties": { + "blendColor": { + "w": { + "annotations": [ + { + "properties": { + "max": 1, + "min": 0 + }, + "typeName": "RangeAnnotationDouble" + } + ], + "value": 0 + }, + "x": { + "annotations": [ + { + "properties": { + "max": 1, + "min": 0 + }, + "typeName": "RangeAnnotationDouble" + } + ], + "value": 0 + }, + "y": { + "annotations": [ + { + "properties": { + "max": 1, + "min": 0 + }, + "typeName": "RangeAnnotationDouble" + } + ], + "value": 0 + }, + "z": { + "annotations": [ + { + "properties": { + "max": 1, + "min": 0 + }, + "typeName": "RangeAnnotationDouble" + } + ], + "value": 0 + } + }, + "blendFactorDestAlpha": 1, + "blendFactorDestColor": 3, + "blendFactorSrcAlpha": 1, + "blendFactorSrcColor": 2, + "blendOperationAlpha": 1, + "blendOperationColor": 1, + "cullmode": 2, + "depthFunction": 4, + "depthwrite": true + }, + "typeName": "BlendOptions::DisplayNameAnnotation" + }, + "private": { + "annotations": [ + { + "properties": { + "name": "Private Material" + }, + "typeName": "DisplayNameAnnotation" + } + ], + "typeName": "Bool::DisplayNameAnnotation", + "value": true + }, + "uniforms": { + "order": [ + "u_color" + ], + "properties": { + "u_color": { + "annotations": [ + { + "properties": { + "engineType": 9 + }, + "typeName": "EngineTypeAnnotation" + }, + { + "properties": { + "featureLevel": 1 + }, + "typeName": "LinkEndAnnotation" + } + ], + "properties": { + "w": { + "annotations": [ + { + "properties": { + "max": 1, + "min": 0 + }, + "typeName": "RangeAnnotationDouble" + } + ], + "value": 0.5 + }, + "x": { + "annotations": [ + { + "properties": { + "max": 1, + "min": 0 + }, + "typeName": "RangeAnnotationDouble" + } + ], + "value": 1 + }, + "y": { + "annotations": [ + { + "properties": { + "max": 1, + "min": 0 + }, + "typeName": "RangeAnnotationDouble" + } + ], + "value": 0 + }, + "z": { + "annotations": [ + { + "properties": { + "max": 1, + "min": 0 + }, + "typeName": "RangeAnnotationDouble" + } + ], + "value": 0 + } + }, + "typeName": "Vec4f::EngineTypeAnnotation::LinkEndAnnotation" + } + }, + "typeName": "Table" + } + }, + "typeName": "Table" + } + } + }, + "mesh": "8dcb406c-3056-4de0-94bc-8f64e8d05306", + "objectID": "ff40c4f6-0eda-457a-888d-d22cd13e3112", + "objectName": "red", + "rotation": { + "x": { + "annotations": [ + { + "properties": { + "max": 360, + "min": -360 + }, + "typeName": "RangeAnnotationDouble" + } + ], + "value": 0 + }, + "y": { + "annotations": [ + { + "properties": { + "max": 360, + "min": -360 + }, + "typeName": "RangeAnnotationDouble" + } + ], + "value": 0 + }, + "z": { + "annotations": [ + { + "properties": { + "max": 360, + "min": -360 + }, + "typeName": "RangeAnnotationDouble" + } + ], + "value": 0 + } + }, + "scaling": { + "x": { + "annotations": [ + { + "properties": { + "max": 100, + "min": 0.1 + }, + "typeName": "RangeAnnotationDouble" + } + ], + "value": 1 + }, + "y": { + "annotations": [ + { + "properties": { + "max": 100, + "min": 0.1 + }, + "typeName": "RangeAnnotationDouble" + } + ], + "value": 1 + }, + "z": { + "annotations": [ + { + "properties": { + "max": 100, + "min": 0.1 + }, + "typeName": "RangeAnnotationDouble" + } + ], + "value": 1 + } + }, + "tags": { + "properties": [ + { + "typeName": "String", + "value": "red" + } + ] + }, + "translation": { + "x": { + "annotations": [ + { + "properties": { + "max": 100, + "min": -100 + }, + "typeName": "RangeAnnotationDouble" + } + ], + "value": 0 + }, + "y": { + "annotations": [ + { + "properties": { + "max": 100, + "min": -100 + }, + "typeName": "RangeAnnotationDouble" + } + ], + "value": 0 + }, + "z": { + "annotations": [ + { + "properties": { + "max": 100, + "min": -100 + }, + "typeName": "RangeAnnotationDouble" + } + ], + "value": 0 + } + }, + "visibility": true + }, + "typeName": "MeshNode" + } + ], + "links": [ + ], + "logicEngineVersion": [ + 1, + 1, + 0 + ], + "racoVersion": [ + 1, + 4, + 0 + ], + "ramsesVersion": [ + 27, + 0, + 121 + ], + "structPropMap": { + "AnchorPointOutputs": { + "depth": "Double::DisplayNameAnnotation::LinkStartAnnotation", + "viewportCoords": "Vec2f::DisplayNameAnnotation::LinkStartAnnotation" + }, + "BlendOptions": { + "blendColor": "Vec4f::DisplayNameAnnotation", + "blendFactorDestAlpha": "Int::DisplayNameAnnotation::EnumerationAnnotation", + "blendFactorDestColor": "Int::DisplayNameAnnotation::EnumerationAnnotation", + "blendFactorSrcAlpha": "Int::DisplayNameAnnotation::EnumerationAnnotation", + "blendFactorSrcColor": "Int::DisplayNameAnnotation::EnumerationAnnotation", + "blendOperationAlpha": "Int::DisplayNameAnnotation::EnumerationAnnotation", + "blendOperationColor": "Int::DisplayNameAnnotation::EnumerationAnnotation", + "cullmode": "Int::DisplayNameAnnotation::EnumerationAnnotation", + "depthFunction": "Int::DisplayNameAnnotation::EnumerationAnnotation", + "depthwrite": "Bool::DisplayNameAnnotation" + }, + "CameraViewport": { + "height": "Int::RangeAnnotationInt::DisplayNameAnnotation::LinkEndAnnotation", + "offsetX": "Int::RangeAnnotationInt::DisplayNameAnnotation::LinkEndAnnotation", + "offsetY": "Int::RangeAnnotationInt::DisplayNameAnnotation::LinkEndAnnotation", + "width": "Int::RangeAnnotationInt::DisplayNameAnnotation::LinkEndAnnotation" + }, + "DefaultResourceDirectories": { + "imageSubdirectory": "String::DisplayNameAnnotation::URIAnnotation", + "interfaceSubdirectory": "String::DisplayNameAnnotation::URIAnnotation", + "meshSubdirectory": "String::DisplayNameAnnotation::URIAnnotation", + "scriptSubdirectory": "String::DisplayNameAnnotation::URIAnnotation", + "shaderSubdirectory": "String::DisplayNameAnnotation::URIAnnotation" + }, + "LuaStandardModuleSelection": { + "base": "Bool::DisplayNameAnnotation", + "debug": "Bool::DisplayNameAnnotation", + "math": "Bool::DisplayNameAnnotation", + "string": "Bool::DisplayNameAnnotation", + "table": "Bool::DisplayNameAnnotation" + }, + "OrthographicFrustum": { + "bottomPlane": "Double::DisplayNameAnnotation::RangeAnnotationDouble::LinkEndAnnotation", + "farPlane": "Double::DisplayNameAnnotation::RangeAnnotationDouble::LinkEndAnnotation", + "leftPlane": "Double::DisplayNameAnnotation::RangeAnnotationDouble::LinkEndAnnotation", + "nearPlane": "Double::DisplayNameAnnotation::RangeAnnotationDouble::LinkEndAnnotation", + "rightPlane": "Double::DisplayNameAnnotation::RangeAnnotationDouble::LinkEndAnnotation", + "topPlane": "Double::DisplayNameAnnotation::RangeAnnotationDouble::LinkEndAnnotation" + }, + "TimerInput": { + "ticker_us": "Int64::DisplayNameAnnotation::LinkEndAnnotation" + }, + "TimerOutput": { + "ticker_us": "Int64::DisplayNameAnnotation::LinkStartAnnotation" + }, + "Vec2f": { + "x": "Double::DisplayNameAnnotation::RangeAnnotationDouble", + "y": "Double::DisplayNameAnnotation::RangeAnnotationDouble" + }, + "Vec2i": { + "i1": "Int::DisplayNameAnnotation::RangeAnnotationInt", + "i2": "Int::DisplayNameAnnotation::RangeAnnotationInt" + }, + "Vec3f": { + "x": "Double::DisplayNameAnnotation::RangeAnnotationDouble", + "y": "Double::DisplayNameAnnotation::RangeAnnotationDouble", + "z": "Double::DisplayNameAnnotation::RangeAnnotationDouble" + }, + "Vec3i": { + "i1": "Int::DisplayNameAnnotation::RangeAnnotationInt", + "i2": "Int::DisplayNameAnnotation::RangeAnnotationInt", + "i3": "Int::DisplayNameAnnotation::RangeAnnotationInt" + }, + "Vec4f": { + "w": "Double::DisplayNameAnnotation::RangeAnnotationDouble", + "x": "Double::DisplayNameAnnotation::RangeAnnotationDouble", + "y": "Double::DisplayNameAnnotation::RangeAnnotationDouble", + "z": "Double::DisplayNameAnnotation::RangeAnnotationDouble" + }, + "Vec4i": { + "i1": "Int::DisplayNameAnnotation::RangeAnnotationInt", + "i2": "Int::DisplayNameAnnotation::RangeAnnotationInt", + "i3": "Int::DisplayNameAnnotation::RangeAnnotationInt", + "i4": "Int::DisplayNameAnnotation::RangeAnnotationInt" + } + }, + "userTypePropMap": { + "AnchorPoint": { + "camera": "BaseCamera::DisplayNameAnnotation", + "children": "Table::ArraySemanticAnnotation::HiddenProperty", + "node": "Node::DisplayNameAnnotation", + "objectID": "String::HiddenProperty", + "objectName": "String::DisplayNameAnnotation", + "outputs": "AnchorPointOutputs::DisplayNameAnnotation" + }, + "Animation": { + "animationChannels": "Table::DisplayNameAnnotation", + "children": "Table::ArraySemanticAnnotation::HiddenProperty", + "objectID": "String::HiddenProperty", + "objectName": "String::DisplayNameAnnotation", + "outputs": "Table::DisplayNameAnnotation", + "progress": "Double::DisplayNameAnnotation::RangeAnnotationDouble::LinkEndAnnotation" + }, + "AnimationChannel": { + "animationIndex": "Int::DisplayNameAnnotation", + "children": "Table::ArraySemanticAnnotation::HiddenProperty", + "objectID": "String::HiddenProperty", + "objectName": "String::DisplayNameAnnotation", + "samplerIndex": "Int::DisplayNameAnnotation", + "uri": "String::URIAnnotation::DisplayNameAnnotation" + }, + "CubeMap": { + "anisotropy": "Int::DisplayNameAnnotation::RangeAnnotationInt", + "children": "Table::ArraySemanticAnnotation::HiddenProperty", + "generateMipmaps": "Bool::DisplayNameAnnotation", + "level2uriBack": "String::URIAnnotation::DisplayNameAnnotation", + "level2uriBottom": "String::URIAnnotation::DisplayNameAnnotation", + "level2uriFront": "String::URIAnnotation::DisplayNameAnnotation", + "level2uriLeft": "String::URIAnnotation::DisplayNameAnnotation", + "level2uriRight": "String::URIAnnotation::DisplayNameAnnotation", + "level2uriTop": "String::URIAnnotation::DisplayNameAnnotation", + "level3uriBack": "String::URIAnnotation::DisplayNameAnnotation", + "level3uriBottom": "String::URIAnnotation::DisplayNameAnnotation", + "level3uriFront": "String::URIAnnotation::DisplayNameAnnotation", + "level3uriLeft": "String::URIAnnotation::DisplayNameAnnotation", + "level3uriRight": "String::URIAnnotation::DisplayNameAnnotation", + "level3uriTop": "String::URIAnnotation::DisplayNameAnnotation", + "level4uriBack": "String::URIAnnotation::DisplayNameAnnotation", + "level4uriBottom": "String::URIAnnotation::DisplayNameAnnotation", + "level4uriFront": "String::URIAnnotation::DisplayNameAnnotation", + "level4uriLeft": "String::URIAnnotation::DisplayNameAnnotation", + "level4uriRight": "String::URIAnnotation::DisplayNameAnnotation", + "level4uriTop": "String::URIAnnotation::DisplayNameAnnotation", + "magSamplingMethod": "Int::DisplayNameAnnotation::EnumerationAnnotation", + "minSamplingMethod": "Int::DisplayNameAnnotation::EnumerationAnnotation", + "mipmapLevel": "Int::DisplayNameAnnotation::RangeAnnotationInt", + "objectID": "String::HiddenProperty", + "objectName": "String::DisplayNameAnnotation", + "textureFormat": "Int::DisplayNameAnnotation::EnumerationAnnotation", + "uriBack": "String::URIAnnotation::DisplayNameAnnotation", + "uriBottom": "String::URIAnnotation::DisplayNameAnnotation", + "uriFront": "String::URIAnnotation::DisplayNameAnnotation", + "uriLeft": "String::URIAnnotation::DisplayNameAnnotation", + "uriRight": "String::URIAnnotation::DisplayNameAnnotation", + "uriTop": "String::URIAnnotation::DisplayNameAnnotation", + "wrapUMode": "Int::DisplayNameAnnotation::EnumerationAnnotation", + "wrapVMode": "Int::DisplayNameAnnotation::EnumerationAnnotation" + }, + "LuaInterface": { + "children": "Table::ArraySemanticAnnotation::HiddenProperty", + "inputs": "Table::DisplayNameAnnotation::LinkStartAnnotation::LinkEndAnnotation", + "objectID": "String::HiddenProperty", + "objectName": "String::DisplayNameAnnotation", + "uri": "String::URIAnnotation::DisplayNameAnnotation" + }, + "LuaScript": { + "children": "Table::ArraySemanticAnnotation::HiddenProperty", + "inputs": "Table::DisplayNameAnnotation::LinkEndAnnotation", + "luaModules": "Table::DisplayNameAnnotation", + "objectID": "String::HiddenProperty", + "objectName": "String::DisplayNameAnnotation", + "outputs": "Table::DisplayNameAnnotation", + "stdModules": "LuaStandardModuleSelection::DisplayNameAnnotation", + "uri": "String::URIAnnotation::DisplayNameAnnotation" + }, + "LuaScriptModule": { + "children": "Table::ArraySemanticAnnotation::HiddenProperty", + "objectID": "String::HiddenProperty", + "objectName": "String::DisplayNameAnnotation", + "stdModules": "LuaStandardModuleSelection::DisplayNameAnnotation", + "uri": "String::URIAnnotation::DisplayNameAnnotation" + }, + "Material": { + "children": "Table::ArraySemanticAnnotation::HiddenProperty", + "objectID": "String::HiddenProperty", + "objectName": "String::DisplayNameAnnotation", + "options": "BlendOptions::DisplayNameAnnotation", + "tags": "Table::ArraySemanticAnnotation::HiddenProperty::TagContainerAnnotation::DisplayNameAnnotation", + "uniforms": "Table::DisplayNameAnnotation", + "uriDefines": "String::URIAnnotation::DisplayNameAnnotation", + "uriFragment": "String::URIAnnotation::DisplayNameAnnotation", + "uriGeometry": "String::URIAnnotation::DisplayNameAnnotation", + "uriVertex": "String::URIAnnotation::DisplayNameAnnotation" + }, + "Mesh": { + "bakeMeshes": "Bool::DisplayNameAnnotation", + "children": "Table::ArraySemanticAnnotation::HiddenProperty", + "materialNames": "Table::ArraySemanticAnnotation::HiddenProperty", + "meshIndex": "Int::DisplayNameAnnotation", + "objectID": "String::HiddenProperty", + "objectName": "String::DisplayNameAnnotation", + "uri": "String::URIAnnotation::DisplayNameAnnotation" + }, + "MeshNode": { + "children": "Table::ArraySemanticAnnotation::HiddenProperty", + "enabled": "Bool::DisplayNameAnnotation::LinkEndAnnotation::FeatureLevel", + "instanceCount": "Int::DisplayNameAnnotation::RangeAnnotationInt", + "materials": "Table::DisplayNameAnnotation", + "mesh": "Mesh::DisplayNameAnnotation", + "objectID": "String::HiddenProperty", + "objectName": "String::DisplayNameAnnotation", + "rotation": "Vec3f::DisplayNameAnnotation::LinkEndAnnotation", + "scaling": "Vec3f::DisplayNameAnnotation::LinkEndAnnotation", + "tags": "Table::ArraySemanticAnnotation::HiddenProperty::TagContainerAnnotation::DisplayNameAnnotation", + "translation": "Vec3f::DisplayNameAnnotation::LinkEndAnnotation", + "visibility": "Bool::DisplayNameAnnotation::LinkEndAnnotation" + }, + "Node": { + "children": "Table::ArraySemanticAnnotation::HiddenProperty", + "enabled": "Bool::DisplayNameAnnotation::LinkEndAnnotation::FeatureLevel", + "objectID": "String::HiddenProperty", + "objectName": "String::DisplayNameAnnotation", + "rotation": "Vec3f::DisplayNameAnnotation::LinkEndAnnotation", + "scaling": "Vec3f::DisplayNameAnnotation::LinkEndAnnotation", + "tags": "Table::ArraySemanticAnnotation::HiddenProperty::TagContainerAnnotation::DisplayNameAnnotation", + "translation": "Vec3f::DisplayNameAnnotation::LinkEndAnnotation", + "visibility": "Bool::DisplayNameAnnotation::LinkEndAnnotation" + }, + "OrthographicCamera": { + "children": "Table::ArraySemanticAnnotation::HiddenProperty", + "enabled": "Bool::DisplayNameAnnotation::LinkEndAnnotation::FeatureLevel", + "frustum": "OrthographicFrustum::DisplayNameAnnotation::LinkEndAnnotation", + "objectID": "String::HiddenProperty", + "objectName": "String::DisplayNameAnnotation", + "rotation": "Vec3f::DisplayNameAnnotation::LinkEndAnnotation", + "scaling": "Vec3f::DisplayNameAnnotation::LinkEndAnnotation", + "tags": "Table::ArraySemanticAnnotation::HiddenProperty::TagContainerAnnotation::DisplayNameAnnotation", + "translation": "Vec3f::DisplayNameAnnotation::LinkEndAnnotation", + "viewport": "CameraViewport::DisplayNameAnnotation::LinkEndAnnotation", + "visibility": "Bool::DisplayNameAnnotation::LinkEndAnnotation" + }, + "PerspectiveCamera": { + "children": "Table::ArraySemanticAnnotation::HiddenProperty", + "enabled": "Bool::DisplayNameAnnotation::LinkEndAnnotation::FeatureLevel", + "frustum": "Table::DisplayNameAnnotation::LinkEndAnnotation", + "frustumType": "Int::DisplayNameAnnotation::EnumerationAnnotation::FeatureLevel", + "objectID": "String::HiddenProperty", + "objectName": "String::DisplayNameAnnotation", + "rotation": "Vec3f::DisplayNameAnnotation::LinkEndAnnotation", + "scaling": "Vec3f::DisplayNameAnnotation::LinkEndAnnotation", + "tags": "Table::ArraySemanticAnnotation::HiddenProperty::TagContainerAnnotation::DisplayNameAnnotation", + "translation": "Vec3f::DisplayNameAnnotation::LinkEndAnnotation", + "viewport": "CameraViewport::DisplayNameAnnotation::LinkEndAnnotation", + "visibility": "Bool::DisplayNameAnnotation::LinkEndAnnotation" + }, + "Prefab": { + "children": "Table::ArraySemanticAnnotation::HiddenProperty", + "objectID": "String::HiddenProperty", + "objectName": "String::DisplayNameAnnotation" + }, + "PrefabInstance": { + "children": "Table::ArraySemanticAnnotation::HiddenProperty", + "enabled": "Bool::DisplayNameAnnotation::LinkEndAnnotation::FeatureLevel", + "objectID": "String::HiddenProperty", + "objectName": "String::DisplayNameAnnotation", + "rotation": "Vec3f::DisplayNameAnnotation::LinkEndAnnotation", + "scaling": "Vec3f::DisplayNameAnnotation::LinkEndAnnotation", + "tags": "Table::ArraySemanticAnnotation::HiddenProperty::TagContainerAnnotation::DisplayNameAnnotation", + "template": "Prefab::DisplayNameAnnotation", + "translation": "Vec3f::DisplayNameAnnotation::LinkEndAnnotation", + "visibility": "Bool::DisplayNameAnnotation::LinkEndAnnotation" + }, + "ProjectSettings": { + "backgroundColor": "Vec4f::DisplayNameAnnotation", + "children": "Table::ArraySemanticAnnotation::HiddenProperty", + "defaultResourceFolders": "DefaultResourceDirectories::DisplayNameAnnotation", + "featureLevel": "Int::DisplayNameAnnotation::ReadOnlyAnnotation", + "objectID": "String::HiddenProperty", + "objectName": "String::DisplayNameAnnotation", + "saveAsZip": "Bool::DisplayNameAnnotation", + "sceneId": "Int::DisplayNameAnnotation::RangeAnnotationInt", + "viewport": "Vec2i::DisplayNameAnnotation" + }, + "RenderBuffer": { + "anisotropy": "Int::DisplayNameAnnotation::RangeAnnotationInt", + "children": "Table::ArraySemanticAnnotation::HiddenProperty", + "format": "Int::DisplayNameAnnotation::EnumerationAnnotation", + "height": "Int::RangeAnnotationInt::DisplayNameAnnotation", + "magSamplingMethod": "Int::DisplayNameAnnotation::EnumerationAnnotation", + "minSamplingMethod": "Int::DisplayNameAnnotation::EnumerationAnnotation", + "objectID": "String::HiddenProperty", + "objectName": "String::DisplayNameAnnotation", + "width": "Int::RangeAnnotationInt::DisplayNameAnnotation", + "wrapUMode": "Int::DisplayNameAnnotation::EnumerationAnnotation", + "wrapVMode": "Int::DisplayNameAnnotation::EnumerationAnnotation" + }, + "RenderLayer": { + "children": "Table::ArraySemanticAnnotation::HiddenProperty", + "materialFilterMode": "Int::DisplayNameAnnotation::EnumerationAnnotation", + "materialFilterTags": "Table::ArraySemanticAnnotation::HiddenProperty::TagContainerAnnotation::DisplayNameAnnotation", + "objectID": "String::HiddenProperty", + "objectName": "String::DisplayNameAnnotation", + "renderableTags": "Table::RenderableTagContainerAnnotation::HiddenProperty::DisplayNameAnnotation", + "sortOrder": "Int::DisplayNameAnnotation::EnumerationAnnotation", + "tags": "Table::ArraySemanticAnnotation::HiddenProperty::TagContainerAnnotation::DisplayNameAnnotation" + }, + "RenderPass": { + "camera": "BaseCamera::DisplayNameAnnotation", + "children": "Table::ArraySemanticAnnotation::HiddenProperty", + "clearColor": "Vec4f::DisplayNameAnnotation::LinkEndAnnotation", + "enableClearColor": "Bool::DisplayNameAnnotation", + "enableClearDepth": "Bool::DisplayNameAnnotation", + "enableClearStencil": "Bool::DisplayNameAnnotation", + "enabled": "Bool::DisplayNameAnnotation::LinkEndAnnotation", + "layer0": "RenderLayer::DisplayNameAnnotation", + "layer1": "RenderLayer::DisplayNameAnnotation::EmptyReferenceAllowable", + "layer2": "RenderLayer::DisplayNameAnnotation::EmptyReferenceAllowable", + "layer3": "RenderLayer::DisplayNameAnnotation::EmptyReferenceAllowable", + "layer4": "RenderLayer::DisplayNameAnnotation::EmptyReferenceAllowable", + "layer5": "RenderLayer::DisplayNameAnnotation::EmptyReferenceAllowable", + "layer6": "RenderLayer::DisplayNameAnnotation::EmptyReferenceAllowable", + "layer7": "RenderLayer::DisplayNameAnnotation::EmptyReferenceAllowable", + "objectID": "String::HiddenProperty", + "objectName": "String::DisplayNameAnnotation", + "renderOnce": "Bool::DisplayNameAnnotation::LinkEndAnnotation::FeatureLevel", + "renderOrder": "Int::DisplayNameAnnotation::LinkEndAnnotation", + "target": "RenderTarget::DisplayNameAnnotation::EmptyReferenceAllowable" + }, + "RenderTarget": { + "buffer0": "RenderBuffer::DisplayNameAnnotation", + "buffer1": "RenderBuffer::DisplayNameAnnotation::EmptyReferenceAllowable", + "buffer2": "RenderBuffer::DisplayNameAnnotation::EmptyReferenceAllowable", + "buffer3": "RenderBuffer::DisplayNameAnnotation::EmptyReferenceAllowable", + "buffer4": "RenderBuffer::DisplayNameAnnotation::EmptyReferenceAllowable", + "buffer5": "RenderBuffer::DisplayNameAnnotation::EmptyReferenceAllowable", + "buffer6": "RenderBuffer::DisplayNameAnnotation::EmptyReferenceAllowable", + "buffer7": "RenderBuffer::DisplayNameAnnotation::EmptyReferenceAllowable", + "children": "Table::ArraySemanticAnnotation::HiddenProperty", + "objectID": "String::HiddenProperty", + "objectName": "String::DisplayNameAnnotation" + }, + "Texture": { + "anisotropy": "Int::DisplayNameAnnotation::RangeAnnotationInt", + "children": "Table::ArraySemanticAnnotation::HiddenProperty", + "flipTexture": "Bool::DisplayNameAnnotation", + "generateMipmaps": "Bool::DisplayNameAnnotation", + "level2uri": "String::URIAnnotation::DisplayNameAnnotation", + "level3uri": "String::URIAnnotation::DisplayNameAnnotation", + "level4uri": "String::URIAnnotation::DisplayNameAnnotation", + "magSamplingMethod": "Int::DisplayNameAnnotation::EnumerationAnnotation", + "minSamplingMethod": "Int::DisplayNameAnnotation::EnumerationAnnotation", + "mipmapLevel": "Int::DisplayNameAnnotation::RangeAnnotationInt", + "objectID": "String::HiddenProperty", + "objectName": "String::DisplayNameAnnotation", + "textureFormat": "Int::DisplayNameAnnotation::EnumerationAnnotation", + "uri": "String::URIAnnotation::DisplayNameAnnotation", + "wrapUMode": "Int::DisplayNameAnnotation::EnumerationAnnotation", + "wrapVMode": "Int::DisplayNameAnnotation::EnumerationAnnotation" + }, + "Timer": { + "children": "Table::ArraySemanticAnnotation::HiddenProperty", + "inputs": "TimerInput::DisplayNameAnnotation", + "objectID": "String::HiddenProperty", + "objectName": "String::DisplayNameAnnotation", + "outputs": "TimerOutput::DisplayNameAnnotation" + } + } +} diff --git a/datamodel/libCore/tests/migrationTestData/V45.rca b/datamodel/libCore/tests/migrationTestData/V45.rca new file mode 100644 index 00000000..042fc75c --- /dev/null +++ b/datamodel/libCore/tests/migrationTestData/V45.rca @@ -0,0 +1,1076 @@ +{ + "externalProjects": { + }, + "featureLevel": 1, + "fileVersion": 45, + "instances": [ + { + "properties": { + "enabled": true, + "frustum": { + "bottomPlane": { + "annotations": [ + { + "properties": { + "max": 0, + "min": -1000 + }, + "typeName": "RangeAnnotationDouble" + } + ], + "value": -10 + }, + "farPlane": { + "annotations": [ + { + "properties": { + "max": 10000, + "min": 100 + }, + "typeName": "RangeAnnotationDouble" + } + ], + "value": 1000 + }, + "leftPlane": { + "annotations": [ + { + "properties": { + "max": 0, + "min": -1000 + }, + "typeName": "RangeAnnotationDouble" + } + ], + "value": -10 + }, + "nearPlane": { + "annotations": [ + { + "properties": { + "max": 1, + "min": 0.1 + }, + "typeName": "RangeAnnotationDouble" + } + ], + "value": 0.1 + }, + "rightPlane": { + "annotations": [ + { + "properties": { + "max": 1000, + "min": 0 + }, + "typeName": "RangeAnnotationDouble" + } + ], + "value": 10 + }, + "topPlane": { + "annotations": [ + { + "properties": { + "max": 1000, + "min": 0 + }, + "typeName": "RangeAnnotationDouble" + } + ], + "value": 10 + } + }, + "objectID": "308e3906-e06c-4e6d-a67a-760c1a8e0f5a", + "objectName": "OrthographicCamera", + "rotation": { + "x": { + "annotations": [ + { + "properties": { + "max": 360, + "min": -360 + }, + "typeName": "RangeAnnotationDouble" + } + ], + "value": 0 + }, + "y": { + "annotations": [ + { + "properties": { + "max": 360, + "min": -360 + }, + "typeName": "RangeAnnotationDouble" + } + ], + "value": 0 + }, + "z": { + "annotations": [ + { + "properties": { + "max": 360, + "min": -360 + }, + "typeName": "RangeAnnotationDouble" + } + ], + "value": 0 + } + }, + "scaling": { + "x": { + "annotations": [ + { + "properties": { + "max": 100, + "min": 0.1 + }, + "typeName": "RangeAnnotationDouble" + } + ], + "value": 1 + }, + "y": { + "annotations": [ + { + "properties": { + "max": 100, + "min": 0.1 + }, + "typeName": "RangeAnnotationDouble" + } + ], + "value": 1 + }, + "z": { + "annotations": [ + { + "properties": { + "max": 100, + "min": 0.1 + }, + "typeName": "RangeAnnotationDouble" + } + ], + "value": 1 + } + }, + "translation": { + "x": { + "annotations": [ + { + "properties": { + "max": 100, + "min": -100 + }, + "typeName": "RangeAnnotationDouble" + } + ], + "value": 0 + }, + "y": { + "annotations": [ + { + "properties": { + "max": 100, + "min": -100 + }, + "typeName": "RangeAnnotationDouble" + } + ], + "value": 0 + }, + "z": { + "annotations": [ + { + "properties": { + "max": 100, + "min": -100 + }, + "typeName": "RangeAnnotationDouble" + } + ], + "value": 0 + } + }, + "viewport": { + "height": { + "annotations": [ + { + "properties": { + "max": 7680, + "min": 0 + }, + "typeName": "RangeAnnotationInt" + } + ], + "value": 720 + }, + "offsetX": { + "annotations": [ + { + "properties": { + "max": 7680, + "min": -7680 + }, + "typeName": "RangeAnnotationInt" + } + ], + "value": 0 + }, + "offsetY": { + "annotations": [ + { + "properties": { + "max": 7680, + "min": -7680 + }, + "typeName": "RangeAnnotationInt" + } + ], + "value": 0 + }, + "width": { + "annotations": [ + { + "properties": { + "max": 7680, + "min": 0 + }, + "typeName": "RangeAnnotationInt" + } + ], + "value": 1440 + } + }, + "visibility": true + }, + "typeName": "OrthographicCamera" + }, + { + "properties": { + "backgroundColor": { + "w": { + "annotations": [ + { + "properties": { + "max": 1, + "min": 0 + }, + "typeName": "RangeAnnotationDouble" + } + ], + "value": 1 + }, + "x": { + "annotations": [ + { + "properties": { + "max": 1, + "min": 0 + }, + "typeName": "RangeAnnotationDouble" + } + ], + "value": 0 + }, + "y": { + "annotations": [ + { + "properties": { + "max": 1, + "min": 0 + }, + "typeName": "RangeAnnotationDouble" + } + ], + "value": 0 + }, + "z": { + "annotations": [ + { + "properties": { + "max": 1, + "min": 0 + }, + "typeName": "RangeAnnotationDouble" + } + ], + "value": 0 + } + }, + "defaultResourceFolders": { + "imageSubdirectory": "images", + "interfaceSubdirectory": "interfaces", + "meshSubdirectory": "meshes", + "scriptSubdirectory": "scripts", + "shaderSubdirectory": "shaders" + }, + "featureLevel": 1, + "objectID": "69c46284-6ff8-44e3-b8d0-3ef46fb6494d", + "objectName": "V45", + "saveAsZip": false, + "sceneId": { + "annotations": [ + { + "properties": { + "max": 1024, + "min": 1 + }, + "typeName": "RangeAnnotationInt" + } + ], + "value": 123 + }, + "viewport": { + "i1": { + "annotations": [ + { + "properties": { + "max": 4096, + "min": 0 + }, + "typeName": "RangeAnnotationInt" + } + ], + "value": 1440 + }, + "i2": { + "annotations": [ + { + "properties": { + "max": 4096, + "min": 0 + }, + "typeName": "RangeAnnotationInt" + } + ], + "value": 720 + } + } + }, + "typeName": "ProjectSettings" + }, + { + "properties": { + "anisotropy": { + "annotations": [ + { + "properties": { + "max": 32000, + "min": 1 + }, + "typeName": "RangeAnnotationInt" + } + ], + "value": 1 + }, + "format": 4, + "height": { + "annotations": [ + { + "properties": { + "max": 7680, + "min": 1 + }, + "typeName": "RangeAnnotationInt" + } + ], + "value": 256 + }, + "magSamplingMethod": 0, + "minSamplingMethod": 0, + "objectID": "c1233f24-68d1-451d-9715-e7b195e9fa0a", + "objectName": "RenderBuffer", + "width": { + "annotations": [ + { + "properties": { + "max": 7680, + "min": 1 + }, + "typeName": "RangeAnnotationInt" + } + ], + "value": 256 + }, + "wrapUMode": 0, + "wrapVMode": 0 + }, + "typeName": "RenderBuffer" + }, + { + "properties": { + "buffer0": "c1233f24-68d1-451d-9715-e7b195e9fa0a", + "buffer1": null, + "buffer2": null, + "buffer3": null, + "buffer4": null, + "buffer5": null, + "buffer6": null, + "buffer7": null, + "objectID": "ccc4ec6a-af2c-4dc1-91ce-6f8d05ec2acf", + "objectName": "RenderTarget" + }, + "typeName": "RenderTarget" + }, + { + "properties": { + "enabled": true, + "frustum": { + "order": [ + "nearPlane", + "farPlane", + "fieldOfView", + "aspectRatio" + ], + "properties": { + "aspectRatio": { + "annotations": [ + { + "properties": { + "name": "aspectRatio" + }, + "typeName": "DisplayNameAnnotation" + }, + { + "properties": { + "max": 4, + "min": 0.5 + }, + "typeName": "RangeAnnotationDouble" + }, + { + "properties": { + "featureLevel": 1 + }, + "typeName": "LinkEndAnnotation" + } + ], + "typeName": "Double::DisplayNameAnnotation::RangeAnnotationDouble::LinkEndAnnotation", + "value": 2 + }, + "farPlane": { + "annotations": [ + { + "properties": { + "name": "farPlane" + }, + "typeName": "DisplayNameAnnotation" + }, + { + "properties": { + "max": 10000, + "min": 100 + }, + "typeName": "RangeAnnotationDouble" + }, + { + "properties": { + "featureLevel": 1 + }, + "typeName": "LinkEndAnnotation" + } + ], + "typeName": "Double::DisplayNameAnnotation::RangeAnnotationDouble::LinkEndAnnotation", + "value": 1000 + }, + "fieldOfView": { + "annotations": [ + { + "properties": { + "name": "fieldOfView" + }, + "typeName": "DisplayNameAnnotation" + }, + { + "properties": { + "max": 120, + "min": 10 + }, + "typeName": "RangeAnnotationDouble" + }, + { + "properties": { + "featureLevel": 1 + }, + "typeName": "LinkEndAnnotation" + } + ], + "typeName": "Double::DisplayNameAnnotation::RangeAnnotationDouble::LinkEndAnnotation", + "value": 35 + }, + "nearPlane": { + "annotations": [ + { + "properties": { + "name": "nearPlane" + }, + "typeName": "DisplayNameAnnotation" + }, + { + "properties": { + "max": 1, + "min": 0.1 + }, + "typeName": "RangeAnnotationDouble" + }, + { + "properties": { + "featureLevel": 1 + }, + "typeName": "LinkEndAnnotation" + } + ], + "typeName": "Double::DisplayNameAnnotation::RangeAnnotationDouble::LinkEndAnnotation", + "value": 0.1 + } + } + }, + "frustumType": 0, + "objectID": "edafe11f-950a-42c8-ab32-a76c6f3a84bc", + "objectName": "PerspectiveCamera", + "rotation": { + "x": { + "annotations": [ + { + "properties": { + "max": 360, + "min": -360 + }, + "typeName": "RangeAnnotationDouble" + } + ], + "value": 0 + }, + "y": { + "annotations": [ + { + "properties": { + "max": 360, + "min": -360 + }, + "typeName": "RangeAnnotationDouble" + } + ], + "value": 0 + }, + "z": { + "annotations": [ + { + "properties": { + "max": 360, + "min": -360 + }, + "typeName": "RangeAnnotationDouble" + } + ], + "value": 0 + } + }, + "scaling": { + "x": { + "annotations": [ + { + "properties": { + "max": 100, + "min": 0.1 + }, + "typeName": "RangeAnnotationDouble" + } + ], + "value": 1 + }, + "y": { + "annotations": [ + { + "properties": { + "max": 100, + "min": 0.1 + }, + "typeName": "RangeAnnotationDouble" + } + ], + "value": 1 + }, + "z": { + "annotations": [ + { + "properties": { + "max": 100, + "min": 0.1 + }, + "typeName": "RangeAnnotationDouble" + } + ], + "value": 1 + } + }, + "translation": { + "x": { + "annotations": [ + { + "properties": { + "max": 100, + "min": -100 + }, + "typeName": "RangeAnnotationDouble" + } + ], + "value": 0 + }, + "y": { + "annotations": [ + { + "properties": { + "max": 100, + "min": -100 + }, + "typeName": "RangeAnnotationDouble" + } + ], + "value": 0 + }, + "z": { + "annotations": [ + { + "properties": { + "max": 100, + "min": -100 + }, + "typeName": "RangeAnnotationDouble" + } + ], + "value": 10 + } + }, + "viewport": { + "height": { + "annotations": [ + { + "properties": { + "max": 7680, + "min": 0 + }, + "typeName": "RangeAnnotationInt" + } + ], + "value": 720 + }, + "offsetX": { + "annotations": [ + { + "properties": { + "max": 7680, + "min": -7680 + }, + "typeName": "RangeAnnotationInt" + } + ], + "value": 0 + }, + "offsetY": { + "annotations": [ + { + "properties": { + "max": 7680, + "min": -7680 + }, + "typeName": "RangeAnnotationInt" + } + ], + "value": 0 + }, + "width": { + "annotations": [ + { + "properties": { + "max": 7680, + "min": 0 + }, + "typeName": "RangeAnnotationInt" + } + ], + "value": 1440 + } + }, + "visibility": true + }, + "typeName": "PerspectiveCamera" + } + ], + "links": [ + ], + "logicEngineVersion": [ + 1, + 2, + 2 + ], + "racoVersion": [ + 1, + 4, + 0 + ], + "ramsesVersion": [ + 27, + 0, + 125 + ], + "structPropMap": { + "AnchorPointOutputs": { + "depth": "Double::DisplayNameAnnotation::LinkStartAnnotation", + "viewportCoords": "Vec2f::DisplayNameAnnotation::LinkStartAnnotation" + }, + "BlendOptions": { + "blendColor": "Vec4f::DisplayNameAnnotation", + "blendFactorDestAlpha": "Int::DisplayNameAnnotation::EnumerationAnnotation", + "blendFactorDestColor": "Int::DisplayNameAnnotation::EnumerationAnnotation", + "blendFactorSrcAlpha": "Int::DisplayNameAnnotation::EnumerationAnnotation", + "blendFactorSrcColor": "Int::DisplayNameAnnotation::EnumerationAnnotation", + "blendOperationAlpha": "Int::DisplayNameAnnotation::EnumerationAnnotation", + "blendOperationColor": "Int::DisplayNameAnnotation::EnumerationAnnotation", + "cullmode": "Int::DisplayNameAnnotation::EnumerationAnnotation", + "depthFunction": "Int::DisplayNameAnnotation::EnumerationAnnotation", + "depthwrite": "Bool::DisplayNameAnnotation" + }, + "CameraViewport": { + "height": "Int::RangeAnnotationInt::DisplayNameAnnotation::LinkEndAnnotation", + "offsetX": "Int::RangeAnnotationInt::DisplayNameAnnotation::LinkEndAnnotation", + "offsetY": "Int::RangeAnnotationInt::DisplayNameAnnotation::LinkEndAnnotation", + "width": "Int::RangeAnnotationInt::DisplayNameAnnotation::LinkEndAnnotation" + }, + "DefaultResourceDirectories": { + "imageSubdirectory": "String::DisplayNameAnnotation::URIAnnotation", + "interfaceSubdirectory": "String::DisplayNameAnnotation::URIAnnotation", + "meshSubdirectory": "String::DisplayNameAnnotation::URIAnnotation", + "scriptSubdirectory": "String::DisplayNameAnnotation::URIAnnotation", + "shaderSubdirectory": "String::DisplayNameAnnotation::URIAnnotation" + }, + "LuaStandardModuleSelection": { + "base": "Bool::DisplayNameAnnotation", + "debug": "Bool::DisplayNameAnnotation", + "math": "Bool::DisplayNameAnnotation", + "string": "Bool::DisplayNameAnnotation", + "table": "Bool::DisplayNameAnnotation" + }, + "OrthographicFrustum": { + "bottomPlane": "Double::DisplayNameAnnotation::RangeAnnotationDouble::LinkEndAnnotation", + "farPlane": "Double::DisplayNameAnnotation::RangeAnnotationDouble::LinkEndAnnotation", + "leftPlane": "Double::DisplayNameAnnotation::RangeAnnotationDouble::LinkEndAnnotation", + "nearPlane": "Double::DisplayNameAnnotation::RangeAnnotationDouble::LinkEndAnnotation", + "rightPlane": "Double::DisplayNameAnnotation::RangeAnnotationDouble::LinkEndAnnotation", + "topPlane": "Double::DisplayNameAnnotation::RangeAnnotationDouble::LinkEndAnnotation" + }, + "TimerInput": { + "ticker_us": "Int64::DisplayNameAnnotation::LinkEndAnnotation" + }, + "TimerOutput": { + "ticker_us": "Int64::DisplayNameAnnotation::LinkStartAnnotation" + }, + "Vec2f": { + "x": "Double::DisplayNameAnnotation::RangeAnnotationDouble", + "y": "Double::DisplayNameAnnotation::RangeAnnotationDouble" + }, + "Vec2i": { + "i1": "Int::DisplayNameAnnotation::RangeAnnotationInt", + "i2": "Int::DisplayNameAnnotation::RangeAnnotationInt" + }, + "Vec3f": { + "x": "Double::DisplayNameAnnotation::RangeAnnotationDouble", + "y": "Double::DisplayNameAnnotation::RangeAnnotationDouble", + "z": "Double::DisplayNameAnnotation::RangeAnnotationDouble" + }, + "Vec3i": { + "i1": "Int::DisplayNameAnnotation::RangeAnnotationInt", + "i2": "Int::DisplayNameAnnotation::RangeAnnotationInt", + "i3": "Int::DisplayNameAnnotation::RangeAnnotationInt" + }, + "Vec4f": { + "w": "Double::DisplayNameAnnotation::RangeAnnotationDouble", + "x": "Double::DisplayNameAnnotation::RangeAnnotationDouble", + "y": "Double::DisplayNameAnnotation::RangeAnnotationDouble", + "z": "Double::DisplayNameAnnotation::RangeAnnotationDouble" + }, + "Vec4i": { + "i1": "Int::DisplayNameAnnotation::RangeAnnotationInt", + "i2": "Int::DisplayNameAnnotation::RangeAnnotationInt", + "i3": "Int::DisplayNameAnnotation::RangeAnnotationInt", + "i4": "Int::DisplayNameAnnotation::RangeAnnotationInt" + } + }, + "userTypePropMap": { + "AnchorPoint": { + "camera": "BaseCamera::DisplayNameAnnotation", + "children": "Table::ArraySemanticAnnotation::HiddenProperty", + "node": "Node::DisplayNameAnnotation", + "objectID": "String::HiddenProperty", + "objectName": "String::DisplayNameAnnotation", + "outputs": "AnchorPointOutputs::DisplayNameAnnotation" + }, + "Animation": { + "animationChannels": "Table::DisplayNameAnnotation", + "children": "Table::ArraySemanticAnnotation::HiddenProperty", + "objectID": "String::HiddenProperty", + "objectName": "String::DisplayNameAnnotation", + "outputs": "Table::DisplayNameAnnotation", + "progress": "Double::DisplayNameAnnotation::RangeAnnotationDouble::LinkEndAnnotation" + }, + "AnimationChannel": { + "animationIndex": "Int::DisplayNameAnnotation", + "children": "Table::ArraySemanticAnnotation::HiddenProperty", + "objectID": "String::HiddenProperty", + "objectName": "String::DisplayNameAnnotation", + "samplerIndex": "Int::DisplayNameAnnotation", + "uri": "String::URIAnnotation::DisplayNameAnnotation" + }, + "CubeMap": { + "anisotropy": "Int::DisplayNameAnnotation::RangeAnnotationInt", + "children": "Table::ArraySemanticAnnotation::HiddenProperty", + "generateMipmaps": "Bool::DisplayNameAnnotation", + "level2uriBack": "String::URIAnnotation::DisplayNameAnnotation", + "level2uriBottom": "String::URIAnnotation::DisplayNameAnnotation", + "level2uriFront": "String::URIAnnotation::DisplayNameAnnotation", + "level2uriLeft": "String::URIAnnotation::DisplayNameAnnotation", + "level2uriRight": "String::URIAnnotation::DisplayNameAnnotation", + "level2uriTop": "String::URIAnnotation::DisplayNameAnnotation", + "level3uriBack": "String::URIAnnotation::DisplayNameAnnotation", + "level3uriBottom": "String::URIAnnotation::DisplayNameAnnotation", + "level3uriFront": "String::URIAnnotation::DisplayNameAnnotation", + "level3uriLeft": "String::URIAnnotation::DisplayNameAnnotation", + "level3uriRight": "String::URIAnnotation::DisplayNameAnnotation", + "level3uriTop": "String::URIAnnotation::DisplayNameAnnotation", + "level4uriBack": "String::URIAnnotation::DisplayNameAnnotation", + "level4uriBottom": "String::URIAnnotation::DisplayNameAnnotation", + "level4uriFront": "String::URIAnnotation::DisplayNameAnnotation", + "level4uriLeft": "String::URIAnnotation::DisplayNameAnnotation", + "level4uriRight": "String::URIAnnotation::DisplayNameAnnotation", + "level4uriTop": "String::URIAnnotation::DisplayNameAnnotation", + "magSamplingMethod": "Int::DisplayNameAnnotation::EnumerationAnnotation", + "minSamplingMethod": "Int::DisplayNameAnnotation::EnumerationAnnotation", + "mipmapLevel": "Int::DisplayNameAnnotation::RangeAnnotationInt", + "objectID": "String::HiddenProperty", + "objectName": "String::DisplayNameAnnotation", + "textureFormat": "Int::DisplayNameAnnotation::EnumerationAnnotation", + "uriBack": "String::URIAnnotation::DisplayNameAnnotation", + "uriBottom": "String::URIAnnotation::DisplayNameAnnotation", + "uriFront": "String::URIAnnotation::DisplayNameAnnotation", + "uriLeft": "String::URIAnnotation::DisplayNameAnnotation", + "uriRight": "String::URIAnnotation::DisplayNameAnnotation", + "uriTop": "String::URIAnnotation::DisplayNameAnnotation", + "wrapUMode": "Int::DisplayNameAnnotation::EnumerationAnnotation", + "wrapVMode": "Int::DisplayNameAnnotation::EnumerationAnnotation" + }, + "LuaInterface": { + "children": "Table::ArraySemanticAnnotation::HiddenProperty", + "inputs": "Table::DisplayNameAnnotation::LinkStartAnnotation::LinkEndAnnotation", + "objectID": "String::HiddenProperty", + "objectName": "String::DisplayNameAnnotation", + "uri": "String::URIAnnotation::DisplayNameAnnotation" + }, + "LuaScript": { + "children": "Table::ArraySemanticAnnotation::HiddenProperty", + "inputs": "Table::DisplayNameAnnotation::LinkEndAnnotation", + "luaModules": "Table::DisplayNameAnnotation", + "objectID": "String::HiddenProperty", + "objectName": "String::DisplayNameAnnotation", + "outputs": "Table::DisplayNameAnnotation", + "stdModules": "LuaStandardModuleSelection::DisplayNameAnnotation", + "uri": "String::URIAnnotation::DisplayNameAnnotation" + }, + "LuaScriptModule": { + "children": "Table::ArraySemanticAnnotation::HiddenProperty", + "objectID": "String::HiddenProperty", + "objectName": "String::DisplayNameAnnotation", + "stdModules": "LuaStandardModuleSelection::DisplayNameAnnotation", + "uri": "String::URIAnnotation::DisplayNameAnnotation" + }, + "Material": { + "children": "Table::ArraySemanticAnnotation::HiddenProperty", + "objectID": "String::HiddenProperty", + "objectName": "String::DisplayNameAnnotation", + "options": "BlendOptions::DisplayNameAnnotation", + "tags": "Table::ArraySemanticAnnotation::HiddenProperty::TagContainerAnnotation::DisplayNameAnnotation", + "uniforms": "Table::DisplayNameAnnotation", + "uriDefines": "String::URIAnnotation::DisplayNameAnnotation", + "uriFragment": "String::URIAnnotation::DisplayNameAnnotation", + "uriGeometry": "String::URIAnnotation::DisplayNameAnnotation", + "uriVertex": "String::URIAnnotation::DisplayNameAnnotation" + }, + "Mesh": { + "bakeMeshes": "Bool::DisplayNameAnnotation", + "children": "Table::ArraySemanticAnnotation::HiddenProperty", + "materialNames": "Table::ArraySemanticAnnotation::HiddenProperty", + "meshIndex": "Int::DisplayNameAnnotation", + "objectID": "String::HiddenProperty", + "objectName": "String::DisplayNameAnnotation", + "uri": "String::URIAnnotation::DisplayNameAnnotation" + }, + "MeshNode": { + "children": "Table::ArraySemanticAnnotation::HiddenProperty", + "enabled": "Bool::DisplayNameAnnotation::LinkEndAnnotation::FeatureLevel", + "instanceCount": "Int::DisplayNameAnnotation::RangeAnnotationInt", + "materials": "Table::DisplayNameAnnotation", + "mesh": "Mesh::DisplayNameAnnotation", + "objectID": "String::HiddenProperty", + "objectName": "String::DisplayNameAnnotation", + "rotation": "Vec3f::DisplayNameAnnotation::LinkEndAnnotation", + "scaling": "Vec3f::DisplayNameAnnotation::LinkEndAnnotation", + "tags": "Table::ArraySemanticAnnotation::HiddenProperty::TagContainerAnnotation::DisplayNameAnnotation", + "translation": "Vec3f::DisplayNameAnnotation::LinkEndAnnotation", + "visibility": "Bool::DisplayNameAnnotation::LinkEndAnnotation" + }, + "Node": { + "children": "Table::ArraySemanticAnnotation::HiddenProperty", + "enabled": "Bool::DisplayNameAnnotation::LinkEndAnnotation::FeatureLevel", + "objectID": "String::HiddenProperty", + "objectName": "String::DisplayNameAnnotation", + "rotation": "Vec3f::DisplayNameAnnotation::LinkEndAnnotation", + "scaling": "Vec3f::DisplayNameAnnotation::LinkEndAnnotation", + "tags": "Table::ArraySemanticAnnotation::HiddenProperty::TagContainerAnnotation::DisplayNameAnnotation", + "translation": "Vec3f::DisplayNameAnnotation::LinkEndAnnotation", + "visibility": "Bool::DisplayNameAnnotation::LinkEndAnnotation" + }, + "OrthographicCamera": { + "children": "Table::ArraySemanticAnnotation::HiddenProperty", + "enabled": "Bool::DisplayNameAnnotation::LinkEndAnnotation::FeatureLevel", + "frustum": "OrthographicFrustum::DisplayNameAnnotation::LinkEndAnnotation", + "objectID": "String::HiddenProperty", + "objectName": "String::DisplayNameAnnotation", + "rotation": "Vec3f::DisplayNameAnnotation::LinkEndAnnotation", + "scaling": "Vec3f::DisplayNameAnnotation::LinkEndAnnotation", + "tags": "Table::ArraySemanticAnnotation::HiddenProperty::TagContainerAnnotation::DisplayNameAnnotation", + "translation": "Vec3f::DisplayNameAnnotation::LinkEndAnnotation", + "viewport": "CameraViewport::DisplayNameAnnotation::LinkEndAnnotation", + "visibility": "Bool::DisplayNameAnnotation::LinkEndAnnotation" + }, + "PerspectiveCamera": { + "children": "Table::ArraySemanticAnnotation::HiddenProperty", + "enabled": "Bool::DisplayNameAnnotation::LinkEndAnnotation::FeatureLevel", + "frustum": "Table::DisplayNameAnnotation::LinkEndAnnotation", + "frustumType": "Int::DisplayNameAnnotation::EnumerationAnnotation::FeatureLevel", + "objectID": "String::HiddenProperty", + "objectName": "String::DisplayNameAnnotation", + "rotation": "Vec3f::DisplayNameAnnotation::LinkEndAnnotation", + "scaling": "Vec3f::DisplayNameAnnotation::LinkEndAnnotation", + "tags": "Table::ArraySemanticAnnotation::HiddenProperty::TagContainerAnnotation::DisplayNameAnnotation", + "translation": "Vec3f::DisplayNameAnnotation::LinkEndAnnotation", + "viewport": "CameraViewport::DisplayNameAnnotation::LinkEndAnnotation", + "visibility": "Bool::DisplayNameAnnotation::LinkEndAnnotation" + }, + "Prefab": { + "children": "Table::ArraySemanticAnnotation::HiddenProperty", + "objectID": "String::HiddenProperty", + "objectName": "String::DisplayNameAnnotation" + }, + "PrefabInstance": { + "children": "Table::ArraySemanticAnnotation::HiddenProperty", + "enabled": "Bool::DisplayNameAnnotation::LinkEndAnnotation::FeatureLevel", + "objectID": "String::HiddenProperty", + "objectName": "String::DisplayNameAnnotation", + "rotation": "Vec3f::DisplayNameAnnotation::LinkEndAnnotation", + "scaling": "Vec3f::DisplayNameAnnotation::LinkEndAnnotation", + "tags": "Table::ArraySemanticAnnotation::HiddenProperty::TagContainerAnnotation::DisplayNameAnnotation", + "template": "Prefab::DisplayNameAnnotation", + "translation": "Vec3f::DisplayNameAnnotation::LinkEndAnnotation", + "visibility": "Bool::DisplayNameAnnotation::LinkEndAnnotation" + }, + "ProjectSettings": { + "backgroundColor": "Vec4f::DisplayNameAnnotation", + "children": "Table::ArraySemanticAnnotation::HiddenProperty", + "defaultResourceFolders": "DefaultResourceDirectories::DisplayNameAnnotation", + "featureLevel": "Int::DisplayNameAnnotation::ReadOnlyAnnotation", + "objectID": "String::HiddenProperty", + "objectName": "String::DisplayNameAnnotation", + "saveAsZip": "Bool::DisplayNameAnnotation", + "sceneId": "Int::DisplayNameAnnotation::RangeAnnotationInt", + "viewport": "Vec2i::DisplayNameAnnotation" + }, + "RenderBuffer": { + "anisotropy": "Int::DisplayNameAnnotation::RangeAnnotationInt", + "children": "Table::ArraySemanticAnnotation::HiddenProperty", + "format": "Int::DisplayNameAnnotation::EnumerationAnnotation", + "height": "Int::RangeAnnotationInt::DisplayNameAnnotation", + "magSamplingMethod": "Int::DisplayNameAnnotation::EnumerationAnnotation", + "minSamplingMethod": "Int::DisplayNameAnnotation::EnumerationAnnotation", + "objectID": "String::HiddenProperty", + "objectName": "String::DisplayNameAnnotation", + "width": "Int::RangeAnnotationInt::DisplayNameAnnotation", + "wrapUMode": "Int::DisplayNameAnnotation::EnumerationAnnotation", + "wrapVMode": "Int::DisplayNameAnnotation::EnumerationAnnotation" + }, + "RenderLayer": { + "children": "Table::ArraySemanticAnnotation::HiddenProperty", + "materialFilterMode": "Int::DisplayNameAnnotation::EnumerationAnnotation", + "materialFilterTags": "Table::ArraySemanticAnnotation::HiddenProperty::TagContainerAnnotation::DisplayNameAnnotation", + "objectID": "String::HiddenProperty", + "objectName": "String::DisplayNameAnnotation", + "renderableTags": "Table::RenderableTagContainerAnnotation::DisplayNameAnnotation", + "sortOrder": "Int::DisplayNameAnnotation::EnumerationAnnotation", + "tags": "Table::ArraySemanticAnnotation::HiddenProperty::TagContainerAnnotation::DisplayNameAnnotation" + }, + "RenderPass": { + "camera": "BaseCamera::DisplayNameAnnotation", + "children": "Table::ArraySemanticAnnotation::HiddenProperty", + "clearColor": "Vec4f::DisplayNameAnnotation::LinkEndAnnotation", + "enableClearColor": "Bool::DisplayNameAnnotation", + "enableClearDepth": "Bool::DisplayNameAnnotation", + "enableClearStencil": "Bool::DisplayNameAnnotation", + "enabled": "Bool::DisplayNameAnnotation::LinkEndAnnotation", + "layer0": "RenderLayer::DisplayNameAnnotation", + "layer1": "RenderLayer::DisplayNameAnnotation::EmptyReferenceAllowable", + "layer2": "RenderLayer::DisplayNameAnnotation::EmptyReferenceAllowable", + "layer3": "RenderLayer::DisplayNameAnnotation::EmptyReferenceAllowable", + "layer4": "RenderLayer::DisplayNameAnnotation::EmptyReferenceAllowable", + "layer5": "RenderLayer::DisplayNameAnnotation::EmptyReferenceAllowable", + "layer6": "RenderLayer::DisplayNameAnnotation::EmptyReferenceAllowable", + "layer7": "RenderLayer::DisplayNameAnnotation::EmptyReferenceAllowable", + "objectID": "String::HiddenProperty", + "objectName": "String::DisplayNameAnnotation", + "renderOnce": "Bool::DisplayNameAnnotation::LinkEndAnnotation::FeatureLevel", + "renderOrder": "Int::DisplayNameAnnotation::LinkEndAnnotation", + "target": "RenderTarget::DisplayNameAnnotation::EmptyReferenceAllowable" + }, + "RenderTarget": { + "buffer0": "RenderBuffer::DisplayNameAnnotation", + "buffer1": "RenderBuffer::DisplayNameAnnotation::EmptyReferenceAllowable", + "buffer2": "RenderBuffer::DisplayNameAnnotation::EmptyReferenceAllowable", + "buffer3": "RenderBuffer::DisplayNameAnnotation::EmptyReferenceAllowable", + "buffer4": "RenderBuffer::DisplayNameAnnotation::EmptyReferenceAllowable", + "buffer5": "RenderBuffer::DisplayNameAnnotation::EmptyReferenceAllowable", + "buffer6": "RenderBuffer::DisplayNameAnnotation::EmptyReferenceAllowable", + "buffer7": "RenderBuffer::DisplayNameAnnotation::EmptyReferenceAllowable", + "children": "Table::ArraySemanticAnnotation::HiddenProperty", + "objectID": "String::HiddenProperty", + "objectName": "String::DisplayNameAnnotation" + }, + "Texture": { + "anisotropy": "Int::DisplayNameAnnotation::RangeAnnotationInt", + "children": "Table::ArraySemanticAnnotation::HiddenProperty", + "flipTexture": "Bool::DisplayNameAnnotation", + "generateMipmaps": "Bool::DisplayNameAnnotation", + "level2uri": "String::URIAnnotation::DisplayNameAnnotation", + "level3uri": "String::URIAnnotation::DisplayNameAnnotation", + "level4uri": "String::URIAnnotation::DisplayNameAnnotation", + "magSamplingMethod": "Int::DisplayNameAnnotation::EnumerationAnnotation", + "minSamplingMethod": "Int::DisplayNameAnnotation::EnumerationAnnotation", + "mipmapLevel": "Int::DisplayNameAnnotation::RangeAnnotationInt", + "objectID": "String::HiddenProperty", + "objectName": "String::DisplayNameAnnotation", + "textureFormat": "Int::DisplayNameAnnotation::EnumerationAnnotation", + "uri": "String::URIAnnotation::DisplayNameAnnotation", + "wrapUMode": "Int::DisplayNameAnnotation::EnumerationAnnotation", + "wrapVMode": "Int::DisplayNameAnnotation::EnumerationAnnotation" + }, + "Timer": { + "children": "Table::ArraySemanticAnnotation::HiddenProperty", + "inputs": "TimerInput::DisplayNameAnnotation", + "objectID": "String::HiddenProperty", + "objectName": "String::DisplayNameAnnotation", + "outputs": "TimerOutput::DisplayNameAnnotation" + } + } +} diff --git a/datamodel/libCore/tests/migrationTestData/version-current.rca b/datamodel/libCore/tests/migrationTestData/version-current.rca index 621a9422..594ceee5 100644 --- a/datamodel/libCore/tests/migrationTestData/version-current.rca +++ b/datamodel/libCore/tests/migrationTestData/version-current.rca @@ -1,8 +1,8 @@ { "externalProjects": { }, - "featureLevel": 2, - "fileVersion": 44, + "featureLevel": 3, + "fileVersion": 46, "instances": [ { "properties": { @@ -36,6 +36,14 @@ "buffer5": null, "buffer6": null, "buffer7": null, + "bufferMS0": null, + "bufferMS1": null, + "bufferMS2": null, + "bufferMS3": null, + "bufferMS4": null, + "bufferMS5": null, + "bufferMS6": null, + "bufferMS7": null, "objectID": "077b744c-c82d-467d-a406-4fca3603ae8a", "objectName": "RenderTarget" }, @@ -402,6 +410,253 @@ }, "typeName": "RenderBuffer" }, + { + "properties": { + "enabled": true, + "frustum": { + "bottomPlane": { + "annotations": [ + { + "properties": { + "max": 0, + "min": -1000 + }, + "typeName": "RangeAnnotationDouble" + } + ], + "value": -10 + }, + "farPlane": { + "annotations": [ + { + "properties": { + "max": 10000, + "min": 100 + }, + "typeName": "RangeAnnotationDouble" + } + ], + "value": 1000 + }, + "leftPlane": { + "annotations": [ + { + "properties": { + "max": 0, + "min": -1000 + }, + "typeName": "RangeAnnotationDouble" + } + ], + "value": -10 + }, + "nearPlane": { + "annotations": [ + { + "properties": { + "max": 1, + "min": 0.1 + }, + "typeName": "RangeAnnotationDouble" + } + ], + "value": 0.1 + }, + "rightPlane": { + "annotations": [ + { + "properties": { + "max": 1000, + "min": 0 + }, + "typeName": "RangeAnnotationDouble" + } + ], + "value": 10 + }, + "topPlane": { + "annotations": [ + { + "properties": { + "max": 1000, + "min": 0 + }, + "typeName": "RangeAnnotationDouble" + } + ], + "value": 10 + } + }, + "objectID": "2bf34d57-3ad1-48a4-ac27-ace003f55167", + "objectName": "OrthographicCamera", + "rotation": { + "x": { + "annotations": [ + { + "properties": { + "max": 360, + "min": -360 + }, + "typeName": "RangeAnnotationDouble" + } + ], + "value": 0 + }, + "y": { + "annotations": [ + { + "properties": { + "max": 360, + "min": -360 + }, + "typeName": "RangeAnnotationDouble" + } + ], + "value": 0 + }, + "z": { + "annotations": [ + { + "properties": { + "max": 360, + "min": -360 + }, + "typeName": "RangeAnnotationDouble" + } + ], + "value": 0 + } + }, + "scaling": { + "x": { + "annotations": [ + { + "properties": { + "max": 100, + "min": 0.1 + }, + "typeName": "RangeAnnotationDouble" + } + ], + "value": 1 + }, + "y": { + "annotations": [ + { + "properties": { + "max": 100, + "min": 0.1 + }, + "typeName": "RangeAnnotationDouble" + } + ], + "value": 1 + }, + "z": { + "annotations": [ + { + "properties": { + "max": 100, + "min": 0.1 + }, + "typeName": "RangeAnnotationDouble" + } + ], + "value": 1 + } + }, + "translation": { + "x": { + "annotations": [ + { + "properties": { + "max": 100, + "min": -100 + }, + "typeName": "RangeAnnotationDouble" + } + ], + "value": 0 + }, + "y": { + "annotations": [ + { + "properties": { + "max": 100, + "min": -100 + }, + "typeName": "RangeAnnotationDouble" + } + ], + "value": 0 + }, + "z": { + "annotations": [ + { + "properties": { + "max": 100, + "min": -100 + }, + "typeName": "RangeAnnotationDouble" + } + ], + "value": 0 + } + }, + "viewport": { + "height": { + "annotations": [ + { + "properties": { + "max": 7680, + "min": 1 + }, + "typeName": "RangeAnnotationInt" + } + ], + "value": 720 + }, + "offsetX": { + "annotations": [ + { + "properties": { + "max": 7680, + "min": -7680 + }, + "typeName": "RangeAnnotationInt" + } + ], + "value": 0 + }, + "offsetY": { + "annotations": [ + { + "properties": { + "max": 7680, + "min": -7680 + }, + "typeName": "RangeAnnotationInt" + } + ], + "value": 0 + }, + "width": { + "annotations": [ + { + "properties": { + "max": 7680, + "min": 1 + }, + "typeName": "RangeAnnotationInt" + } + ], + "value": 1440 + } + }, + "visibility": true + }, + "typeName": "OrthographicCamera" + }, { "properties": { "objectID": "323d3539-5667-4b0e-b009-a324a9d35247", @@ -482,7 +737,7 @@ } ], "typeName": "TextureSampler2DBase::EngineTypeAnnotation", - "value": "0d2c96e3-0ed2-4f95-ac54-03b9fa81e963" + "value": null } } }, @@ -495,239 +750,120 @@ }, { "properties": { - "objectID": "383b0dab-8d10-4d96-b17e-f0144d268263", - "objectName": "mat_no_tex", - "options": { - "blendColor": { - "w": { - "annotations": [ - { - "properties": { - "max": 1, - "min": 0 - }, - "typeName": "RangeAnnotationDouble" - } - ], - "value": 0 - }, - "x": { + "enabled": true, + "frustum": { + "order": [ + "nearPlane", + "farPlane", + "fieldOfView", + "aspectRatio" + ], + "properties": { + "aspectRatio": { "annotations": [ { "properties": { - "max": 1, - "min": 0 + "name": "aspectRatio" + }, + "typeName": "DisplayNameAnnotation" + }, + { + "properties": { + "max": 4, + "min": 0.5 }, "typeName": "RangeAnnotationDouble" + }, + { + "properties": { + "featureLevel": 1 + }, + "typeName": "LinkEndAnnotation" } ], - "value": 0 + "typeName": "Double::DisplayNameAnnotation::RangeAnnotationDouble::LinkEndAnnotation", + "value": 2 }, - "y": { + "farPlane": { "annotations": [ { "properties": { - "max": 1, - "min": 0 + "name": "farPlane" + }, + "typeName": "DisplayNameAnnotation" + }, + { + "properties": { + "max": 10000, + "min": 100 }, "typeName": "RangeAnnotationDouble" + }, + { + "properties": { + "featureLevel": 1 + }, + "typeName": "LinkEndAnnotation" } ], - "value": 0 + "typeName": "Double::DisplayNameAnnotation::RangeAnnotationDouble::LinkEndAnnotation", + "value": 1000 }, - "z": { + "fieldOfView": { "annotations": [ { "properties": { - "max": 1, - "min": 0 + "name": "fieldOfView" + }, + "typeName": "DisplayNameAnnotation" + }, + { + "properties": { + "max": 120, + "min": 10 }, "typeName": "RangeAnnotationDouble" + }, + { + "properties": { + "featureLevel": 1 + }, + "typeName": "LinkEndAnnotation" } ], - "value": 0 - } - }, - "blendFactorDestAlpha": 1, - "blendFactorDestColor": 3, - "blendFactorSrcAlpha": 1, - "blendFactorSrcColor": 2, - "blendOperationAlpha": 0, - "blendOperationColor": 0, - "cullmode": 2, - "depthFunction": 4, - "depthwrite": true - }, - "uniforms": { - "order": [ - "u_Tex" - ], - "properties": { - "u_Tex": { + "typeName": "Double::DisplayNameAnnotation::RangeAnnotationDouble::LinkEndAnnotation", + "value": 35 + }, + "nearPlane": { "annotations": [ { "properties": { - "engineType": 15 + "name": "nearPlane" }, - "typeName": "EngineTypeAnnotation" - } - ], - "typeName": "TextureSampler2DBase::EngineTypeAnnotation", - "value": null - } - } - }, - "uriDefines": "", - "uriFragment": "../../../../../raco-oss-ref/datamodel/libCore/tests/testData/simple_texture.frag", - "uriGeometry": "", - "uriVertex": "../../../../../raco-oss-ref/datamodel/libCore/tests/testData/simple_texture.vert" - }, - "typeName": "Material" - }, - { - "properties": { - "enabled": true, - "instanceCount": { - "annotations": [ - { - "properties": { - "max": 20, - "min": 1 - }, - "typeName": "RangeAnnotationInt" - } - ], - "value": 1 - }, - "materials": { - "order": [ - "material" - ], - "properties": { - "material": { - "order": [ - "material", - "private", - "options", - "uniforms" - ], - "properties": { - "material": { - "typeName": "Material", - "value": "383b0dab-8d10-4d96-b17e-f0144d268263" + "typeName": "DisplayNameAnnotation" }, - "options": { - "annotations": [ - { - "properties": { - "name": "Options" - }, - "typeName": "DisplayNameAnnotation" - } - ], + { "properties": { - "blendColor": { - "w": { - "annotations": [ - { - "properties": { - "max": 1, - "min": 0 - }, - "typeName": "RangeAnnotationDouble" - } - ], - "value": 0 - }, - "x": { - "annotations": [ - { - "properties": { - "max": 1, - "min": 0 - }, - "typeName": "RangeAnnotationDouble" - } - ], - "value": 0 - }, - "y": { - "annotations": [ - { - "properties": { - "max": 1, - "min": 0 - }, - "typeName": "RangeAnnotationDouble" - } - ], - "value": 0 - }, - "z": { - "annotations": [ - { - "properties": { - "max": 1, - "min": 0 - }, - "typeName": "RangeAnnotationDouble" - } - ], - "value": 0 - } - }, - "blendFactorDestAlpha": 1, - "blendFactorDestColor": 3, - "blendFactorSrcAlpha": 1, - "blendFactorSrcColor": 2, - "blendOperationAlpha": 0, - "blendOperationColor": 0, - "cullmode": 2, - "depthFunction": 4, - "depthwrite": true + "max": 1, + "min": 0.1 }, - "typeName": "BlendOptions::DisplayNameAnnotation" - }, - "private": { - "annotations": [ - { - "properties": { - "name": "Private Material" - }, - "typeName": "DisplayNameAnnotation" - } - ], - "typeName": "Bool::DisplayNameAnnotation", - "value": true + "typeName": "RangeAnnotationDouble" }, - "uniforms": { - "order": [ - "u_Tex" - ], + { "properties": { - "u_Tex": { - "annotations": [ - { - "properties": { - "engineType": 15 - }, - "typeName": "EngineTypeAnnotation" - } - ], - "typeName": "TextureSampler2DBase::EngineTypeAnnotation", - "value": "0d2c96e3-0ed2-4f95-ac54-03b9fa81e963" - } + "featureLevel": 1 }, - "typeName": "Table" + "typeName": "LinkEndAnnotation" } - }, - "typeName": "Table" + ], + "typeName": "Double::DisplayNameAnnotation::RangeAnnotationDouble::LinkEndAnnotation", + "value": 0.1 } } }, - "mesh": "a11461f9-239e-4fb5-8a4e-aa55cd177039", - "objectID": "3ccf4fe2-660d-40e8-84f9-eb4a932c87ca", - "objectName": "meshnode_with_tex", + "frustumType": 0, + "objectID": "34d3f7f0-1d2d-4d1d-adc1-4eee283a7b80", + "objectName": "PerspectiveCamera", "rotation": { "x": { "annotations": [ @@ -787,236 +923,30 @@ "min": 0.1 }, "typeName": "RangeAnnotationDouble" - } - ], - "value": 1 - }, - "z": { - "annotations": [ - { - "properties": { - "max": 100, - "min": 0.1 - }, - "typeName": "RangeAnnotationDouble" - } - ], - "value": 1 - } - }, - "translation": { - "x": { - "annotations": [ - { - "properties": { - "max": 100, - "min": -100 - }, - "typeName": "RangeAnnotationDouble" - } - ], - "value": 0 - }, - "y": { - "annotations": [ - { - "properties": { - "max": 100, - "min": -100 - }, - "typeName": "RangeAnnotationDouble" - } - ], - "value": 0 - }, - "z": { - "annotations": [ - { - "properties": { - "max": 100, - "min": -100 - }, - "typeName": "RangeAnnotationDouble" - } - ], - "value": 0 - } - }, - "visibility": true - }, - "typeName": "MeshNode" - }, - { - "properties": { - "anisotropy": { - "annotations": [ - { - "properties": { - "max": 32000, - "min": 1 - }, - "typeName": "RangeAnnotationInt" - } - ], - "value": 1 - }, - "generateMipmaps": false, - "level2uriBack": "", - "level2uriBottom": "", - "level2uriFront": "", - "level2uriLeft": "", - "level2uriRight": "", - "level2uriTop": "", - "level3uriBack": "", - "level3uriBottom": "", - "level3uriFront": "", - "level3uriLeft": "", - "level3uriRight": "", - "level3uriTop": "", - "level4uriBack": "", - "level4uriBottom": "", - "level4uriFront": "", - "level4uriLeft": "", - "level4uriRight": "", - "level4uriTop": "", - "magSamplingMethod": 0, - "minSamplingMethod": 0, - "mipmapLevel": { - "annotations": [ - { - "properties": { - "max": 4, - "min": 1 - }, - "typeName": "RangeAnnotationInt" - } - ], - "value": 1 - }, - "objectID": "410631d1-cb7f-40c9-88c7-3bb1515d5caa", - "objectName": "CubeMap", - "textureFormat": 5, - "uriBack": "", - "uriBottom": "", - "uriFront": "", - "uriLeft": "", - "uriRight": "", - "uriTop": "", - "wrapUMode": 0, - "wrapVMode": 0 - }, - "typeName": "CubeMap" - }, - { - "properties": { - "materialFilterMode": 0, - "objectID": "4ba33009-0575-4128-bbbe-48e3ebc6e6ad", - "objectName": "RenderLayerManual", - "sortOrder": 0 - }, - "typeName": "RenderLayer" - }, - { - "properties": { - "objectID": "588bfb37-2015-4819-a868-6ed65f986484", - "objectName": "LuaScript", - "stdModules": { - "base": true, - "debug": true, - "math": true, - "string": true, - "table": true - }, - "uri": "" - }, - "typeName": "LuaScript" - }, - { - "properties": { - "animationChannels": { - "order": [ - "Channel 0", - "Channel 1", - "Channel 2", - "Channel 3", - "Channel 4", - "Channel 5", - "Channel 6", - "Channel 7" - ], - "properties": { - "Channel 0": { - "typeName": "AnimationChannel", - "value": null - }, - "Channel 1": { - "typeName": "AnimationChannel", - "value": null - }, - "Channel 2": { - "typeName": "AnimationChannel", - "value": null - }, - "Channel 3": { - "typeName": "AnimationChannel", - "value": null - }, - "Channel 4": { - "typeName": "AnimationChannel", - "value": null - }, - "Channel 5": { - "typeName": "AnimationChannel", - "value": null - }, - "Channel 6": { - "typeName": "AnimationChannel", - "value": null - }, - "Channel 7": { - "typeName": "AnimationChannel", - "value": null - } - } - }, - "objectID": "6d1b00fe-912f-4a92-83ce-7d405ac6b989", - "objectName": "Animation", - "progress": { - "annotations": [ - { - "properties": { - "max": 1, - "min": 0 - }, - "typeName": "RangeAnnotationDouble" - } - ], - "value": 0 - } - }, - "typeName": "Animation" - }, - { - "properties": { - "backgroundColor": { - "w": { + } + ], + "value": 1 + }, + "z": { "annotations": [ { "properties": { - "max": 1, - "min": 0 + "max": 100, + "min": 0.1 }, "typeName": "RangeAnnotationDouble" } ], "value": 1 - }, + } + }, + "translation": { "x": { "annotations": [ { "properties": { - "max": 1, - "min": 0 + "max": 100, + "min": -100 }, "typeName": "RangeAnnotationDouble" } @@ -1027,8 +957,8 @@ "annotations": [ { "properties": { - "max": 1, - "min": 0 + "max": 100, + "min": -100 }, "typeName": "RangeAnnotationDouble" } @@ -1039,8 +969,8 @@ "annotations": [ { "properties": { - "max": 1, - "min": 0 + "max": 100, + "min": -100 }, "typeName": "RangeAnnotationDouble" } @@ -1048,137 +978,295 @@ "value": 0 } }, - "defaultResourceFolders": { - "imageSubdirectory": "images", - "interfaceSubdirectory": "interfaces", - "meshSubdirectory": "meshes", - "scriptSubdirectory": "scripts", - "shaderSubdirectory": "shaders" - }, - "featureLevel": 2, - "objectID": "71454add-eb56-4288-9057-825539914bed", - "objectName": "test-offscreen-tex-uniform-migration-material", - "saveAsZip": false, - "sceneId": { - "annotations": [ - { - "properties": { - "max": 1024, - "min": 1 - }, - "typeName": "RangeAnnotationInt" - } - ], - "value": 123 - }, "viewport": { - "i1": { + "height": { "annotations": [ { "properties": { - "max": 4096, - "min": 0 + "max": 7680, + "min": 1 }, "typeName": "RangeAnnotationInt" } ], - "value": 1440 + "value": 720 }, - "i2": { + "offsetX": { "annotations": [ { "properties": { - "max": 4096, - "min": 0 + "max": 7680, + "min": -7680 }, "typeName": "RangeAnnotationInt" } ], - "value": 720 - } - } - }, - "typeName": "ProjectSettings" - }, - { - "properties": { - "enabled": true, - "frustum": { - "bottomPlane": { - "annotations": [ - { - "properties": { - "max": 0, - "min": -1000 - }, - "typeName": "RangeAnnotationDouble" - } - ], - "value": -10 + "value": 0 }, - "farPlane": { + "offsetY": { "annotations": [ { "properties": { - "max": 10000, - "min": 100 + "max": 7680, + "min": -7680 }, - "typeName": "RangeAnnotationDouble" + "typeName": "RangeAnnotationInt" } ], - "value": 1000 + "value": 0 }, - "leftPlane": { + "width": { "annotations": [ { "properties": { - "max": 0, - "min": -1000 + "max": 7680, + "min": 1 }, - "typeName": "RangeAnnotationDouble" + "typeName": "RangeAnnotationInt" } ], - "value": -10 + "value": 1440 + } + }, + "visibility": true + }, + "typeName": "PerspectiveCamera" + }, + { + "properties": { + "objectID": "383b0dab-8d10-4d96-b17e-f0144d268263", + "objectName": "mat_no_tex", + "options": { + "blendColor": { + "w": { + "annotations": [ + { + "properties": { + "max": 1, + "min": 0 + }, + "typeName": "RangeAnnotationDouble" + } + ], + "value": 0 + }, + "x": { + "annotations": [ + { + "properties": { + "max": 1, + "min": 0 + }, + "typeName": "RangeAnnotationDouble" + } + ], + "value": 0 + }, + "y": { + "annotations": [ + { + "properties": { + "max": 1, + "min": 0 + }, + "typeName": "RangeAnnotationDouble" + } + ], + "value": 0 + }, + "z": { + "annotations": [ + { + "properties": { + "max": 1, + "min": 0 + }, + "typeName": "RangeAnnotationDouble" + } + ], + "value": 0 + } }, - "nearPlane": { - "annotations": [ - { - "properties": { - "max": 1, - "min": 0.1 + "blendFactorDestAlpha": 1, + "blendFactorDestColor": 3, + "blendFactorSrcAlpha": 1, + "blendFactorSrcColor": 2, + "blendOperationAlpha": 0, + "blendOperationColor": 0, + "cullmode": 2, + "depthFunction": 4, + "depthwrite": true + }, + "uniforms": { + "order": [ + "u_Tex" + ], + "properties": { + "u_Tex": { + "annotations": [ + { + "properties": { + "engineType": 15 + }, + "typeName": "EngineTypeAnnotation" + } + ], + "typeName": "TextureSampler2DBase::EngineTypeAnnotation", + "value": null + } + } + }, + "uriDefines": "", + "uriFragment": "../../../../../raco-oss-ref/datamodel/libCore/tests/testData/simple_texture.frag", + "uriGeometry": "", + "uriVertex": "../../../../../raco-oss-ref/datamodel/libCore/tests/testData/simple_texture.vert" + }, + "typeName": "Material" + }, + { + "properties": { + "enabled": true, + "instanceCount": { + "annotations": [ + { + "properties": { + "max": 20, + "min": 1 + }, + "typeName": "RangeAnnotationInt" + } + ], + "value": 1 + }, + "materials": { + "order": [ + "material" + ], + "properties": { + "material": { + "order": [ + "material", + "private", + "options", + "uniforms" + ], + "properties": { + "material": { + "typeName": "Material", + "value": "383b0dab-8d10-4d96-b17e-f0144d268263" }, - "typeName": "RangeAnnotationDouble" - } - ], - "value": 0.1 - }, - "rightPlane": { - "annotations": [ - { - "properties": { - "max": 1000, - "min": 0 + "options": { + "annotations": [ + { + "properties": { + "name": "Options" + }, + "typeName": "DisplayNameAnnotation" + } + ], + "properties": { + "blendColor": { + "w": { + "annotations": [ + { + "properties": { + "max": 1, + "min": 0 + }, + "typeName": "RangeAnnotationDouble" + } + ], + "value": 0 + }, + "x": { + "annotations": [ + { + "properties": { + "max": 1, + "min": 0 + }, + "typeName": "RangeAnnotationDouble" + } + ], + "value": 0 + }, + "y": { + "annotations": [ + { + "properties": { + "max": 1, + "min": 0 + }, + "typeName": "RangeAnnotationDouble" + } + ], + "value": 0 + }, + "z": { + "annotations": [ + { + "properties": { + "max": 1, + "min": 0 + }, + "typeName": "RangeAnnotationDouble" + } + ], + "value": 0 + } + }, + "blendFactorDestAlpha": 1, + "blendFactorDestColor": 3, + "blendFactorSrcAlpha": 1, + "blendFactorSrcColor": 2, + "blendOperationAlpha": 0, + "blendOperationColor": 0, + "cullmode": 2, + "depthFunction": 4, + "depthwrite": true + }, + "typeName": "BlendOptions::DisplayNameAnnotation" }, - "typeName": "RangeAnnotationDouble" - } - ], - "value": 10 - }, - "topPlane": { - "annotations": [ - { - "properties": { - "max": 1000, - "min": 0 + "private": { + "annotations": [ + { + "properties": { + "name": "Private Material" + }, + "typeName": "DisplayNameAnnotation" + } + ], + "typeName": "Bool::DisplayNameAnnotation", + "value": true }, - "typeName": "RangeAnnotationDouble" - } - ], - "value": 10 + "uniforms": { + "order": [ + "u_Tex" + ], + "properties": { + "u_Tex": { + "annotations": [ + { + "properties": { + "engineType": 15 + }, + "typeName": "EngineTypeAnnotation" + } + ], + "typeName": "TextureSampler2DBase::EngineTypeAnnotation", + "value": null + } + }, + "typeName": "Table" + } + }, + "typeName": "Table" + } } }, - "objectID": "8328f7f9-fa7a-45f0-b58f-ef1b9976bb47", - "objectName": "OrthographicCamera", + "mesh": "a11461f9-239e-4fb5-8a4e-aa55cd177039", + "objectID": "3ccf4fe2-660d-40e8-84f9-eb4a932c87ca", + "objectName": "meshnode_with_tex", "rotation": { "x": { "annotations": [ @@ -1293,213 +1381,366 @@ "value": 0 } }, - "viewport": { - "height": { - "annotations": [ - { - "properties": { - "max": 7680, - "min": 0 - }, - "typeName": "RangeAnnotationInt" - } - ], - "value": 720 - }, - "offsetX": { - "annotations": [ - { - "properties": { - "max": 7680, - "min": -7680 - }, - "typeName": "RangeAnnotationInt" - } - ], - "value": 0 - }, - "offsetY": { - "annotations": [ - { - "properties": { - "max": 7680, - "min": -7680 - }, - "typeName": "RangeAnnotationInt" - } - ], - "value": 0 - }, - "width": { - "annotations": [ - { - "properties": { - "max": 7680, - "min": 0 - }, - "typeName": "RangeAnnotationInt" - } - ], - "value": 1440 + "visibility": true + }, + "typeName": "MeshNode" + }, + { + "properties": { + "anisotropy": { + "annotations": [ + { + "properties": { + "max": 32000, + "min": 1 + }, + "typeName": "RangeAnnotationInt" + } + ], + "value": 1 + }, + "generateMipmaps": false, + "level2uriBack": "", + "level2uriBottom": "", + "level2uriFront": "", + "level2uriLeft": "", + "level2uriRight": "", + "level2uriTop": "", + "level3uriBack": "", + "level3uriBottom": "", + "level3uriFront": "", + "level3uriLeft": "", + "level3uriRight": "", + "level3uriTop": "", + "level4uriBack": "", + "level4uriBottom": "", + "level4uriFront": "", + "level4uriLeft": "", + "level4uriRight": "", + "level4uriTop": "", + "magSamplingMethod": 0, + "minSamplingMethod": 0, + "mipmapLevel": { + "annotations": [ + { + "properties": { + "max": 4, + "min": 1 + }, + "typeName": "RangeAnnotationInt" + } + ], + "value": 1 + }, + "objectID": "410631d1-cb7f-40c9-88c7-3bb1515d5caa", + "objectName": "CubeMap", + "textureFormat": 5, + "uriBack": "", + "uriBottom": "", + "uriFront": "", + "uriLeft": "", + "uriRight": "", + "uriTop": "", + "wrapUMode": 0, + "wrapVMode": 0 + }, + "typeName": "CubeMap" + }, + { + "properties": { + "materialFilterMode": 0, + "objectID": "4ba33009-0575-4128-bbbe-48e3ebc6e6ad", + "objectName": "RenderLayerManual", + "sortOrder": 0 + }, + "typeName": "RenderLayer" + }, + { + "properties": { + "objectID": "588bfb37-2015-4819-a868-6ed65f986484", + "objectName": "LuaScript", + "stdModules": { + "base": true, + "debug": true, + "math": true, + "string": true, + "table": true + }, + "uri": "" + }, + "typeName": "LuaScript" + }, + { + "properties": { + "animationChannels": { + "order": [ + "Channel 0", + "Channel 1", + "Channel 2", + "Channel 3", + "Channel 4", + "Channel 5", + "Channel 6", + "Channel 7" + ], + "properties": { + "Channel 0": { + "typeName": "AnimationChannel", + "value": null + }, + "Channel 1": { + "typeName": "AnimationChannel", + "value": null + }, + "Channel 2": { + "typeName": "AnimationChannel", + "value": null + }, + "Channel 3": { + "typeName": "AnimationChannel", + "value": null + }, + "Channel 4": { + "typeName": "AnimationChannel", + "value": null + }, + "Channel 5": { + "typeName": "AnimationChannel", + "value": null + }, + "Channel 6": { + "typeName": "AnimationChannel", + "value": null + }, + "Channel 7": { + "typeName": "AnimationChannel", + "value": null + } } }, - "visibility": true + "objectID": "6d1b00fe-912f-4a92-83ce-7d405ac6b989", + "objectName": "Animation", + "progress": { + "annotations": [ + { + "properties": { + "max": 1, + "min": 0 + }, + "typeName": "RangeAnnotationDouble" + } + ], + "value": 0 + } }, - "typeName": "OrthographicCamera" + "typeName": "Animation" }, { "properties": { - "children": { - "properties": [ - { - "typeName": "Ref", - "value": "122d0f90-2dea-4ddd-95f5-d537f5381a3e" - }, - { - "typeName": "Ref", - "value": "3ccf4fe2-660d-40e8-84f9-eb4a932c87ca" - } - ] - }, - "enabled": true, - "objectID": "8593d288-67a3-4ae1-9c8c-942786ddd1f2", - "objectName": "Node", - "rotation": { - "x": { - "annotations": [ - { - "properties": { - "max": 360, - "min": -360 - }, - "typeName": "RangeAnnotationDouble" - } - ], - "value": 0 - }, - "y": { + "backgroundColor": { + "w": { "annotations": [ { "properties": { - "max": 360, - "min": -360 + "max": 1, + "min": 0 }, "typeName": "RangeAnnotationDouble" } ], - "value": 0 + "value": 1 }, - "z": { - "annotations": [ - { - "properties": { - "max": 360, - "min": -360 - }, - "typeName": "RangeAnnotationDouble" - } - ], - "value": 0 - } - }, - "scaling": { "x": { "annotations": [ { "properties": { - "max": 100, - "min": 0.1 + "max": 1, + "min": 0 }, "typeName": "RangeAnnotationDouble" } ], - "value": 1 + "value": 0 }, "y": { "annotations": [ { "properties": { - "max": 100, - "min": 0.1 + "max": 1, + "min": 0 }, "typeName": "RangeAnnotationDouble" } ], - "value": 1 + "value": 0 }, "z": { "annotations": [ { "properties": { - "max": 100, - "min": 0.1 + "max": 1, + "min": 0 }, "typeName": "RangeAnnotationDouble" } ], - "value": 1 + "value": 0 } }, - "translation": { - "x": { - "annotations": [ - { - "properties": { - "max": 100, - "min": -100 - }, - "typeName": "RangeAnnotationDouble" - } - ], - "value": 0 - }, - "y": { + "defaultResourceFolders": { + "imageSubdirectory": "images", + "interfaceSubdirectory": "interfaces", + "meshSubdirectory": "meshes", + "scriptSubdirectory": "scripts", + "shaderSubdirectory": "shaders" + }, + "featureLevel": 3, + "objectID": "71454add-eb56-4288-9057-825539914bed", + "objectName": "test-offscreen-tex-uniform-migration-material", + "saveAsZip": false, + "sceneId": { + "annotations": [ + { + "properties": { + "max": 1024, + "min": 1 + }, + "typeName": "RangeAnnotationInt" + } + ], + "value": 123 + }, + "viewport": { + "i1": { "annotations": [ { "properties": { - "max": 100, - "min": -100 + "max": 4096, + "min": 0 }, - "typeName": "RangeAnnotationDouble" + "typeName": "RangeAnnotationInt" } ], - "value": 0 + "value": 1440 }, - "z": { + "i2": { "annotations": [ { "properties": { - "max": 100, - "min": -100 + "max": 4096, + "min": 0 }, - "typeName": "RangeAnnotationDouble" + "typeName": "RangeAnnotationInt" } ], - "value": 0 + "value": 720 } - }, - "visibility": true + } }, - "typeName": "Node" + "typeName": "ProjectSettings" }, { "properties": { - "inputs": { - "ticker_us": "0" + "destinationX": { + "annotations": [ + { + "properties": { + "max": 7680, + "min": 0 + }, + "typeName": "RangeAnnotationInt" + } + ], + "value": 0 }, - "objectID": "9c2eeeea-cef0-42a5-9815-d32d38ccfd60", - "objectName": "Timer", - "outputs": { - "ticker_us": "1281425339981" + "destinationY": { + "annotations": [ + { + "properties": { + "max": 7680, + "min": 0 + }, + "typeName": "RangeAnnotationInt" + } + ], + "value": 0 + }, + "enabled": true, + "height": { + "annotations": [ + { + "properties": { + "max": 7680, + "min": 0 + }, + "typeName": "RangeAnnotationInt" + } + ], + "value": 256 + }, + "objectID": "76759483-55f2-45e4-bcdc-bb59d7e9d481", + "objectName": "BlitPass", + "renderOrder": 0, + "sourceRenderBuffer": null, + "sourceRenderBufferMS": null, + "sourceX": { + "annotations": [ + { + "properties": { + "max": 7680, + "min": 0 + }, + "typeName": "RangeAnnotationInt" + } + ], + "value": 0 + }, + "sourceY": { + "annotations": [ + { + "properties": { + "max": 7680, + "min": 0 + }, + "typeName": "RangeAnnotationInt" + } + ], + "value": 0 + }, + "targetRenderBuffer": null, + "targetRenderBufferMS": null, + "width": { + "annotations": [ + { + "properties": { + "max": 7680, + "min": 0 + }, + "typeName": "RangeAnnotationInt" + } + ], + "value": 256 } }, - "typeName": "Timer" + "typeName": "BlitPass" }, { "properties": { + "children": { + "properties": [ + { + "typeName": "Ref", + "value": "122d0f90-2dea-4ddd-95f5-d537f5381a3e" + }, + { + "typeName": "Ref", + "value": "3ccf4fe2-660d-40e8-84f9-eb4a932c87ca" + } + ] + }, "enabled": true, - "objectID": "9ec70db9-1ec5-4c64-833d-44eb4ad1d495", - "objectName": "PrefabInstance", + "objectID": "8593d288-67a3-4ae1-9c8c-942786ddd1f2", + "objectName": "Node", "rotation": { "x": { "annotations": [ @@ -1576,7 +1817,6 @@ "value": 1 } }, - "template": null, "translation": { "x": { "annotations": [ @@ -1617,124 +1857,26 @@ }, "visibility": true }, - "typeName": "PrefabInstance" + "typeName": "Node" }, { "properties": { - "enabled": true, - "frustum": { - "order": [ - "nearPlane", - "farPlane", - "fieldOfView", - "aspectRatio" - ], - "properties": { - "aspectRatio": { - "annotations": [ - { - "properties": { - "name": "aspectRatio" - }, - "typeName": "DisplayNameAnnotation" - }, - { - "properties": { - "max": 4, - "min": 0.5 - }, - "typeName": "RangeAnnotationDouble" - }, - { - "properties": { - "featureLevel": 1 - }, - "typeName": "LinkEndAnnotation" - } - ], - "typeName": "Double::DisplayNameAnnotation::RangeAnnotationDouble::LinkEndAnnotation", - "value": 2 - }, - "farPlane": { - "annotations": [ - { - "properties": { - "name": "farPlane" - }, - "typeName": "DisplayNameAnnotation" - }, - { - "properties": { - "max": 10000, - "min": 100 - }, - "typeName": "RangeAnnotationDouble" - }, - { - "properties": { - "featureLevel": 1 - }, - "typeName": "LinkEndAnnotation" - } - ], - "typeName": "Double::DisplayNameAnnotation::RangeAnnotationDouble::LinkEndAnnotation", - "value": 1000 - }, - "fieldOfView": { - "annotations": [ - { - "properties": { - "name": "fieldOfView" - }, - "typeName": "DisplayNameAnnotation" - }, - { - "properties": { - "max": 120, - "min": 10 - }, - "typeName": "RangeAnnotationDouble" - }, - { - "properties": { - "featureLevel": 1 - }, - "typeName": "LinkEndAnnotation" - } - ], - "typeName": "Double::DisplayNameAnnotation::RangeAnnotationDouble::LinkEndAnnotation", - "value": 35 - }, - "nearPlane": { - "annotations": [ - { - "properties": { - "name": "nearPlane" - }, - "typeName": "DisplayNameAnnotation" - }, - { - "properties": { - "max": 1, - "min": 0.1 - }, - "typeName": "RangeAnnotationDouble" - }, - { - "properties": { - "featureLevel": 1 - }, - "typeName": "LinkEndAnnotation" - } - ], - "typeName": "Double::DisplayNameAnnotation::RangeAnnotationDouble::LinkEndAnnotation", - "value": 0.1 - } - } + "inputs": { + "ticker_us": "0" }, - "frustumType": 0, - "objectID": "a093b1e1-6dc2-4a15-9628-73476c306782", - "objectName": "PerspectiveCamera", + "objectID": "9c2eeeea-cef0-42a5-9815-d32d38ccfd60", + "objectName": "Timer", + "outputs": { + "ticker_us": "515768050963" + } + }, + "typeName": "Timer" + }, + { + "properties": { + "enabled": true, + "objectID": "9ec70db9-1ec5-4c64-833d-44eb4ad1d495", + "objectName": "PrefabInstance", "rotation": { "x": { "annotations": [ @@ -1811,6 +1953,7 @@ "value": 1 } }, + "template": null, "translation": { "x": { "annotations": [ @@ -1846,62 +1989,12 @@ "typeName": "RangeAnnotationDouble" } ], - "value": 10 - } - }, - "viewport": { - "height": { - "annotations": [ - { - "properties": { - "max": 7680, - "min": 0 - }, - "typeName": "RangeAnnotationInt" - } - ], - "value": 720 - }, - "offsetX": { - "annotations": [ - { - "properties": { - "max": 7680, - "min": -7680 - }, - "typeName": "RangeAnnotationInt" - } - ], "value": 0 - }, - "offsetY": { - "annotations": [ - { - "properties": { - "max": 7680, - "min": -7680 - }, - "typeName": "RangeAnnotationInt" - } - ], - "value": 0 - }, - "width": { - "annotations": [ - { - "properties": { - "max": 7680, - "min": 0 - }, - "typeName": "RangeAnnotationInt" - } - ], - "value": 1440 } }, "visibility": true }, - "typeName": "PerspectiveCamera" + "typeName": "PrefabInstance" }, { "properties": { @@ -1986,6 +2079,50 @@ }, "typeName": "AnchorPoint" }, + { + "properties": { + "format": 4, + "height": { + "annotations": [ + { + "properties": { + "max": 7680, + "min": 1 + }, + "typeName": "RangeAnnotationInt" + } + ], + "value": 256 + }, + "objectID": "dc208dbd-5bc6-4bb0-b4f5-6710c82dd65c", + "objectName": "RenderBufferMS", + "sampleCount": { + "annotations": [ + { + "properties": { + "max": 8, + "min": 1 + }, + "typeName": "RangeAnnotationInt" + } + ], + "value": 1 + }, + "width": { + "annotations": [ + { + "properties": { + "max": 7680, + "min": 1 + }, + "typeName": "RangeAnnotationInt" + } + ], + "value": 256 + } + }, + "typeName": "RenderBufferMS" + }, { "properties": { "camera": null, @@ -2064,18 +2201,18 @@ ], "logicEngineVersion": [ 1, - 1, - 0 + 2, + 2 ], "racoVersion": [ 1, - 2, + 4, 0 ], "ramsesVersion": [ 27, 0, - 121 + 125 ], "structPropMap": { "AnchorPointOutputs": { @@ -2184,6 +2321,23 @@ "samplerIndex": "Int::DisplayNameAnnotation", "uri": "String::URIAnnotation::DisplayNameAnnotation" }, + "BlitPass": { + "children": "Table::ArraySemanticAnnotation::HiddenProperty", + "destinationX": "Int::RangeAnnotationInt::DisplayNameAnnotation", + "destinationY": "Int::RangeAnnotationInt::DisplayNameAnnotation", + "enabled": "Bool::DisplayNameAnnotation", + "height": "Int::RangeAnnotationInt::DisplayNameAnnotation", + "objectID": "String::HiddenProperty", + "objectName": "String::DisplayNameAnnotation", + "renderOrder": "Int::DisplayNameAnnotation", + "sourceRenderBuffer": "RenderBuffer::DisplayNameAnnotation::EmptyReferenceAllowable", + "sourceRenderBufferMS": "RenderBufferMS::DisplayNameAnnotation::EmptyReferenceAllowable", + "sourceX": "Int::RangeAnnotationInt::DisplayNameAnnotation", + "sourceY": "Int::RangeAnnotationInt::DisplayNameAnnotation", + "targetRenderBuffer": "RenderBuffer::DisplayNameAnnotation::EmptyReferenceAllowable", + "targetRenderBufferMS": "RenderBufferMS::DisplayNameAnnotation::EmptyReferenceAllowable", + "width": "Int::RangeAnnotationInt::DisplayNameAnnotation" + }, "CubeMap": { "anisotropy": "Int::DisplayNameAnnotation::RangeAnnotationInt", "children": "Table::ArraySemanticAnnotation::HiddenProperty", @@ -2359,13 +2513,22 @@ "wrapUMode": "Int::DisplayNameAnnotation::EnumerationAnnotation", "wrapVMode": "Int::DisplayNameAnnotation::EnumerationAnnotation" }, + "RenderBufferMS": { + "children": "Table::ArraySemanticAnnotation::HiddenProperty", + "format": "Int::DisplayNameAnnotation::EnumerationAnnotation", + "height": "Int::RangeAnnotationInt::DisplayNameAnnotation", + "objectID": "String::HiddenProperty", + "objectName": "String::DisplayNameAnnotation", + "sampleCount": "Int::RangeAnnotationInt::DisplayNameAnnotation", + "width": "Int::RangeAnnotationInt::DisplayNameAnnotation" + }, "RenderLayer": { "children": "Table::ArraySemanticAnnotation::HiddenProperty", "materialFilterMode": "Int::DisplayNameAnnotation::EnumerationAnnotation", "materialFilterTags": "Table::ArraySemanticAnnotation::HiddenProperty::TagContainerAnnotation::DisplayNameAnnotation", "objectID": "String::HiddenProperty", "objectName": "String::DisplayNameAnnotation", - "renderableTags": "Table::RenderableTagContainerAnnotation::HiddenProperty::DisplayNameAnnotation", + "renderableTags": "Table::RenderableTagContainerAnnotation::DisplayNameAnnotation", "sortOrder": "Int::DisplayNameAnnotation::EnumerationAnnotation", "tags": "Table::ArraySemanticAnnotation::HiddenProperty::TagContainerAnnotation::DisplayNameAnnotation" }, @@ -2392,7 +2555,7 @@ "target": "RenderTarget::DisplayNameAnnotation::EmptyReferenceAllowable" }, "RenderTarget": { - "buffer0": "RenderBuffer::DisplayNameAnnotation", + "buffer0": "RenderBuffer::DisplayNameAnnotation::EmptyReferenceAllowable", "buffer1": "RenderBuffer::DisplayNameAnnotation::EmptyReferenceAllowable", "buffer2": "RenderBuffer::DisplayNameAnnotation::EmptyReferenceAllowable", "buffer3": "RenderBuffer::DisplayNameAnnotation::EmptyReferenceAllowable", @@ -2400,6 +2563,14 @@ "buffer5": "RenderBuffer::DisplayNameAnnotation::EmptyReferenceAllowable", "buffer6": "RenderBuffer::DisplayNameAnnotation::EmptyReferenceAllowable", "buffer7": "RenderBuffer::DisplayNameAnnotation::EmptyReferenceAllowable", + "bufferMS0": "RenderBufferMS::DisplayNameAnnotation::EmptyReferenceAllowable", + "bufferMS1": "RenderBufferMS::DisplayNameAnnotation::EmptyReferenceAllowable", + "bufferMS2": "RenderBufferMS::DisplayNameAnnotation::EmptyReferenceAllowable", + "bufferMS3": "RenderBufferMS::DisplayNameAnnotation::EmptyReferenceAllowable", + "bufferMS4": "RenderBufferMS::DisplayNameAnnotation::EmptyReferenceAllowable", + "bufferMS5": "RenderBufferMS::DisplayNameAnnotation::EmptyReferenceAllowable", + "bufferMS6": "RenderBufferMS::DisplayNameAnnotation::EmptyReferenceAllowable", + "bufferMS7": "RenderBufferMS::DisplayNameAnnotation::EmptyReferenceAllowable", "children": "Table::ArraySemanticAnnotation::HiddenProperty", "objectID": "String::HiddenProperty", "objectName": "String::DisplayNameAnnotation" diff --git a/datamodel/libDataStorage/include/data_storage/Value.h b/datamodel/libDataStorage/include/data_storage/Value.h index 1437f642..83599186 100644 --- a/datamodel/libDataStorage/include/data_storage/Value.h +++ b/datamodel/libDataStorage/include/data_storage/Value.h @@ -154,6 +154,8 @@ class ValueBase { public: static std::unique_ptr create(PrimitiveType type); + virtual ~ValueBase() = default; + virtual PrimitiveType type() const = 0; // Basic typename of the property not including annotation information. diff --git a/datamodel/libUserTypes/CMakeLists.txt b/datamodel/libUserTypes/CMakeLists.txt index 3aa622aa..9bd17885 100644 --- a/datamodel/libUserTypes/CMakeLists.txt +++ b/datamodel/libUserTypes/CMakeLists.txt @@ -15,6 +15,7 @@ add_library(libUserTypes include/user_types/BaseCamera.h include/user_types/BaseObject.h include/user_types/BaseTexture.h + include/user_types/BlitPass.h src/BlitPass.cpp include/user_types/CubeMap.h src/CubeMap.cpp include/user_types/DefaultValues.h include/user_types/EngineTypeAnnotation.h @@ -32,6 +33,7 @@ add_library(libUserTypes include/user_types/Prefab.h src/Prefab.cpp include/user_types/PrefabInstance.h src/PrefabInstance.cpp include/user_types/RenderBuffer.h + include/user_types/RenderBufferMS.h src/RenderBufferMS.cpp include/user_types/RenderLayer.h include/user_types/RenderPass.h src/RenderPass.cpp include/user_types/RenderTarget.h diff --git a/datamodel/libUserTypes/include/user_types/Animation.h b/datamodel/libUserTypes/include/user_types/Animation.h index 0ba372c1..5049d0c3 100644 --- a/datamodel/libUserTypes/include/user_types/Animation.h +++ b/datamodel/libUserTypes/include/user_types/Animation.h @@ -44,8 +44,6 @@ class Animation : public BaseObject { properties_.emplace_back("outputs", &outputs_); } - void onBeforeDeleteObject(Errors& errors) const override; - void onAfterContextActivated(BaseContext& context) override; void onAfterReferencedObjectChanged(BaseContext& context, ValueHandle const& changedObject) override; void onAfterValueChanged(BaseContext& context, ValueHandle const& value) override; diff --git a/datamodel/libUserTypes/include/user_types/BaseCamera.h b/datamodel/libUserTypes/include/user_types/BaseCamera.h index c56367e3..7c484859 100644 --- a/datamodel/libUserTypes/include/user_types/BaseCamera.h +++ b/datamodel/libUserTypes/include/user_types/BaseCamera.h @@ -59,8 +59,8 @@ class CameraViewport : public StructBase { Property, DisplayNameAnnotation, LinkEndAnnotation> offsetX_{0, {-7680, 7680}, {"offsetX"}, {}}; Property, DisplayNameAnnotation, LinkEndAnnotation> offsetY_{0, {-7680, 7680}, {"offsetY"}, {}}; - Property, DisplayNameAnnotation, LinkEndAnnotation> width_{1440, {0, 7680}, {"width"}, {}}; - Property, DisplayNameAnnotation, LinkEndAnnotation> height_{720, {0, 7680}, {"height"}, {}}; + Property, DisplayNameAnnotation, LinkEndAnnotation> width_{1440, {1, 7680}, {"width"}, {}}; + Property, DisplayNameAnnotation, LinkEndAnnotation> height_{720, {1, 7680}, {"height"}, {}}; }; class BaseCamera : public Node { diff --git a/datamodel/libUserTypes/include/user_types/BlitPass.h b/datamodel/libUserTypes/include/user_types/BlitPass.h new file mode 100644 index 00000000..2aeaf5e1 --- /dev/null +++ b/datamodel/libUserTypes/include/user_types/BlitPass.h @@ -0,0 +1,83 @@ +/* + * SPDX-License-Identifier: MPL-2.0 + * + * This file is part of Ramses Composer + * (see https://github.com/bmwcarit/ramses-composer). + * + * This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. + * If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +#pragma once + +#include "user_types/BaseObject.h" +#include "user_types/RenderBuffer.h" +#include "user_types/RenderBufferMS.h" + +namespace raco::user_types { + +class BlitPass : public BaseObject { +public: + static inline const TypeDescriptor typeDescription = {"BlitPass", true}; + TypeDescriptor const& getTypeDescription() const override { + return typeDescription; + } + + BlitPass(BlitPass const& other) + : BaseObject(other), + sourceRenderBuffer_(other.sourceRenderBuffer_), + targetRenderBuffer_(other.targetRenderBuffer_), + sourceX_(other.sourceX_), + sourceY_(other.sourceY_), + destinationX_(other.destinationX_), + destinationY_(other.destinationY_), + width_(other.width_), + height_(other.height_), + enabled_(other.enabled_), + renderOrder_(other.renderOrder_) { + fillPropertyDescription(); + } + + BlitPass(const std::string& name, const std::string& id) : BaseObject(name, id) { + fillPropertyDescription(); + } + + void fillPropertyDescription() { + properties_.emplace_back("sourceRenderBuffer", &sourceRenderBuffer_); + properties_.emplace_back("targetRenderBuffer", &targetRenderBuffer_); + properties_.emplace_back("sourceRenderBufferMS", &sourceRenderBufferMS_); + properties_.emplace_back("targetRenderBufferMS", &targetRenderBufferMS_); + properties_.emplace_back("sourceX", &sourceX_); + properties_.emplace_back("sourceY", &sourceY_); + properties_.emplace_back("destinationX", &destinationX_); + properties_.emplace_back("destinationY", &destinationY_); + properties_.emplace_back("width", &width_); + properties_.emplace_back("height", &height_); + properties_.emplace_back("enabled", &enabled_); + properties_.emplace_back("renderOrder", &renderOrder_); + } + + void onAfterContextActivated(BaseContext& context) override; + void onAfterValueChanged(BaseContext& context, ValueHandle const& value) override; + void onAfterReferencedObjectChanged(BaseContext& context, ValueHandle const& changedObject) override; + + Property sourceRenderBuffer_{{}, {"Source Render Buffer"}, {}}; + Property targetRenderBuffer_{{}, {"Target Render Buffer"}, {}}; + Property sourceRenderBufferMS_{{}, {"Source Render Buffer (Multisampled)"}, {}}; + Property targetRenderBufferMS_{{}, {"Target Render Buffer (Multisampled)"}, {}}; + + Property, DisplayNameAnnotation> sourceX_{0, {0, 7680}, {"Source Buffer Offset X"}}; + Property, DisplayNameAnnotation> sourceY_{0, {0, 7680}, {"Source Buffer Offset Y"}}; + Property, DisplayNameAnnotation> destinationX_{0, {0, 7680}, {"Destination Buffer Offset X"}}; + Property, DisplayNameAnnotation> destinationY_{0, {0, 7680}, {"Destination Buffer Offset Y"}}; + Property, DisplayNameAnnotation> width_{256, {0, 7680}, {"Blitting Region Width"}}; + Property, DisplayNameAnnotation> height_{256, {0, 7680}, {"Blitting Region Height"}}; + + Property enabled_{true, {"Enabled"}}; + Property renderOrder_{0, {"Render Order"}}; + +private: + void validateBufferCompatibility(BaseContext& context); +}; + +using SBlitPass = std::shared_ptr; +} // namespace raco::user_types \ No newline at end of file diff --git a/datamodel/libUserTypes/include/user_types/CubeMap.h b/datamodel/libUserTypes/include/user_types/CubeMap.h index a99c5245..df90b025 100644 --- a/datamodel/libUserTypes/include/user_types/CubeMap.h +++ b/datamodel/libUserTypes/include/user_types/CubeMap.h @@ -87,6 +87,7 @@ class CubeMap : public BaseTexture { properties_.emplace_back("level4uriBack", &level4uriBack_); } + void onAfterContextActivated(BaseContext& context) override; void onAfterValueChanged(BaseContext& context, ValueHandle const& value) override; void updateFromExternalFile(BaseContext& context) override; diff --git a/datamodel/libUserTypes/include/user_types/RenderBufferMS.h b/datamodel/libUserTypes/include/user_types/RenderBufferMS.h new file mode 100644 index 00000000..5cd475f9 --- /dev/null +++ b/datamodel/libUserTypes/include/user_types/RenderBufferMS.h @@ -0,0 +1,62 @@ +/* + * SPDX-License-Identifier: MPL-2.0 + * + * This file is part of Ramses Composer + * (see https://github.com/bmwcarit/ramses-composer). + * + * This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. + * If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +#pragma once + +#include "core/Context.h" +#include "core/EngineInterface.h" +#include "core/Errors.h" +#include "user_types/DefaultValues.h" +#include "user_types/BaseTexture.h" + +namespace raco::user_types { + +class RenderBufferMS : public BaseObject { +public: + static inline auto SAMPLE_COUNT_MIN = 1; + static inline auto SAMPLE_COUNT_MAX = 8; + + static inline const TypeDescriptor typeDescription = {"RenderBufferMS", true}; + TypeDescriptor const& getTypeDescription() const override { + return typeDescription; + } + + RenderBufferMS(RenderBufferMS const& other) + : BaseObject(other), width_(other.width_), height_(other.height_), sampleCount_(other.sampleCount_) { + fillPropertyDescription(); + } + + RenderBufferMS(const std::string& name, const std::string& id) : BaseObject(name, id) { + fillPropertyDescription(); + } + + void fillPropertyDescription() { + properties_.emplace_back("width", &width_); + properties_.emplace_back("height", &height_); + properties_.emplace_back("format", &format_); + properties_.emplace_back("sampleCount", &sampleCount_); + } + + void onAfterContextActivated(BaseContext& context) override; + void onAfterValueChanged(BaseContext& context, ValueHandle const& value) override; + + Property, DisplayNameAnnotation> width_{256, {1, 7680}, {"Width"}}; + Property, DisplayNameAnnotation> height_{256, {1, 7680}, {"Height"}}; + Property format_{DEFAULT_VALUE_RENDER_BUFFER_FORMAT, DisplayNameAnnotation("Format"), EnumerationAnnotation{EngineEnumeration::RenderBufferFormat}}; + + Property, DisplayNameAnnotation> sampleCount_{SAMPLE_COUNT_MIN, {SAMPLE_COUNT_MIN, SAMPLE_COUNT_MAX}, {"Sample Count"}}; + +private: + void validateSampleCount(BaseContext& context); + +}; + +using SRenderBufferMS = std::shared_ptr; + +} // namespace raco::user_types diff --git a/datamodel/libUserTypes/include/user_types/RenderLayer.h b/datamodel/libUserTypes/include/user_types/RenderLayer.h index 7a42f248..79ce90c8 100644 --- a/datamodel/libUserTypes/include/user_types/RenderLayer.h +++ b/datamodel/libUserTypes/include/user_types/RenderLayer.h @@ -66,7 +66,7 @@ class RenderLayer : public BaseObject { // contains tag name -> order index map as normal Value properties // - property name is the tag name // - property value is the order index - Property renderableTags_{{}, {}, {}, {"Renderable Tags"}}; + Property renderableTags_{{}, {}, {"Renderable Tags"}}; Property materialFilterTags_{{}, {}, {}, {}, {"Material Filter Tags"}}; Property materialFilterMode_{static_cast(ERenderLayerMaterialFilterMode::Exclusive), {"Material Filter Mode"}, EngineEnumeration::RenderLayerMaterialFilterMode}; diff --git a/datamodel/libUserTypes/include/user_types/RenderTarget.h b/datamodel/libUserTypes/include/user_types/RenderTarget.h index 8ee6eb08..0798c00f 100644 --- a/datamodel/libUserTypes/include/user_types/RenderTarget.h +++ b/datamodel/libUserTypes/include/user_types/RenderTarget.h @@ -11,6 +11,7 @@ #include "user_types/BaseObject.h" #include "user_types/RenderBuffer.h" +#include "user_types/RenderBufferMS.h" namespace raco::user_types { @@ -30,7 +31,15 @@ class RenderTarget : public BaseObject { buffer4_(other.buffer4_), buffer5_(other.buffer5_), buffer6_(other.buffer6_), - buffer7_(other.buffer7_) { + buffer7_(other.buffer7_), + bufferMS0_(other.bufferMS0_), + bufferMS1_(other.bufferMS1_), + bufferMS2_(other.bufferMS2_), + bufferMS3_(other.bufferMS3_), + bufferMS4_(other.bufferMS4_), + bufferMS5_(other.bufferMS5_), + bufferMS6_(other.bufferMS6_), + bufferMS7_(other.bufferMS7_) { fillPropertyDescription(); } @@ -47,12 +56,20 @@ class RenderTarget : public BaseObject { properties_.emplace_back("buffer5", &buffer5_); properties_.emplace_back("buffer6", &buffer6_); properties_.emplace_back("buffer7", &buffer7_); + properties_.emplace_back("bufferMS0", &bufferMS0_); + properties_.emplace_back("bufferMS1", &bufferMS1_); + properties_.emplace_back("bufferMS2", &bufferMS2_); + properties_.emplace_back("bufferMS3", &bufferMS3_); + properties_.emplace_back("bufferMS4", &bufferMS4_); + properties_.emplace_back("bufferMS5", &bufferMS5_); + properties_.emplace_back("bufferMS6", &bufferMS6_); + properties_.emplace_back("bufferMS7", &bufferMS7_); } // TODO: want variable number of buffers //Property buffers_{{}, {}, {"Buffers"}}; - Property buffer0_{{}, {"Buffer 0"}}; + Property buffer0_{{}, {"Buffer 0"}, {}}; Property buffer1_{{}, {"Buffer 1"}, {}}; Property buffer2_{{}, {"Buffer 2"}, {}}; Property buffer3_{{}, {"Buffer 3"}, {}}; @@ -60,6 +77,14 @@ class RenderTarget : public BaseObject { Property buffer5_{{}, {"Buffer 5"}, {}}; Property buffer6_{{}, {"Buffer 6"}, {}}; Property buffer7_{{}, {"Buffer 7"}, {}}; + Property bufferMS0_{{}, {"Buffer (Multisampled) 0"}, {}}; + Property bufferMS1_{{}, {"Buffer (Multisampled) 1"}, {}}; + Property bufferMS2_{{}, {"Buffer (Multisampled) 2"}, {}}; + Property bufferMS3_{{}, {"Buffer (Multisampled) 3"}, {}}; + Property bufferMS4_{{}, {"Buffer (Multisampled) 4"}, {}}; + Property bufferMS5_{{}, {"Buffer (Multisampled) 5"}, {}}; + Property bufferMS6_{{}, {"Buffer (Multisampled) 6"}, {}}; + Property bufferMS7_{{}, {"Buffer (Multisampled) 7"}, {}}; }; using SRenderTarget = std::shared_ptr; diff --git a/datamodel/libUserTypes/include/user_types/Texture.h b/datamodel/libUserTypes/include/user_types/Texture.h index 4d9a15b0..9d8ef8d3 100644 --- a/datamodel/libUserTypes/include/user_types/Texture.h +++ b/datamodel/libUserTypes/include/user_types/Texture.h @@ -41,6 +41,7 @@ class Texture : public TextureSampler2DBase { properties_.emplace_back("level4uri", &level4uri_); } + void onAfterContextActivated(BaseContext& context) override; void onAfterValueChanged(BaseContext& context, ValueHandle const& value) override; void updateFromExternalFile(BaseContext& context) override; diff --git a/datamodel/libUserTypes/include/user_types/UserObjectFactory.h b/datamodel/libUserTypes/include/user_types/UserObjectFactory.h index ca6daba0..0f2cafb5 100644 --- a/datamodel/libUserTypes/include/user_types/UserObjectFactory.h +++ b/datamodel/libUserTypes/include/user_types/UserObjectFactory.h @@ -26,6 +26,8 @@ class Texture; using STexture = std::shared_ptr; class TextureSampler2DBase; using STextureSampler2DBase = std::shared_ptr; +class RenderBufferMS; +using SRenderBufferMS = std::shared_ptr; class BlendOptions; @@ -54,6 +56,8 @@ class UserObjectFactory : public raco::core::UserObjectFactoryInterface { Property, Property, + Property, + Property, Property, Property, @@ -69,6 +73,7 @@ class UserObjectFactory : public raco::core::UserObjectFactoryInterface { Property, Property, Property, + Property, Property, Property, @@ -86,6 +91,7 @@ class UserObjectFactory : public raco::core::UserObjectFactoryInterface { Property, Property, Property, + Property, Property, Property, @@ -103,6 +109,7 @@ class UserObjectFactory : public raco::core::UserObjectFactoryInterface { Property, Property, Property, + Property, Property, Property, @@ -118,6 +125,7 @@ class UserObjectFactory : public raco::core::UserObjectFactoryInterface { Property, Property, Property, + Property, Property >; diff --git a/datamodel/libUserTypes/src/Animation.cpp b/datamodel/libUserTypes/src/Animation.cpp index bb8df246..c7aa617c 100644 --- a/datamodel/libUserTypes/src/Animation.cpp +++ b/datamodel/libUserTypes/src/Animation.cpp @@ -16,10 +16,6 @@ namespace raco::user_types { -void Animation::onBeforeDeleteObject(Errors& errors) const { - BaseObject::onBeforeDeleteObject(errors); -} - void Animation::onAfterContextActivated(BaseContext& context) { // Only set default animation channel amount when animationChannels is empty (ie. created by user) // TODO: the initial creation of the channel should be in the constructor. diff --git a/datamodel/libUserTypes/src/BlitPass.cpp b/datamodel/libUserTypes/src/BlitPass.cpp new file mode 100644 index 00000000..5d9dd264 --- /dev/null +++ b/datamodel/libUserTypes/src/BlitPass.cpp @@ -0,0 +1,56 @@ +/* + * SPDX-License-Identifier: MPL-2.0 + * + * This file is part of Ramses Composer + * (see https://github.com/bmwcarit/ramses-composer). + * + * This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. + * If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +#include "user_types/BlitPass.h" +#include "Validation.h" +#include "core/Context.h" +#include "core/Handles.h" +#include "core/PathQueries.h" +#include "core/Project.h" +#include "core/Queries.h" +#include "user_types/LuaScriptModule.h" +#include "utils/FileUtils.h" + +namespace raco::user_types { + +void BlitPass::onAfterContextActivated(BaseContext& context) { + BaseObject::onAfterContextActivated(context); + + validateBufferCompatibility(context); +} + +void BlitPass::onAfterValueChanged(BaseContext& context, ValueHandle const& value) { + BaseObject::onAfterValueChanged(context, value); + + validateBufferCompatibility(context); + + context.changeMultiplexer().recordPreviewDirty(shared_from_this()); +} + +void BlitPass::onAfterReferencedObjectChanged(BaseContext& context, ValueHandle const& changedObject) { + context.changeMultiplexer().recordPreviewDirty(shared_from_this()); +} + +void BlitPass::validateBufferCompatibility(BaseContext & context) { + ValueHandle sourceBufferHandle{shared_from_this(), &user_types::BlitPass::sourceRenderBufferMS_}; + if (*sourceRenderBuffer_ && *sourceRenderBufferMS_) { + context.errors().addError(raco::core::ErrorCategory::GENERAL, ErrorLevel::WARNING, sourceBufferHandle, "Single-sample and multi-sample source buffer selected: Single-sample source buffer will be preferred."); + } else { + context.errors().removeError(sourceBufferHandle); + } + + ValueHandle targetBufferHandle{shared_from_this(), &user_types::BlitPass::targetRenderBufferMS_}; + if (*targetRenderBuffer_ && *targetRenderBufferMS_) { + context.errors().addError(raco::core::ErrorCategory::GENERAL, ErrorLevel::WARNING, targetBufferHandle, "Single-sample and multi-sample target buffer selected: Single-sample target buffer will be preferred."); + } else { + context.errors().removeError(targetBufferHandle); + } +} + +} // namespace raco::user_types diff --git a/datamodel/libUserTypes/src/CubeMap.cpp b/datamodel/libUserTypes/src/CubeMap.cpp index 8355a4ba..713d0677 100644 --- a/datamodel/libUserTypes/src/CubeMap.cpp +++ b/datamodel/libUserTypes/src/CubeMap.cpp @@ -16,6 +16,13 @@ namespace raco::user_types { + +void CubeMap::onAfterContextActivated(BaseContext& context) { + BaseObject::onAfterContextActivated(context); + + validateMipmapLevel(context); +} + void CubeMap::onAfterValueChanged(BaseContext& context, ValueHandle const& value) { BaseObject::onAfterValueChanged(context, value); @@ -26,6 +33,7 @@ void CubeMap::onAfterValueChanged(BaseContext& context, ValueHandle const& value validateMipmapLevel(context); } } + void CubeMap::updateFromExternalFile(BaseContext& context) { validateURIs(context); @@ -95,7 +103,6 @@ void CubeMap::validateURIs(BaseContext& context) { void CubeMap::validateMipmapLevel(BaseContext& context) { auto mipmapLevelValue = ValueHandle{shared_from_this(), &raco::user_types::CubeMap::mipmapLevel_}; - context.errors().removeError(mipmapLevelValue); if (*mipmapLevel_ < 1 || *mipmapLevel_ > 4) { context.errors().addError(core::ErrorCategory::GENERAL, core::ErrorLevel::ERROR, mipmapLevelValue, @@ -103,6 +110,8 @@ void CubeMap::validateMipmapLevel(BaseContext& context) { } else if (*generateMipmaps_ && *mipmapLevel_ != 1) { context.errors().addError(core::ErrorCategory::GENERAL, core::ErrorLevel::WARNING, mipmapLevelValue, "Mipmap level larger than 1 specified while auto-generation flag is on - Ramses will ignore the auto-generation flag and still try to use the manually specified URIs."); + } else { + context.errors().removeError(mipmapLevelValue); } } diff --git a/datamodel/libUserTypes/src/RenderBufferMS.cpp b/datamodel/libUserTypes/src/RenderBufferMS.cpp new file mode 100644 index 00000000..cfba6d8a --- /dev/null +++ b/datamodel/libUserTypes/src/RenderBufferMS.cpp @@ -0,0 +1,38 @@ +/* + * SPDX-License-Identifier: MPL-2.0 + * + * This file is part of Ramses Composer + * (see https://github.com/bmwcarit/ramses-composer). + * + * This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. + * If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include "user_types/RenderBufferMS.h" + +namespace raco::user_types { + +void RenderBufferMS::onAfterContextActivated(BaseContext& context) { + BaseObject::onAfterContextActivated(context); + + validateSampleCount(context); +} + +void RenderBufferMS::onAfterValueChanged(BaseContext& context, ValueHandle const& value) { + BaseObject::onAfterValueChanged(context, value); + + if (value.isRefToProp(&RenderBufferMS::sampleCount_)) { + validateSampleCount(context); + } +} + +void RenderBufferMS::validateSampleCount(BaseContext& context) { + ValueHandle handle{shared_from_this(), &RenderBufferMS::sampleCount_}; + if (*sampleCount_ < SAMPLE_COUNT_MIN || *sampleCount_ > SAMPLE_COUNT_MAX) { + context.errors().addError(ErrorCategory::GENERAL, ErrorLevel::ERROR, handle, "Invalid sample count.\nOnly values from 1 to 8 are allowed."); + } else { + context.errors().removeError(handle); + } +} + +} // namespace raco::user_types \ No newline at end of file diff --git a/datamodel/libUserTypes/src/SyncTableWithEngineInterface.cpp b/datamodel/libUserTypes/src/SyncTableWithEngineInterface.cpp index 2fde7f8c..a4728d99 100644 --- a/datamodel/libUserTypes/src/SyncTableWithEngineInterface.cpp +++ b/datamodel/libUserTypes/src/SyncTableWithEngineInterface.cpp @@ -18,6 +18,7 @@ #include "user_types/Texture.h" #include "user_types/EngineTypeAnnotation.h" #include "user_types/UserObjectFactory.h" +#include "user_types/RenderBufferMS.h" namespace raco::user_types { @@ -76,6 +77,10 @@ raco::data_storage::ValueBase* createDynamicProperty(EnginePrimitive type) { return UserObjectFactory::staticCreateProperty({}, {type}, {Args()}...); break; + case EnginePrimitive::TextureSampler2DMS: + return UserObjectFactory::staticCreateProperty({}, {type}, {Args()}...); + break; + case EnginePrimitive::TextureSamplerCube: return UserObjectFactory::staticCreateProperty({}, {type}, {Args()}...); break; diff --git a/datamodel/libUserTypes/src/Texture.cpp b/datamodel/libUserTypes/src/Texture.cpp index d873be7a..3548cbd6 100644 --- a/datamodel/libUserTypes/src/Texture.cpp +++ b/datamodel/libUserTypes/src/Texture.cpp @@ -14,6 +14,12 @@ namespace raco::user_types { +void Texture::onAfterContextActivated(BaseContext& context) { + BaseObject::onAfterContextActivated(context); + + validateMipmapLevel(context); +} + void Texture::onAfterValueChanged(BaseContext& context, ValueHandle const& value) { BaseObject::onAfterValueChanged(context, value); @@ -55,14 +61,15 @@ void Texture::validateURIs(BaseContext& context) { void Texture::validateMipmapLevel(BaseContext& context) { auto mipmapLevelValue = ValueHandle{shared_from_this(), &raco::user_types::Texture::mipmapLevel_}; - context.errors().removeError(mipmapLevelValue); - + if (*mipmapLevel_ < 1 || *mipmapLevel_ > 4) { context.errors().addError(core::ErrorCategory::GENERAL, core::ErrorLevel::ERROR, mipmapLevelValue, "Invalid mipmap level - only mipmap levels 1 to 4 are allowed."); } else if (*generateMipmaps_ && *mipmapLevel_ != 1) { context.errors().addError(core::ErrorCategory::GENERAL, core::ErrorLevel::WARNING, mipmapLevelValue, "Mipmap level larger than 1 specified while auto-generation flag is on - Ramses will ignore the auto-generation flag and still try to use the manually specified URIs."); + } else { + context.errors().removeError(mipmapLevelValue); } } diff --git a/datamodel/libUserTypes/src/UserObjectFactory.cpp b/datamodel/libUserTypes/src/UserObjectFactory.cpp index 59a21cdc..d122758d 100644 --- a/datamodel/libUserTypes/src/UserObjectFactory.cpp +++ b/datamodel/libUserTypes/src/UserObjectFactory.cpp @@ -18,6 +18,7 @@ #include "user_types/BaseCamera.h" #include "user_types/BaseObject.h" #include "user_types/BaseTexture.h" +#include "user_types/BlitPass.h" #include "user_types/CubeMap.h" #include "user_types/LuaInterface.h" #include "user_types/LuaScript.h" @@ -31,6 +32,7 @@ #include "user_types/Prefab.h" #include "user_types/PrefabInstance.h" #include "user_types/RenderBuffer.h" +#include "user_types/RenderBufferMS.h" #include "user_types/RenderLayer.h" #include "user_types/RenderTarget.h" #include "user_types/RenderPass.h" @@ -86,7 +88,8 @@ UserObjectFactory::UserObjectFactory() { AnchorPoint, Animation, AnimationChannel, - CubeMap, + BlitPass, + CubeMap, Node, MeshNode, Mesh, @@ -101,6 +104,7 @@ UserObjectFactory::UserObjectFactory() { Texture, Timer, RenderBuffer, + RenderBufferMS, RenderLayer, RenderTarget, RenderPass diff --git a/datamodel/libUserTypes/tests/CMakeLists.txt b/datamodel/libUserTypes/tests/CMakeLists.txt index 06317603..f5612ff4 100644 --- a/datamodel/libUserTypes/tests/CMakeLists.txt +++ b/datamodel/libUserTypes/tests/CMakeLists.txt @@ -28,6 +28,7 @@ set(TEST_SOURCES set(TEST_LIBRARIES raco::RamsesBase raco::UserTypes + raco::ApplicationLib raco::Testing ) raco_package_add_headless_test( @@ -58,4 +59,6 @@ raco_package_add_test_resources( scripts/types-scalar.lua shaders/basic.vert shaders/basic.frag + shaders/uniform-array.vert + shaders/uniform-array.frag ) diff --git a/datamodel/libUserTypes/tests/Material_test.cpp b/datamodel/libUserTypes/tests/Material_test.cpp index 101e97ac..5b7b4e95 100644 --- a/datamodel/libUserTypes/tests/Material_test.cpp +++ b/datamodel/libUserTypes/tests/Material_test.cpp @@ -54,6 +54,32 @@ TEST_F(MaterialTest, validShader) { ASSERT_FALSE(commandInterface.errors().hasError(fragmentUriHandle)); } +TEST_F(MaterialTest, uniform_array) { + auto mat = create_material("material", (test_path() / "shaders/uniform-array.vert").string(), (test_path() / "shaders/uniform-array.frag").string()); + + auto checkArray = [](const Table& container, std::string propName, int size, PrimitiveType type) { + EXPECT_TRUE(container.hasProperty(propName)); + EXPECT_EQ(container.get(propName)->type(), PrimitiveType::Table); + auto& array = container.get(propName)->asTable(); + EXPECT_EQ(array.size(), size); + for (int index = 0; index < size; index++) { + EXPECT_EQ(array.name(index), std::to_string(index + 1)); + EXPECT_EQ(array.get(index)->type(), type); + } + }; + + checkArray(*mat->uniforms_, "ivec", 2, PrimitiveType::Int); + checkArray(*mat->uniforms_, "fvec", 5, PrimitiveType::Double); + + checkArray(*mat->uniforms_, "avec2", 4, PrimitiveType::Struct); + checkArray(*mat->uniforms_, "avec3", 5, PrimitiveType::Struct); + checkArray(*mat->uniforms_, "avec4", 6, PrimitiveType::Struct); + + checkArray(*mat->uniforms_, "aivec2", 4, PrimitiveType::Struct); + checkArray(*mat->uniforms_, "aivec3", 5, PrimitiveType::Struct); + checkArray(*mat->uniforms_, "aivec4", 6, PrimitiveType::Struct); +} + TEST_F(MaterialTest, settingShaderShouldAutomaticallySetOtherShadersIfPresent) { auto def = makeFile("shaders/basic.def", ""); diff --git a/datamodel/libUserTypes/tests/Texture_test.cpp b/datamodel/libUserTypes/tests/Texture_test.cpp index 6174adc1..9c6461dd 100644 --- a/datamodel/libUserTypes/tests/Texture_test.cpp +++ b/datamodel/libUserTypes/tests/Texture_test.cpp @@ -8,6 +8,9 @@ * If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "testing/TestEnvironmentCore.h" +#include "application/RaCoApplication.h" +#include "application/RaCoProject.h" +#include "core/Errors.h" #include "user_types/Texture.h" #include @@ -47,3 +50,31 @@ TEST_F(TextureTest, levelOtherThanOneWhenGenerationFlagIsActivated) { commandInterface.set({texture, &raco::user_types::Texture::generateMipmaps_}, false); ASSERT_FALSE(commandInterface.errors().hasError({texture, &raco::user_types::Texture::mipmapLevel_})); } + +TEST_F(TextureTest, error_present_after_load) { + raco::ramses_base::HeadlessEngineBackend backend{raco::ramses_base::BaseEngineBackend::maxFeatureLevel}; + + { + raco::application::RaCoApplication app{backend}; + auto& cmd = *app.activeRaCoProject().commandInterface(); + + auto texture{cmd.createObject(Texture::typeDescription.typeName, "texture")}; + cmd.set({texture, &raco::user_types::Texture::mipmapLevel_}, -1); + ASSERT_TRUE(cmd.errors().hasError({texture, &raco::user_types::Texture::mipmapLevel_})); + + std::string msg; + app.activeRaCoProject().saveAs(QString::fromStdString((test_path() / "test.rca").string()), msg); + } + + { + raco::application::RaCoApplicationLaunchSettings settings; + settings.initialProject = (test_path() / "test.rca").string().c_str(); + raco::application::RaCoApplication app{backend, settings}; + + auto& project = *app.activeRaCoProject().project(); + + auto texture = raco::core::Queries::findByName(project.instances(), "texture"); + + ASSERT_TRUE(app.activeRaCoProject().errors()->hasError({texture, &raco::user_types::Texture::mipmapLevel_})); + } +} \ No newline at end of file diff --git a/gui/libObjectTree/include/object_tree_view_model/ObjectTreeNode.h b/gui/libObjectTree/include/object_tree_view_model/ObjectTreeNode.h index 1d9acc22..33bf983a 100644 --- a/gui/libObjectTree/include/object_tree_view_model/ObjectTreeNode.h +++ b/gui/libObjectTree/include/object_tree_view_model/ObjectTreeNode.h @@ -17,6 +17,7 @@ enum class ObjectTreeNodeType { EditorObject, ExternalProject, ExtRefGroup, + TypeParent, Root }; @@ -24,6 +25,7 @@ class ObjectTreeNode { public: explicit ObjectTreeNode(core::SEditorObject obj, ObjectTreeNode *parent); explicit ObjectTreeNode(ObjectTreeNodeType type, ObjectTreeNode *parent); + explicit ObjectTreeNode(const std::string& typeName); ~ObjectTreeNode(); @@ -43,6 +45,7 @@ class ObjectTreeNode { std::string getID() const; std::string getDisplayName() const; std::string getDisplayType() const; + std::string getTypeName() const; std::string getExternalProjectName() const; std::string getExternalProjectPath() const; void setBelongsToExternalProject(const std::string &path, const std::string &name); @@ -52,6 +55,7 @@ class ObjectTreeNode { ObjectTreeNodeType type_; std::string externalProjectPath_ = ""; std::string externalProjectName_ = ""; + std::string typeName_ = ""; std::vector children_; core::SEditorObject representedObject_; }; diff --git a/gui/libObjectTree/include/object_tree_view_model/ObjectTreeViewDefaultModel.h b/gui/libObjectTree/include/object_tree_view_model/ObjectTreeViewDefaultModel.h index ab482b55..358d7039 100644 --- a/gui/libObjectTree/include/object_tree_view_model/ObjectTreeViewDefaultModel.h +++ b/gui/libObjectTree/include/object_tree_view_model/ObjectTreeViewDefaultModel.h @@ -51,7 +51,7 @@ class ObjectTreeViewDefaultModel : public QAbstractItemModel { }; ObjectTreeViewDefaultModel(raco::core::CommandInterface* commandInterface, components::SDataChangeDispatcher dispatcher, core::ExternalProjectsStoreInterface* externalProjectStore, const std::vector &allowedCreatableUserTypes, - bool groupExternalReferences = false); + bool groupExternalReferences = false, bool groupByType = false); int columnCount(const QModelIndex& parent = QModelIndex()) const override; int rowCount(const QModelIndex& parent = QModelIndex()) const override; @@ -130,13 +130,14 @@ public Q_SLOTS: components::Subscription afterDispatchSubscription_; components::Subscription extProjectChangedSubscription_; bool groupExternalReferences_; + bool groupByType_; // The dirty flag is set if the tree needs to be rebuilt. See afterDispatchSubscription_ member variable usage. bool dirty_ = false; virtual std::vector filterForTopLevelObjects(const std::vector& objects) const; virtual void setNodeExternalProjectInfo(ObjectTreeNode* node) const; - void constructTreeUnderNode(ObjectTreeNode* rootNode, const std::vector& children, bool groupExternalReferences); + void constructTreeUnderNode(ObjectTreeNode* rootNode, const std::vector& children, bool groupExternalReferences, bool groupByTypes); void resetInvisibleRootNode(); void updateTreeIndexes(); @@ -161,11 +162,13 @@ public Q_SLOTS: {"AnimationChannel", raco::style::Icons::instance().typeAnimationChannel}, {"Animation", raco::style::Icons::instance().typeAnimation}, {"Timer", raco::style::Icons::instance().typeTimer}, - {"AnchorPoint", raco::style::Icons::instance().typeAnchorPoint} + {"AnchorPoint", raco::style::Icons::instance().typeAnchorPoint}, + {"BlitPass", raco::style::Icons::instance().typeBlitPass} }; std::string getOriginPathFromMimeData(const QMimeData* data) const; QMimeData* generateMimeData(const QModelIndexList& indexes, const std::string& originPath) const; + void ensureTypeParentExists(std::map& typeParentMap, const std::string& typeName, ObjectTreeNode* parentNode); }; } diff --git a/gui/libObjectTree/include/object_tree_view_model/ObjectTreeViewSortProxyModels.h b/gui/libObjectTree/include/object_tree_view_model/ObjectTreeViewSortProxyModels.h index 10f4ea9b..fc6700a9 100644 --- a/gui/libObjectTree/include/object_tree_view_model/ObjectTreeViewSortProxyModels.h +++ b/gui/libObjectTree/include/object_tree_view_model/ObjectTreeViewSortProxyModels.h @@ -26,6 +26,15 @@ class ObjectTreeViewDefaultSortFilterProxyModel : public QSortFilterProxyModel { bool sortingEnabled_; }; +// This class sorts External References always to the top. TypeParent nodes will always be sorted alphabetically in descending order. All other nodes will be sorted regularly. +class ObjectTreeViewResourceSortFilterProxyModel : public ObjectTreeViewDefaultSortFilterProxyModel { +public: + ObjectTreeViewResourceSortFilterProxyModel(QObject *parent = nullptr) : ObjectTreeViewDefaultSortFilterProxyModel(parent, true) {} + +protected: + bool lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const override; + +}; // This class only sorts top-level nodes (nodes under the invisible root node in ObjectTreeView models) while keeping the scenegraph structure of child nodes. class ObjectTreeViewTopLevelSortFilterProxyModel : public ObjectTreeViewDefaultSortFilterProxyModel { diff --git a/gui/libObjectTree/src/object_tree_view_model/ObjectTreeNode.cpp b/gui/libObjectTree/src/object_tree_view_model/ObjectTreeNode.cpp index 6139b78b..6798920d 100644 --- a/gui/libObjectTree/src/object_tree_view_model/ObjectTreeNode.cpp +++ b/gui/libObjectTree/src/object_tree_view_model/ObjectTreeNode.cpp @@ -11,7 +11,7 @@ #include #include -#include +#include "core/ProxyTypes.h" namespace raco::object_tree::model { @@ -38,6 +38,13 @@ ObjectTreeNode::ObjectTreeNode(ObjectTreeNodeType type, ObjectTreeNode* parent) } } +ObjectTreeNode::ObjectTreeNode(const std::string& typeName) + : parent_{nullptr}, + type_{ObjectTreeNodeType::TypeParent}, + representedObject_{nullptr}, + typeName_{typeName} { +} + ObjectTreeNode::~ObjectTreeNode() { for (auto child : children_) { delete child; @@ -88,6 +95,12 @@ ObjectTreeNodeType ObjectTreeNode::getType() const { return type_; } +namespace { + std::map irregularObjectTypePluralNames{ + {raco::serialization::proxy::meshTypeName, "Meshes"}, + {raco::serialization::proxy::renderPassTypeName, "RenderPasses"}}; +} + std::string ObjectTreeNode::getDisplayName() const { switch (type_) { case ObjectTreeNodeType::EditorObject: @@ -96,7 +109,13 @@ std::string ObjectTreeNode::getDisplayName() const { return externalProjectPath_; case ObjectTreeNodeType::ExtRefGroup: return "External References"; - default: + case ObjectTreeNodeType::TypeParent: + if (irregularObjectTypePluralNames.find(typeName_) != irregularObjectTypePluralNames.end()) { + return irregularObjectTypePluralNames[typeName_]; + } + + return typeName_ + "s"; + default: return ""; } } @@ -111,6 +130,10 @@ std::string ObjectTreeNode::getDisplayType() const { } } +std::string ObjectTreeNode::getTypeName() const { + return typeName_; +} + std::string ObjectTreeNode::getExternalProjectPath() const { return externalProjectPath_; } @@ -133,6 +156,10 @@ std::string ObjectTreeNode::getID() const { return externalProjectPath_; case ObjectTreeNodeType::ExtRefGroup: return "External References"; + case ObjectTreeNodeType::TypeParent: + return parent_->getType() == ObjectTreeNodeType::ExtRefGroup + ? "external " + typeName_ + : typeName_; case ObjectTreeNodeType::Root: return "Root"; default: @@ -143,7 +170,6 @@ std::string ObjectTreeNode::getID() const { SEditorObject ObjectTreeNode::getRepresentedObject() const { return representedObject_; } - void ObjectTreeNode::setParent(ObjectTreeNode* parent) { parent_ = parent; } diff --git a/gui/libObjectTree/src/object_tree_view_model/ObjectTreeViewDefaultModel.cpp b/gui/libObjectTree/src/object_tree_view_model/ObjectTreeViewDefaultModel.cpp index 9e49663c..087d85c7 100644 --- a/gui/libObjectTree/src/object_tree_view_model/ObjectTreeViewDefaultModel.cpp +++ b/gui/libObjectTree/src/object_tree_view_model/ObjectTreeViewDefaultModel.cpp @@ -42,12 +42,14 @@ ObjectTreeViewDefaultModel::ObjectTreeViewDefaultModel(raco::core::CommandInterf components::SDataChangeDispatcher dispatcher, core::ExternalProjectsStoreInterface* externalProjectStore, const std::vector& allowedCreatableUserTypes, - bool groupExternalReferences) + bool groupExternalReferences, + bool groupByType) : dispatcher_{dispatcher}, commandInterface_{commandInterface}, externalProjectStore_{externalProjectStore}, allowedUserCreatableUserTypes_(allowedCreatableUserTypes), - groupExternalReferences_(groupExternalReferences) { + groupExternalReferences_(groupExternalReferences), + groupByType_(groupByType){ resetInvisibleRootNode(); lifeCycleSubscriptions_["objectLifecycle"].emplace_back(dispatcher_->registerOnObjectsLifeCycle( @@ -89,22 +91,53 @@ QVariant ObjectTreeViewDefaultModel::data(const QModelIndex& index, int role) co switch (role) { case Qt::DecorationRole: { - auto editorObj = treeNode->getRepresentedObject(); - if (editorObj && index.column() == COLUMNINDEX_NAME) { - auto itr = typeIconMap.find(editorObj->getTypeDescription().typeName); + std::string typeName; + if (treeNode->getType() == ObjectTreeNodeType::TypeParent) { + typeName = treeNode->getTypeName(); + } else { + auto editorObj = treeNode->getRepresentedObject(); + if (editorObj) { + typeName = editorObj->getTypeDescription().typeName; + } + } + + if (!typeName.empty() && index.column() == COLUMNINDEX_NAME) { + auto itr = typeIconMap.find(typeName); if (itr == typeIconMap.end()) return QVariant(); return QVariant(itr->second); } return QVariant(); } + case Qt::FontRole: { + if (treeNode->getType() == ObjectTreeNodeType::TypeParent) { + QFont font; + font.setItalic(true); + return QVariant(font); + } + + return QVariant(); + } case Qt::ForegroundRole: { auto editorObj = treeNode->getRepresentedObject(); - if (editorObj && editorObj->query() || treeNode->getType() == ObjectTreeNodeType::ExtRefGroup) { + if (editorObj && editorObj->query()) { return QVariant(Colors::color(Colormap::externalReference)); - } else if (editorObj && Queries::isReadOnly(editorObj)) { + } + + if (treeNode->getType() == ObjectTreeNodeType::ExtRefGroup) { + return QVariant(Colors::color(Colormap::externalReferenceDisabled)); + } + + if (editorObj && Queries::isReadOnly(editorObj)) { return QVariant(Colors::color(Colormap::textDisabled)); } + + if (treeNode->getType() == ObjectTreeNodeType::TypeParent) { + return treeNode->getParent()->getType() == ObjectTreeNodeType::ExtRefGroup + ? QVariant(Colors::color(Colormap::externalReferenceDisabled)) + : QVariant(Colors::color(Colormap::textDisabled)); + } + return QVariant(Colors::color(Colormap::text)); } case Qt::DisplayRole: { @@ -356,19 +389,12 @@ void ObjectTreeViewDefaultModel::buildObjectTree() { return; } - // We don't have a settings object in the unit tests. - if (project()->settings()) { - nodeSubscriptions_["objectName"].emplace_back(dispatcher_->registerOn(ValueHandle(project()->settings(), {"objectName"}), [this]() { - dirty_ = true; - })); - } - auto filteredEditorObjects = filterForTopLevelObjects(project()->instances()); beginResetModel(); resetInvisibleRootNode(); - constructTreeUnderNode(invisibleRootNode_.get(), filteredEditorObjects, groupExternalReferences_); + constructTreeUnderNode(invisibleRootNode_.get(), filteredEditorObjects, groupExternalReferences_, groupByType_); updateTreeIndexes(); endResetModel(); @@ -384,25 +410,44 @@ void ObjectTreeViewDefaultModel::setNodeExternalProjectInfo(ObjectTreeNode* node } } -void ObjectTreeViewDefaultModel::constructTreeUnderNode(ObjectTreeNode* rootNode, const std::vector& children, bool groupExternalReferences) { +void ObjectTreeViewDefaultModel::ensureTypeParentExists(std::map& typeParentMap, const std::string& typeName, ObjectTreeNode* parentNode) { + if (typeParentMap.find(typeName) != typeParentMap.end()) { + return; + } + + auto* parent = new ObjectTreeNode(typeName); + typeParentMap[typeName] = parent; + parentNode->addChildFront(parent); +} +void ObjectTreeViewDefaultModel::constructTreeUnderNode(ObjectTreeNode* rootNode, const std::vector& children, bool groupExternalReferences, bool groupByTypes) { auto rootObject = rootNode->getRepresentedObject(); - auto extrefParent = groupExternalReferences ? nullptr : rootNode; + auto* extRefParent = groupExternalReferences ? nullptr : rootNode; + std::map extRefTypeParents; + std::map typeParents; for (const auto& obj : children) { - auto parentNode = rootNode; + auto* parentNode = rootNode; if (obj->query()) { - if (!extrefParent) { - extrefParent = new ObjectTreeNode(ObjectTreeNodeType::ExtRefGroup, nullptr); - rootNode->addChildFront(extrefParent); + if (extRefParent == nullptr) { + extRefParent = new ObjectTreeNode(ObjectTreeNodeType::ExtRefGroup, nullptr); + rootNode->addChildFront(extRefParent); + } + if (groupByTypes) { + ensureTypeParentExists(extRefTypeParents, obj->getTypeDescription().typeName, extRefParent); + parentNode = extRefTypeParents[obj->getTypeDescription().typeName]; + } else { + parentNode = extRefParent; } - parentNode = extrefParent; + } else if (groupByTypes) { + ensureTypeParentExists(typeParents, obj->getTypeDescription().typeName, rootNode); + parentNode = typeParents[obj->getTypeDescription().typeName]; } - auto node = new ObjectTreeNode(obj, parentNode); + auto* node = new ObjectTreeNode(obj, parentNode); setNodeExternalProjectInfo(node); - constructTreeUnderNode(node, obj->children_->asVector(), false); + constructTreeUnderNode(node, obj->children_->asVector(), false, false); } } diff --git a/gui/libObjectTree/src/object_tree_view_model/ObjectTreeViewExternalProjectModel.cpp b/gui/libObjectTree/src/object_tree_view_model/ObjectTreeViewExternalProjectModel.cpp index 5ef771e2..5550ede1 100644 --- a/gui/libObjectTree/src/object_tree_view_model/ObjectTreeViewExternalProjectModel.cpp +++ b/gui/libObjectTree/src/object_tree_view_model/ObjectTreeViewExternalProjectModel.cpp @@ -159,7 +159,7 @@ void ObjectTreeViewExternalProjectModel::buildObjectTree() { projectRootNode->setBelongsToExternalProject(projectPath, projectName); if (commandInterface) { auto filteredExternalProjectObjects = filterForTopLevelObjects(commandInterface->project()->instances()); - constructTreeUnderNode(projectRootNode, filteredExternalProjectObjects, groupExternalReferences_); + constructTreeUnderNode(projectRootNode, filteredExternalProjectObjects, groupExternalReferences_, false); } } updateTreeIndexes(); diff --git a/gui/libObjectTree/src/object_tree_view_model/ObjectTreeViewResourceModel.cpp b/gui/libObjectTree/src/object_tree_view_model/ObjectTreeViewResourceModel.cpp index a93494da..9fb969e4 100644 --- a/gui/libObjectTree/src/object_tree_view_model/ObjectTreeViewResourceModel.cpp +++ b/gui/libObjectTree/src/object_tree_view_model/ObjectTreeViewResourceModel.cpp @@ -21,7 +21,7 @@ namespace raco::object_tree::model { ObjectTreeViewResourceModel::ObjectTreeViewResourceModel(raco::core::CommandInterface* commandInterface, components::SDataChangeDispatcher dispatcher, core::ExternalProjectsStoreInterface* externalProjectStore, const std::vector& allowedCreatableUserTypes) - : ObjectTreeViewDefaultModel(commandInterface, dispatcher, externalProjectStore, allowedCreatableUserTypes, true) {} + : ObjectTreeViewDefaultModel(commandInterface, dispatcher, externalProjectStore, allowedCreatableUserTypes, true, true) {} bool ObjectTreeViewResourceModel::pasteObjectAtIndex(const QModelIndex& index, bool pasteAsExtref, std::string* outError, const std::string& serializedObjects) { diff --git a/gui/libObjectTree/src/object_tree_view_model/ObjectTreeViewSortProxyModels.cpp b/gui/libObjectTree/src/object_tree_view_model/ObjectTreeViewSortProxyModels.cpp index d02018a3..d351d1cd 100644 --- a/gui/libObjectTree/src/object_tree_view_model/ObjectTreeViewSortProxyModels.cpp +++ b/gui/libObjectTree/src/object_tree_view_model/ObjectTreeViewSortProxyModels.cpp @@ -29,7 +29,8 @@ bool ObjectTreeViewDefaultSortFilterProxyModel::sortingEnabled() const { QVariant ObjectTreeViewDefaultSortFilterProxyModel::data(const QModelIndex& index, int role) const { if (index.isValid()) { - if (static_cast(mapToSource(index).internalPointer())->getType() == ObjectTreeNodeType::ExtRefGroup) { + auto nodeType = static_cast(mapToSource(index).internalPointer())->getType(); + if (nodeType == ObjectTreeNodeType::ExtRefGroup || nodeType == ObjectTreeNodeType::TypeParent) { return QSortFilterProxyModel::data(index, role); } @@ -49,6 +50,33 @@ QVariant ObjectTreeViewDefaultSortFilterProxyModel::data(const QModelIndex& inde return QSortFilterProxyModel::data(index, role); } +bool ObjectTreeViewResourceSortFilterProxyModel::lessThan(const QModelIndex& source_left, const QModelIndex& source_right) const { + auto leftType = static_cast(source_left.internalPointer())->getType(); + if (leftType == ObjectTreeNodeType::ExtRefGroup) { + return sortOrder() == Qt::SortOrder::AscendingOrder; + } + + auto rightType = static_cast(source_right.internalPointer())->getType(); + if (rightType == ObjectTreeNodeType::ExtRefGroup) { + return sortOrder() == Qt::SortOrder::DescendingOrder; + } + + if (leftType == rightType) { + if (leftType == ObjectTreeNodeType::TypeParent) { + auto left = static_cast(source_left.internalPointer())->getDisplayName(); + auto right = static_cast(source_right.internalPointer())->getDisplayName(); + + return sortOrder() == Qt::SortOrder::AscendingOrder + ? left < right + : left > right; + } + + return QSortFilterProxyModel::lessThan(source_left, source_right); + } + + return false; +} + bool ObjectTreeViewTopLevelSortFilterProxyModel::lessThan(const QModelIndex& source_left, const QModelIndex& source_right) const { // The external reference grouping element should always be on top of the list if (static_cast(source_left.internalPointer())->getType() == ObjectTreeNodeType::ExtRefGroup) { diff --git a/gui/libObjectTree/tests/ObjectTreeViewResourceModel_test.cpp b/gui/libObjectTree/tests/ObjectTreeViewResourceModel_test.cpp index f86c7416..e253ef0f 100644 --- a/gui/libObjectTree/tests/ObjectTreeViewResourceModel_test.cpp +++ b/gui/libObjectTree/tests/ObjectTreeViewResourceModel_test.cpp @@ -15,8 +15,10 @@ #include "object_tree_view_model/ObjectTreeViewResourceModel.h" #include "user_types/AnchorPoint.h" #include "user_types/AnimationChannel.h" +#include "user_types/BlitPass.h" #include "user_types/LuaScriptModule.h" #include "user_types/RenderBuffer.h" +#include "user_types/RenderBufferMS.h" #include "user_types/RenderLayer.h" #include "user_types/RenderTarget.h" #include "user_types/RenderPass.h" @@ -31,6 +33,7 @@ class ObjectTreeViewResourceModelTest : public ObjectTreeViewDefaultModelTest { { AnchorPoint::typeDescription.typeName, AnimationChannel::typeDescription.typeName, + BlitPass::typeDescription.typeName, CubeMap::typeDescription.typeName, Material::typeDescription.typeName, Mesh::typeDescription.typeName, @@ -38,6 +41,7 @@ class ObjectTreeViewResourceModelTest : public ObjectTreeViewDefaultModelTest { Texture::typeDescription.typeName, Timer::typeDescription.typeName, RenderBuffer::typeDescription.typeName, + RenderBufferMS::typeDescription.typeName, RenderTarget::typeDescription.typeName, RenderLayer::typeDescription.typeName, RenderPass::typeDescription.typeName})); @@ -49,6 +53,7 @@ TEST_F(ObjectTreeViewResourceModelTest, TypesAllowedIntoIndexEmptyIndex) { std::vector allowedTypesAssert { AnchorPoint::typeDescription.typeName, AnimationChannel::typeDescription.typeName, + BlitPass::typeDescription.typeName, CubeMap::typeDescription.typeName, Material::typeDescription.typeName, Mesh::typeDescription.typeName, @@ -56,6 +61,7 @@ TEST_F(ObjectTreeViewResourceModelTest, TypesAllowedIntoIndexEmptyIndex) { Texture::typeDescription.typeName, Timer::typeDescription.typeName, RenderBuffer::typeDescription.typeName, + RenderBufferMS::typeDescription.typeName, RenderTarget::typeDescription.typeName, RenderLayer::typeDescription.typeName, RenderPass::typeDescription.typeName}; diff --git a/gui/libPropertyBrowser/src/PropertyBrowserItem.cpp b/gui/libPropertyBrowser/src/PropertyBrowserItem.cpp index 0f9d1339..f4418ed1 100644 --- a/gui/libPropertyBrowser/src/PropertyBrowserItem.cpp +++ b/gui/libPropertyBrowser/src/PropertyBrowserItem.cpp @@ -140,7 +140,8 @@ PropertyBrowserItem::PropertyBrowserItem( // when certain conditions depending on another property are fulfilled. static const std::map requiredChildSubscriptions = { {&user_types::RenderPass::typeDescription, "target"}, - {&user_types::RenderBuffer::typeDescription, "format"} + {&user_types::RenderBuffer::typeDescription, "format"}, + {&user_types::RenderLayer::typeDescription, "sortOrder"} }; if (const auto itChildSub = requiredChildSubscriptions.find(&valueHandle_.rootObject()->getTypeDescription()); valueHandle_.depth() == 0 && itChildSub != requiredChildSubscriptions.end()) { changeChildrenSub_ = dispatcher->registerOn(core::ValueHandle{valueHandle_.rootObject(), {itChildSub->second}}, [this, sceneBackend] { @@ -248,7 +249,8 @@ bool PropertyBrowserItem::editable() noexcept { } bool PropertyBrowserItem::expandable() const noexcept { - return valueHandle_.isObject() || !(query() || query()); + return valueHandle_.isObject() || + valueHandle_.hasSubstructure() && !query(); } bool PropertyBrowserItem::showChildren() const { @@ -363,29 +365,22 @@ void PropertyBrowserItem::setTags(std::vector> const void PropertyBrowserItem::createChildren(core::SceneBackendInterface* sceneBackend) { children_.reserve(static_cast(valueHandle_.size())); - if (const auto& renderPass = valueHandle_.rootObject()->as(); renderPass != nullptr && renderPass->target_.asRef() == nullptr) { + + for (int i{0}; i < valueHandle_.size(); i++) { + bool hidden = raco::core::Queries::isHiddenInPropertyBrowser(*project(), valueHandle_[i]); + // The render passes flags for clearing the target can only be used for offscreen rendering. // For the default framebuffer, the settings are in the project settings. // Given that this is a dynamic setting, do it here explicitly and not in Queries::isHidden for now. - // This needs to be refactored, see RAOS-XXX. - for (int i{0}; i < valueHandle_.size(); i++) { - if (!raco::core::Queries::isHiddenInPropertyBrowser(*project(), valueHandle_[i]) && !renderPass->isClearTargetProperty(valueHandle_[i])) { - children_.push_back(new PropertyBrowserItem(valueHandle_[i], dispatcher_, commandInterface_, sceneBackend, model_, this)); - } + if (const auto& renderPass = valueHandle_.rootObject()->as(); renderPass != nullptr && renderPass->target_.asRef() == nullptr && renderPass->isClearTargetProperty(valueHandle_[i])) { + hidden = true; + } else if (const auto& renderBuffer = valueHandle_.rootObject()->as(); renderBuffer != nullptr && !renderBuffer->areSamplingParametersSupported(engineInterface()) && renderBuffer->isSamplingProperty(valueHandle_[i])) { + hidden = true; } - } else if (const auto& renderBuffer = valueHandle_.rootObject()->as(); renderBuffer != nullptr && !renderBuffer->areSamplingParametersSupported(engineInterface())) { - // For the render buffer, the sampling properties should only be available for color formats, not for depth or stencil formats. - for (int i{0}; i < valueHandle_.size(); i++) { - if (!raco::core::Queries::isHiddenInPropertyBrowser(*project(), valueHandle_[i]) && !renderBuffer->isSamplingProperty(valueHandle_[i])) { - children_.push_back(new PropertyBrowserItem(valueHandle_[i], dispatcher_, commandInterface_, sceneBackend, model_, this)); - } + + if (!hidden) { + children_.push_back(new PropertyBrowserItem(valueHandle_[i], dispatcher_, commandInterface_, sceneBackend, model_, this)); } - } else { - for (int i{0}; i < valueHandle_.size(); i++) { - if (!raco::core::Queries::isHiddenInPropertyBrowser(*project(), valueHandle_[i])) { - children_.push_back(new PropertyBrowserItem(valueHandle_[i], dispatcher_, commandInterface_, sceneBackend, model_, this)); - } - } } } diff --git a/gui/libPropertyBrowser/src/PropertyBrowserWidget.cpp b/gui/libPropertyBrowser/src/PropertyBrowserWidget.cpp index f05a855e..3e45d123 100644 --- a/gui/libPropertyBrowser/src/PropertyBrowserWidget.cpp +++ b/gui/libPropertyBrowser/src/PropertyBrowserWidget.cpp @@ -18,6 +18,7 @@ #include "property_browser/PropertyBrowserLayouts.h" #include "property_browser/PropertyBrowserModel.h" #include "property_browser/PropertySubtreeView.h" +#include "core/ProjectSettings.h" #include "style/Icons.h" #include #include @@ -73,19 +74,12 @@ PropertyBrowserView::PropertyBrowserView(raco::core::SceneBackendInterface* scen : currentObjectID_(item->valueHandle().rootObject()->objectID()), sceneBackend_{sceneBackend}, QWidget{parent} { item->setParent(this); auto* layout = new PropertyBrowserGridLayout{this}; - layout->setColumnStretch(0, 1); auto* content = new QWidget{this}; auto* contentLayout = new PropertyBrowserVBoxLayout{content}; - contentLayout->setAlignment(Qt::AlignTop); contentLayout->setContentsMargins(0, 0, 5, 0); content->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::MinimumExpanding); - auto* scrollArea = new QScrollArea{this}; - scrollArea->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::MinimumExpanding); - scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarPolicy::ScrollBarAlwaysOn); - layout->addWidget(scrollArea, 0, 0, 3, Qt::AlignTop); - scrollArea->setWidget(content); - scrollArea->setWidgetResizable(true); + layout->addWidget(content, 0, 0); auto* topNotificationWidget = createNotificationWidget(model, this); layout->addWidget(topNotificationWidget, 0, 0); @@ -126,15 +120,6 @@ PropertyBrowserView::PropertyBrowserView(raco::core::SceneBackendInterface* scen } }); - QObject::connect(scrollArea->verticalScrollBar(), &QScrollBar::rangeChanged, this, [this, scrollArea](int min, int max) { - if (verticalPivotWidget_) { - auto newPosition = mapFromGlobal(verticalPivotWidget_->mapToGlobal({0, 0})); - scrollArea->verticalScrollBar()->setValue(scrollArea->verticalScrollBar()->value() + (newPosition.y() - verticalPivot_.y())); - } - verticalPivotWidget_ = nullptr; - verticalPivot_ = {0, 0}; - }); - contentLayout->addWidget(new PropertySubtreeView{sceneBackend_, model, item, this}); } @@ -215,10 +200,17 @@ void PropertyBrowserWidget::setValueHandle(core::ValueHandle valueHandle) { restorableObjectId_ = valueHandle.rootObject()->objectID(); subscription_ = dispatcher_->registerOnObjectsLifeCycle([](auto) {}, [this, valueHandle](core::SEditorObject obj) { if (valueHandle.rootObject() == obj) { - if (locked_) { - setLocked(false); + // SaveAs with new project ID will delete the ProjecSettings object and create a new one in order to change the object ID. + // We want to move a ProjectSettings property browser to the new ProjectSettings object automatically, + // so we detect this case and instead of clearing we find the new settings object and set a new ValueHandle with it. + if (obj->isType()) { + setValueHandle({commandInterface_->project()->settings()}); + } else { + if (locked_) { + setLocked(false); + } + clearValueHandle(true); } - clearValueHandle(true); } }); propertyBrowser_.reset(new PropertyBrowserView{sceneBackend_, new PropertyBrowserItem{valueHandle, dispatcher_, commandInterface_, sceneBackend_, model_}, model_, this}); diff --git a/gui/libRamsesWidgets/include/ramses_widgets/PreviewContentWidget.h b/gui/libRamsesWidgets/include/ramses_widgets/PreviewContentWidget.h index 17b5c15f..94381716 100644 --- a/gui/libRamsesWidgets/include/ramses_widgets/PreviewContentWidget.h +++ b/gui/libRamsesWidgets/include/ramses_widgets/PreviewContentWidget.h @@ -28,7 +28,7 @@ class PreviewContentWidget final : public QWidget { ramses::sceneId_t getSceneId(); void setSceneId(ramses::sceneId_t id); void setBackgroundColor(core::Vec4f backgroundColor); - void setFilteringMode(PreviewFilteringMode mode); + void setMsaaSampleRate(PreviewMultiSampleRate sampleRate); void commit(bool forceUpdate); public Q_SLOTS: diff --git a/gui/libRamsesWidgets/include/ramses_widgets/PreviewFramebufferScene.h b/gui/libRamsesWidgets/include/ramses_widgets/PreviewFramebufferScene.h index 61cef4df..badbe0e6 100644 --- a/gui/libRamsesWidgets/include/ramses_widgets/PreviewFramebufferScene.h +++ b/gui/libRamsesWidgets/include/ramses_widgets/PreviewFramebufferScene.h @@ -15,9 +15,11 @@ namespace raco::ramses_widgets { -enum class PreviewFilteringMode { - NearestNeighbor, - Linear +enum PreviewMultiSampleRate { + MSAA_1X = 1, + MSAA_2X = 2, + MSAA_4X = 4, + MSAA_8X = 8 }; class PreviewFramebufferScene final { @@ -27,7 +29,7 @@ class PreviewFramebufferScene final { explicit PreviewFramebufferScene(ramses::RamsesClient& client, ramses::sceneId_t sceneId); ramses::sceneId_t getSceneId() const; - ramses::dataConsumerId_t setupFramebufferTexture(RendererBackend& backend, const QSize& size, PreviewFilteringMode filteringMode); + ramses::dataConsumerId_t setupFramebufferTexture(RendererBackend& backend, const QSize& size, PreviewMultiSampleRate sampleRate); void setViewport(const QPoint& viewportPosition, const QSize& viewportSize, const QSize& virtualSize); private: @@ -45,8 +47,8 @@ class PreviewFramebufferScene final { raco::ramses_base::RamsesMeshNode meshNode_; // Offscreen texture and consumer - raco::ramses_base::RamsesTexture2D framebufferTexture_; - raco::ramses_base::RamsesTextureSampler sampler_; + raco::ramses_base::RamsesRenderBuffer renderbuffer_; + raco::ramses_base::RamsesTextureSamplerMS samplerMS_; ramses::dataConsumerId_t framebufferSampleId_; }; diff --git a/gui/libRamsesWidgets/include/ramses_widgets/RamsesPreviewWindow.h b/gui/libRamsesWidgets/include/ramses_widgets/RamsesPreviewWindow.h index 23c0c5fd..7c6d3519 100644 --- a/gui/libRamsesWidgets/include/ramses_widgets/RamsesPreviewWindow.h +++ b/gui/libRamsesWidgets/include/ramses_widgets/RamsesPreviewWindow.h @@ -39,16 +39,16 @@ class RamsesPreviewWindow final { QSize targetSize{0, 0}; QSize virtualSize{0, 0}; QColor backgroundColor{}; - PreviewFilteringMode filteringMode{PreviewFilteringMode::NearestNeighbor}; + PreviewMultiSampleRate sampleRate{MSAA_1X}; bool operator!=(const State & other) const { return this->backgroundColor != other.backgroundColor - || this->filteringMode != other.filteringMode || this->sceneId != other.sceneId || this->targetSize != other.targetSize || this->viewportOffset != other.viewportOffset || this->viewportSize != other.viewportSize - || this->virtualSize != other.virtualSize; + || this->virtualSize != other.virtualSize + || this->sampleRate != other.sampleRate; } }; diff --git a/gui/libRamsesWidgets/src/PreviewContentWidget.cpp b/gui/libRamsesWidgets/src/PreviewContentWidget.cpp index cf492db6..c83d03d5 100644 --- a/gui/libRamsesWidgets/src/PreviewContentWidget.cpp +++ b/gui/libRamsesWidgets/src/PreviewContentWidget.cpp @@ -42,6 +42,13 @@ void PreviewContentWidget::setBackgroundColor(core::Vec4f backgroundColor) { } } +void PreviewContentWidget::setMsaaSampleRate(PreviewMultiSampleRate sampleRate) { + if (ramsesPreview_) { + ramsesPreview_->nextState().sampleRate = sampleRate; + update(); + } +} + ramses::sceneId_t PreviewContentWidget::getSceneId() { if (ramsesPreview_) { return ramsesPreview_->nextState().sceneId; @@ -102,14 +109,6 @@ void PreviewContentWidget::paintEvent(QPaintEvent* e) { } } -void PreviewContentWidget::setFilteringMode(PreviewFilteringMode filteringMode) { - if (ramsesPreview_) { - ramsesPreview_->nextState().filteringMode = filteringMode; - update(); - } -} - - void PreviewContentWidget::commit(bool forceUpdate) { const auto& currentState = ramsesPreview_->currentState(); auto& nextState = ramsesPreview_->nextState(); diff --git a/gui/libRamsesWidgets/src/PreviewFramebufferScene.cpp b/gui/libRamsesWidgets/src/PreviewFramebufferScene.cpp index 9b8b4f69..6d5cf586 100644 --- a/gui/libRamsesWidgets/src/PreviewFramebufferScene.cpp +++ b/gui/libRamsesWidgets/src/PreviewFramebufferScene.cpp @@ -36,30 +36,37 @@ PreviewFramebufferScene::PreviewFramebufferScene( renderPass_->setClearFlags(ramses::EClearFlags_None); static const std::string vertexShader = - "#version 300 es\n\ + "#version 310 es\n\ precision mediump float;\n\ - in vec3 aPosition;\n\ - in vec2 aUVSet0;\n\ + uniform mat4 mvpMatrix;\n\ \n\ + in vec3 a_Position;\n\ + in vec2 a_TextureCoordinate;\n\ out vec2 vTC0;\n\ - uniform mat4 mvpMatrix;\n\ +\n\ void main() {\n\ - vTC0 = aUVSet0;\n\ - gl_Position = mvpMatrix * vec4(aPosition.xyz, 1.0);\n\ + gl_Position = mvpMatrix * vec4(a_Position, 1.0);\n\ + vTC0 = a_TextureCoordinate;\n\ }"; static const std::string fragmentShader = - "#version 300 es\n\ + "#version 310 es\n\ precision mediump float;\n\ \n\ in vec2 vTC0;\n\ - uniform sampler2D uTex0;\n\ + uniform mediump sampler2DMS uTex0;\n\ + uniform int sampleCount;\n\ \n\ out vec4 FragColor;\n\ \n\ void main() {\n\ - vec3 clr0 = texture(uTex0, vTC0).rgb;\n\ - FragColor = vec4(clr0, 1.0); \n\ + vec4 color = vec4(0.0);\n\ +\n\ + for (int i = 0; i < sampleCount; i++)\n\ + color += texelFetch(uTex0, ivec2(vTC0 * vec2(textureSize(uTex0))), i);\n\ +\n\ + color /= float(sampleCount);\n\ + FragColor = color;\n\ }"; ramses::EffectDescription effectDescription{}; @@ -97,11 +104,11 @@ PreviewFramebufferScene::PreviewFramebufferScene( (*geometryBinding_)->setIndices(*indexDataBuffer_.get()); ramses::AttributeInput vertexInput; - effect_->findAttributeInput("aPosition", vertexInput); + effect_->findAttributeInput("a_Position", vertexInput); (*geometryBinding_)->setInputBuffer(vertexInput, *vertexDataBuffer_.get()); ramses::AttributeInput uvInput; - effect_->findAttributeInput("aUVSet0", uvInput); + effect_->findAttributeInput("a_TextureCoordinate", uvInput); (*geometryBinding_)->setInputBuffer(uvInput, *uvDataBuffer_.get()); meshNode_ = ramsesMeshNode(scene_.get()); @@ -132,36 +139,36 @@ ramses::sceneId_t PreviewFramebufferScene::getSceneId() const { return scene_->getSceneId(); } -ramses::dataConsumerId_t PreviewFramebufferScene::setupFramebufferTexture(RendererBackend& backend, const QSize& size, PreviewFilteringMode filteringMode) { - ramses::ETextureSamplingMethod samplingMethod = ramses::ETextureSamplingMethod_Nearest; - if (filteringMode == PreviewFilteringMode::Linear) { - samplingMethod = ramses::ETextureSamplingMethod_Linear; - } - - if (framebufferTexture_ && framebufferTexture_->getWidth() == size.width() && framebufferTexture_->getHeight() == size.height() && sampler_->getMagSamplingMethod() == samplingMethod && sampler_->getMinSamplingMethod() == samplingMethod) { +ramses::dataConsumerId_t PreviewFramebufferScene::setupFramebufferTexture(RendererBackend& backend, const QSize& size, PreviewMultiSampleRate sampleRate) { + if (renderbuffer_ && renderbuffer_->getWidth() == size.width() && renderbuffer_->getHeight() == size.height() && renderbuffer_->getSampleCount() == sampleRate) { return framebufferSampleId_; } - auto& client = backend.client(); ramses::UniformInput texUniformInput; (*appearance_)->getEffect().findUniformInput("uTex0", texUniformInput); + ramses::UniformInput sampleRateUniformInput; + (*appearance_)->getEffect().findUniformInput("sampleCount", sampleRateUniformInput); + std::vector data(4 * size.width() * size.height(), 0); ramses::MipLevelData mipData(static_cast(data.size()), data.data()); const ramses::TextureSwizzle textureSwizzle{}; - framebufferTexture_ = ramsesTexture2D(scene_.get(), ramses::ETextureFormat::RGBA8, size.width(), size.height(), 1, &mipData, false, textureSwizzle, ramses::ResourceCacheFlag_DoNotCache, "framebuffer texture"); + renderbuffer_ = ramsesRenderBuffer(scene_.get(), size.width(), size.height(), ramses::ERenderBufferType_Color, ramses::ERenderBufferFormat_RGBA8, ramses::ERenderBufferAccessMode_ReadWrite, sampleRate); + ramses::RenderTargetDescription rtDesc; + rtDesc.addRenderBuffer(*renderbuffer_); - sampler_ = ramsesTextureSampler(scene_.get(), ramses::ETextureAddressMode_Clamp, ramses::ETextureAddressMode_Clamp, samplingMethod, samplingMethod, framebufferTexture_.get(), 1, "framebuffer sampler"); - (*appearance_)->setInputTexture(texUniformInput, *sampler_.get()); + samplerMS_ = ramsesTextureSamplerMS(scene_.get(), renderbuffer_); + (*appearance_)->setInputTexture(texUniformInput, *samplerMS_.get()); + (*appearance_)->setInputValueInt32(sampleRateUniformInput, sampleRate); scene_->flush(); static ramses::dataConsumerId_t id{42u}; framebufferSampleId_ = backend.internalDataConsumerId(); - scene_->createTextureConsumer(*sampler_.get(), framebufferSampleId_); + scene_->createTextureConsumer(*samplerMS_.get(), framebufferSampleId_); static const ramses::sceneVersionTag_t SCENE_VERSION_TAG_DATA_CONSUMER_CREATED{42}; static const ramses::sceneVersionTag_t SCENE_VERSION_TAG_RESET{41}; diff --git a/gui/libRamsesWidgets/src/PreviewMainWindow.cpp b/gui/libRamsesWidgets/src/PreviewMainWindow.cpp index 9c5576dc..6a3b2cc1 100644 --- a/gui/libRamsesWidgets/src/PreviewMainWindow.cpp +++ b/gui/libRamsesWidgets/src/PreviewMainWindow.cpp @@ -109,35 +109,50 @@ PreviewMainWindow::PreviewMainWindow(RendererBackend& rendererBackend, raco::ram }); ui_->toolBar->insertWidget(ui_->actionSelectSizeMode, sizeMenuButton); } - // Filtering mode tool button + // MSAA button { - auto* filteringMenu = new QMenu{ui_->toolBar}; - filteringMenu->addAction(ui_->actionSetFilteringModeNearestNeighbor); - filteringMenu->addAction(ui_->actionSetFilteringModeLinear); - ui_->actionSetFilteringModeNearestNeighbor->setCheckable(true); - ui_->actionSetFilteringModeNearestNeighbor->setChecked(true); - - - auto* filteringMenuButton = new QToolButton{ui_->toolBar}; - filteringMenuButton->setMenu(filteringMenu); - filteringMenuButton->setPopupMode(QToolButton::InstantPopup); - - ui_->actionSetFilteringModeLinear->setCheckable(true); - filteringMenuButton->setText(ui_->actionSetFilteringModeNearestNeighbor->text()); - - connect(ui_->actionSetFilteringModeNearestNeighbor, &QAction::triggered, this, [this, filteringMenuButton]() { - previewWidget_->setFilteringMode(PreviewFilteringMode::NearestNeighbor); - filteringMenuButton->setText(ui_->actionSetFilteringModeNearestNeighbor->text()); - ui_->actionSetFilteringModeNearestNeighbor->setChecked(true); - ui_->actionSetFilteringModeLinear->setChecked(false); + auto* msaaMenu = new QMenu{ui_->toolBar}; + msaaMenu->addAction(ui_->actionSetMSAAx1); + msaaMenu->addAction(ui_->actionSetMSAAx2); + msaaMenu->addAction(ui_->actionSetMSAAx4); + msaaMenu->addAction(ui_->actionSetMSAAx8); + ui_->actionSetMSAAx1->setCheckable(true); + ui_->actionSetMSAAx1->setChecked(true); + ui_->actionSetMSAAx2->setCheckable(true); + ui_->actionSetMSAAx4->setCheckable(true); + ui_->actionSetMSAAx8->setCheckable(true); + + auto* msaaMenuButton = new QToolButton{ui_->toolBar}; + msaaMenuButton->setMenu(msaaMenu); + msaaMenuButton->setPopupMode(QToolButton::InstantPopup); + + msaaMenuButton->setText(ui_->actionSetMSAAx1->text()); + + auto updateMsaaSelection = [this, msaaMenuButton](QAction* action) { + msaaMenuButton->setText(action->text()); + ui_->actionSetMSAAx1->setChecked(ui_->actionSetMSAAx1 == action); + ui_->actionSetMSAAx2->setChecked(ui_->actionSetMSAAx2 == action); + ui_->actionSetMSAAx4->setChecked(ui_->actionSetMSAAx4 == action); + ui_->actionSetMSAAx8->setChecked(ui_->actionSetMSAAx8 == action); + }; + + connect(ui_->actionSetMSAAx1, &QAction::triggered, this, [this, msaaMenuButton, updateMsaaSelection]() { + previewWidget_->setMsaaSampleRate(PreviewMultiSampleRate::MSAA_1X); + updateMsaaSelection(ui_->actionSetMSAAx1); + }); + connect(ui_->actionSetMSAAx2, &QAction::triggered, this, [this, msaaMenuButton, updateMsaaSelection]() { + previewWidget_->setMsaaSampleRate(PreviewMultiSampleRate::MSAA_2X); + updateMsaaSelection(ui_->actionSetMSAAx2); + }); + connect(ui_->actionSetMSAAx4, &QAction::triggered, this, [this, msaaMenuButton, updateMsaaSelection]() { + previewWidget_->setMsaaSampleRate(PreviewMultiSampleRate::MSAA_4X); + updateMsaaSelection(ui_->actionSetMSAAx4); }); - connect(ui_->actionSetFilteringModeLinear, &QAction::triggered, this, [this, filteringMenuButton]() { - previewWidget_->setFilteringMode(PreviewFilteringMode::Linear); - filteringMenuButton->setText(ui_->actionSetFilteringModeLinear->text()); - ui_->actionSetFilteringModeNearestNeighbor->setChecked(false); - ui_->actionSetFilteringModeLinear->setChecked(true); + connect(ui_->actionSetMSAAx8, &QAction::triggered, this, [this, msaaMenuButton, updateMsaaSelection]() { + previewWidget_->setMsaaSampleRate(PreviewMultiSampleRate::MSAA_8X); + updateMsaaSelection(ui_->actionSetMSAAx8); }); - ui_->toolBar->insertWidget(ui_->actionSelectFilteringMode, filteringMenuButton); + ui_->toolBar->insertWidget(ui_->actionSelectSizeMode, msaaMenuButton); } } diff --git a/gui/libRamsesWidgets/src/PreviewMainWindow.ui b/gui/libRamsesWidgets/src/PreviewMainWindow.ui index 18d6db1e..8f91e3ee 100644 --- a/gui/libRamsesWidgets/src/PreviewMainWindow.ui +++ b/gui/libRamsesWidgets/src/PreviewMainWindow.ui @@ -74,21 +74,37 @@ Original fit - - + + - Nearest filtering + MSAA x1 - Nearest filtering + MSAA x1 - + - Linear filtering + MSAA x2 - Linear filtering + MSAA x2 + + + + + MSAA x4 + + + MSAA x4 + + + + + MSAA x8 + + + MSAA x8 diff --git a/gui/libRamsesWidgets/src/RamsesPreviewWindow.cpp b/gui/libRamsesWidgets/src/RamsesPreviewWindow.cpp index d4ce2ac8..a04c1a97 100644 --- a/gui/libRamsesWidgets/src/RamsesPreviewWindow.cpp +++ b/gui/libRamsesWidgets/src/RamsesPreviewWindow.cpp @@ -106,12 +106,13 @@ RamsesPreviewWindow::State& RamsesPreviewWindow::nextState() { } void RamsesPreviewWindow::commit(bool forceUpdate) { - if (forceUpdate || !displayId_.isValid() || next_.viewportSize != current_.viewportSize || next_.sceneId != current_.sceneId || next_.targetSize != current_.targetSize || next_.filteringMode != current_.filteringMode) { + if (forceUpdate || !displayId_.isValid() || next_.viewportSize != current_.viewportSize || next_.sceneId != current_.sceneId || next_.targetSize != current_.targetSize || next_.sampleRate != current_.sampleRate) { // Unload current scenes reduceAndWaitSceneState(rendererBackend_, (displayId_.isValid()) ? ramses::RendererSceneState::Available : ramses::RendererSceneState::Unavailable, framebufferScene_, current_.sceneId); - if (next_.viewportSize.width() > 0 && next_.viewportSize.height() > 0) { + if (next_.viewportSize.width() > 0 && next_.viewportSize.height() > 0 || next_.sampleRate != current_.sampleRate) { auto& sceneControlAPI = *rendererBackend_.renderer().getSceneControlAPI(); + if (!displayId_.isValid()) { ramses::DisplayConfig displayConfig = {}; /// @todo maybe this setWindowRectangle is not needed? @@ -138,6 +139,7 @@ void RamsesPreviewWindow::commit(bool forceUpdate) { sceneControlAPI.setSceneMapping(framebufferScene_->getSceneId(), displayId_); } current_.viewportSize = next_.viewportSize; + current_.sampleRate = next_.sampleRate; if (next_.sceneId.isValid()) { /// @todo maybe we need to reset old scene mapping? @@ -150,9 +152,8 @@ void RamsesPreviewWindow::commit(bool forceUpdate) { // but an offscreen render buffer created with RamsesRenderer::createOffscreenBuffer to avoid creating the // offscreen render buffer in the scene we eventually export (and in fact for Ramses this offscreen render buffer is // the framebuffer - that we use it later on to blit it into our preview makes for the Ramses scene no difference). - const ramses::dataConsumerId_t dataConsumerId = framebufferScene_->setupFramebufferTexture(rendererBackend_, next_.targetSize, next_.filteringMode); - current_.filteringMode = next_.filteringMode; - offscreenBufferId_ = rendererBackend_.renderer().createOffscreenBuffer(displayId_, next_.targetSize.width(), next_.targetSize.height()); + const ramses::dataConsumerId_t dataConsumerId = framebufferScene_->setupFramebufferTexture(rendererBackend_, next_.targetSize, next_.sampleRate); + offscreenBufferId_ = rendererBackend_.renderer().createOffscreenBuffer(displayId_, next_.targetSize.width(), next_.targetSize.height(), next_.sampleRate); rendererBackend_.renderer().setDisplayBufferClearColor(displayId_, offscreenBufferId_, next_.backgroundColor.redF(), next_.backgroundColor.greenF(), next_.backgroundColor.blueF(), next_.backgroundColor.alphaF()); rendererBackend_.renderer().flush(); diff --git a/gui/libStyle/include/style/Colors.h b/gui/libStyle/include/style/Colors.h index 769259ee..57df21c8 100644 --- a/gui/libStyle/include/style/Colors.h +++ b/gui/libStyle/include/style/Colors.h @@ -31,7 +31,8 @@ enum class Colormap { warningColor, errorColorDark, dockTitleBackground, - externalReference + externalReference, + externalReferenceDisabled, }; class Colors { diff --git a/gui/libStyle/include/style/Icons.h b/gui/libStyle/include/style/Icons.h index 5f254362..6235c0ab 100644 --- a/gui/libStyle/include/style/Icons.h +++ b/gui/libStyle/include/style/Icons.h @@ -41,6 +41,7 @@ class Icons { const QIcon warning{":warningIcon"}; const QIcon error{":errorIcon"}; const QIcon typeNode{":typeNodeIcon"}; + const QIcon typeBlitPass{":typeBlitPassIcon"}; const QIcon typeCamera{":typeCameraIcon"}; const QIcon typeMesh{":typeMeshIcon"}; const QIcon typeMaterial{":typeMaterialIcon"}; diff --git a/gui/libStyle/src/Colors.cpp b/gui/libStyle/src/Colors.cpp index 0541f548..bc5a7356 100644 --- a/gui/libStyle/src/Colors.cpp +++ b/gui/libStyle/src/Colors.cpp @@ -27,7 +27,8 @@ Colors::Colors() noexcept { {Colormap::errorColor, QColor(180, 20, 20)}, {Colormap::errorColorDark, QColor(140, 0, 0)}, {Colormap::dockTitleBackground, QColor(0, 0, 0)}, - {Colormap::externalReference, QColor(170, 250, 70)}}; + {Colormap::externalReference, QColor(170, 250, 70)}, + {Colormap::externalReferenceDisabled, QColor(115, 195, 15)}}; for (const auto& [key, value] : colors_) { brushes_[key] = QBrush(value); diff --git a/resources/CMakeLists.txt b/resources/CMakeLists.txt index 27e6a8a2..882a5227 100644 --- a/resources/CMakeLists.txt +++ b/resources/CMakeLists.txt @@ -49,6 +49,8 @@ set(RESOURCE_FILES shaders/cubemap.vert shaders/default.frag shaders/default.vert + shaders/multisampler.frag + shaders/multisampler.vert shaders/simple_texture.frag shaders/simple_texture.vert example_scene.rca diff --git a/resources/scripts/array.lua b/resources/scripts/array.lua index 3d6bd49f..f5af2502 100644 --- a/resources/scripts/array.lua +++ b/resources/scripts/array.lua @@ -5,4 +5,9 @@ function interface(IN,OUT) end function run(IN,OUT) + OUT.float_array[1] = IN.float_array[1] + OUT.float_array[2] = IN.float_array[2] + OUT.float_array[3] = IN.float_array[3] + OUT.float_array[4] = IN.float_array[4] + OUT.float_array[5] = IN.float_array[5] end diff --git a/resources/scripts/types-scalar.lua b/resources/scripts/types-scalar.lua index 9aec22d6..c64260e0 100644 --- a/resources/scripts/types-scalar.lua +++ b/resources/scripts/types-scalar.lua @@ -35,22 +35,26 @@ end function run(IN,OUT) local v = IN.vector3f - -- OUT.ofloat = v[0] - OUT.ofloat = GLOBAL.test(IN.vector3f) + + OUT.ofloat = IN.float OUT.ointeger = 2*IN.integer OUT.ointeger64 = 2*IN.integer64 - OUT.ovector3f = {IN.float, 2*IN.float, 3.0} - - OUT.ovector4f = {v[1], IN.float, IN.vector3f[1], IN.vector3f[2]} - OUT.obool = not IN.bool - OUT.flag = IN.float > 0.5 - -- OUT.flag = IN.vector3f + OUT.ovector2f = {IN.float, 2 * IN.float} + OUT.ovector3f = {IN.float, 2 * IN.float, 3 * IN.float} + OUT.ovector4f = {IN.float, 2 * IN.float, 3 * IN.float, 4 * IN.float} + OUT.ovector2i = {IN.integer, 2 * IN.integer} + OUT.ovector3i = {IN.integer, 2 * IN.integer, 3 * IN.integer} + OUT.ovector4i = {IN.integer, 2 * IN.integer, 3 * IN.integer, 4 * IN.integer} + if IN.bool then OUT.foo = IN.float else OUT.bar = IN.float end + + OUT.obool = not IN.bool + OUT.flag = IN.float > 0.5 end diff --git a/resources/shaders/multisampler.frag b/resources/shaders/multisampler.frag new file mode 100644 index 00000000..8b378724 --- /dev/null +++ b/resources/shaders/multisampler.frag @@ -0,0 +1,20 @@ +#version 310 es + +precision highp float; + +uniform highp sampler2DMS textureSampler; +uniform highp int sampleCount; + +in lowp vec2 v_TextureCoordinate; +out vec4 fragColor; + +void main(void) +{ + vec4 color = vec4(0.0); + + for (int i = 0; i < sampleCount; i++) + color += texelFetch(textureSampler, ivec2(v_TextureCoordinate * vec2(textureSize(textureSampler))), i); + + color /= float(sampleCount); + fragColor = color; +} \ No newline at end of file diff --git a/resources/shaders/multisampler.vert b/resources/shaders/multisampler.vert new file mode 100644 index 00000000..acbe0880 --- /dev/null +++ b/resources/shaders/multisampler.vert @@ -0,0 +1,13 @@ +#version 310 es + +uniform mat4 uWorldViewProjectionMatrix; + +in vec3 a_Position; +in vec2 a_TextureCoordinate; +out vec2 v_TextureCoordinate; + +void main() +{ + gl_Position = uWorldViewProjectionMatrix * vec4(a_Position, 1.0); + v_TextureCoordinate = a_TextureCoordinate; +} \ No newline at end of file diff --git a/resources/shaders/uniform-array.frag b/resources/shaders/uniform-array.frag new file mode 100644 index 00000000..5cc80df7 --- /dev/null +++ b/resources/shaders/uniform-array.frag @@ -0,0 +1,22 @@ +#version 300 es + +precision mediump float; + +uniform float scalar; + +uniform int ivec[2]; +uniform float fvec[5]; + +uniform vec2 avec2[4]; +uniform vec3 avec3[5]; +uniform vec4 avec4[6]; + +uniform ivec2 aivec2[4]; +uniform ivec3 aivec3[5]; +uniform ivec4 aivec4[6]; + +out mediump vec4 fragColor; + +void main(){ + fragColor = vec4(scalar * fvec[0], scalar * fvec[1], scalar * fvec[2], scalar * fvec[3]); +} \ No newline at end of file diff --git a/resources/shaders/uniform-array.vert b/resources/shaders/uniform-array.vert new file mode 100644 index 00000000..cb84866c --- /dev/null +++ b/resources/shaders/uniform-array.vert @@ -0,0 +1,11 @@ +#version 300 es + +precision mediump float; + +in vec3 a_Position; + +uniform mat4 u_MVPMatrix; + +void main() { + gl_Position = u_MVPMatrix * vec4(a_Position, 1.0); +} \ No newline at end of file diff --git a/resources/shaders/uniform-scalar.frag b/resources/shaders/uniform-scalar.frag new file mode 100644 index 00000000..cc80675d --- /dev/null +++ b/resources/shaders/uniform-scalar.frag @@ -0,0 +1,20 @@ +#version 300 es + +precision mediump float; + +uniform int i; +uniform float f; + +uniform vec2 v2; +uniform vec3 v3; +uniform vec4 v4; + +uniform ivec2 iv2; +uniform ivec3 iv3; +uniform ivec4 iv4; + +out mediump vec4 fragColor; + +void main(){ + fragColor = vec4(f, f, f, 1.0); +} \ No newline at end of file diff --git a/resources/shaders/uniform-scalar.vert b/resources/shaders/uniform-scalar.vert new file mode 100644 index 00000000..cb84866c --- /dev/null +++ b/resources/shaders/uniform-scalar.vert @@ -0,0 +1,11 @@ +#version 300 es + +precision mediump float; + +in vec3 a_Position; + +uniform mat4 u_MVPMatrix; + +void main() { + gl_Position = u_MVPMatrix * vec4(a_Position, 1.0); +} \ No newline at end of file diff --git a/styles/_Default/icons/typeBlitPass.svg b/styles/_Default/icons/typeBlitPass.svg new file mode 100644 index 00000000..fcf3877a --- /dev/null +++ b/styles/_Default/icons/typeBlitPass.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/styles/icons.qrc b/styles/icons.qrc index da935c6b..dcc170f1 100644 --- a/styles/icons.qrc +++ b/styles/icons.qrc @@ -29,6 +29,7 @@ _Default/fonts/Roboto-Light.ttf _Default/icons/typeAnimation.svg _Default/icons/typeAnimationChannel.svg + _Default/icons/typeBlitPass.svg _Default/icons/typeNode.svg _Default/icons/typeCamera.svg _Default/icons/typeMesh.svg diff --git a/third_party/ramses-logic b/third_party/ramses-logic index 52e727a6..eef16edb 160000 --- a/third_party/ramses-logic +++ b/third_party/ramses-logic @@ -1 +1 @@ -Subproject commit 52e727a615d63f5bf00b97079ebec2e819a752ed +Subproject commit eef16edbcfe8e37fabeea4dae9ed954bc1f07e22