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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ cmake_minimum_required(VERSION 3.24.0)
cmake_policy(SET CMP0005 NEW)
cmake_policy(SET CMP0048 NEW) # manages project version

project(QtMeshEditor VERSION 2.29.1 LANGUAGES C CXX)
project(QtMeshEditor VERSION 2.29.0 LANGUAGES C CXX)
message(STATUS "Building QtMeshEditor version ${PROJECT_VERSION}")

set(QTMESHEDITOR_VERSION_STRING "\"${PROJECT_VERSION}\"")
Expand Down
3 changes: 2 additions & 1 deletion cfg/plugins_d.cfg.in
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
# Define plugin folder
PluginFolder=@PLUGIN_DIR@

# Define plugins
# Define plugins (keep aligned with plugins.cfg.in — Codec_STBI is required for common image formats)
Plugin=RenderSystem_GL@PLUGIN_DEBUG_POSTFIX@
Plugin=Plugin_OctreeSceneManager@PLUGIN_DEBUG_POSTFIX@
Plugin=Codec_STBI@PLUGIN_DEBUG_POSTFIX@
#Plugin=Plugin_CgProgramManager
7 changes: 5 additions & 2 deletions cfg/resources.cfg.in
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,12 @@


# Resource locations to be added to the default path
# Keep [General] in sync with resources_d.cfg.in so Release and Debug dev builds match install.
[General]
#FileSystem=media/models
#FileSystem=media/materials/programs
#FileSystem=media/materials/programs/GLSL
# GLSL/CG sources for materials that declare vertex_program/fragment_program (e.g. Example_BumpMapping*)
FileSystem=media/materials/programs/GLSL150
FileSystem=media/materials/programs/GLSL
FileSystem=media/materials/programs
FileSystem=media/materials/scripts
FileSystem=media/materials/textures
7 changes: 5 additions & 2 deletions cfg/resources_d.cfg.in
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,12 @@


# Resource locations to be added to the default path
# Keep [General] in sync with resources.cfg.in so Debug dev builds match Release/install.
[General]
#FileSystem=media/models
#FileSystem=media/materials/programs
#FileSystem=media/materials/programs/GLSL
# GLSL/CG sources for materials that declare vertex_program/fragment_program (e.g. Example_BumpMapping*)
FileSystem=media/materials/programs/GLSL150
FileSystem=media/materials/programs/GLSL
FileSystem=media/materials/programs
FileSystem=media/materials/scripts
FileSystem=media/materials/textures
2 changes: 2 additions & 0 deletions media/RTShaderLib/FFPLib_Texturing.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ THE SOFTWARE.
// see http://msdn.microsoft.com/en-us/library/bb206241.aspx
//-----------------------------------------------------------------------------

#include "RTSLib_Colour.glsl"

//-----------------------------------------------------------------------------
void FFP_TransformTexCoord(in mat4 m, in vec2 v, out vec2 vOut)
{
Expand Down
74 changes: 74 additions & 0 deletions qml/PreferencesDialog.qml
Original file line number Diff line number Diff line change
Expand Up @@ -294,10 +294,18 @@ Rectangle {

// --- Viewport Tab ---
Column {
id: viewportTabColumn
width: parent.width - 32
spacing: 12
visible: currentTab === 2

property int msaaSelection: 4
Component.onCompleted: {
var v = parseInt(readSetting("Viewport/fsaaSamples", 4))
if (v === 0 || v === 2 || v === 4 || v === 8)
msaaSelection = v
}
Comment on lines +303 to +307

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Normalize invalid persisted MSAA values, not just UI state.

Right now, unsupported stored values only fall back in the local UI selection. Consider also correcting persisted settings to avoid stale invalid config remaining in QSettings.

Suggested QML diff
 Component.onCompleted: {
     var v = parseInt(readSetting("Viewport/fsaaSamples", 4))
-    if (v === 0 || v === 2 || v === 4 || v === 8)
-        msaaSelection = v
+    if (v === 0 || v === 2 || v === 4 || v === 8) {
+        msaaSelection = v
+    } else {
+        msaaSelection = 4
+        writeSetting("Viewport/fsaaSamples", 4)
+    }
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
Component.onCompleted: {
var v = parseInt(readSetting("Viewport/fsaaSamples", 4))
if (v === 0 || v === 2 || v === 4 || v === 8)
msaaSelection = v
}
Component.onCompleted: {
var v = parseInt(readSetting("Viewport/fsaaSamples", 4))
if (v === 0 || v === 2 || v === 4 || v === 8) {
msaaSelection = v
} else {
msaaSelection = 4
writeSetting("Viewport/fsaaSamples", 4)
}
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@qml/PreferencesDialog.qml` around lines 303 - 307, The Component.onCompleted
block currently parses the persisted "Viewport/fsaaSamples" and only updates the
UI variable msaaSelection when the value is valid, but it leaves invalid values
in settings; modify the Component.onCompleted handler (the code that calls
readSetting and sets msaaSelection) to validate parsed v (acceptable set:
0,2,4,8) and if v is invalid, reset the persisted setting via the same settings
API (e.g., writeSetting or setSetting) to a safe default (4) and then set
msaaSelection to that default so both the UI state and QSettings are normalized;
reference the existing readSetting call, msaaSelection, and
Component.onCompleted when locating where to add the write/reset logic.


// Grid visibility (themed checkbox)
Row {
spacing: 6
Expand All @@ -318,6 +326,72 @@ Rectangle {
Text { text: "Show Grid"; font.pixelSize: 12; color: textColor; anchors.verticalCenter: parent.verticalCenter }
}

// MSAA (applied immediately; render windows are recreated)
Column {
width: parent.width
spacing: 4

Text {
text: "Anti-aliasing (MSAA)"
font.pixelSize: 12
font.bold: true
color: textColor
}

Flow {
spacing: 3
width: parent.width

Repeater {
model: [
{ "label": "Off", "value": 0 },
{ "label": "2×", "value": 2 },
{ "label": "4×", "value": 4 },
{ "label": "8×", "value": 8 }
]

Rectangle {
width: Math.max(44, (parent.width - 9) / 4)
height: 24
radius: 3
color: modelData.value === viewportTabColumn.msaaSelection ? highlightColor
: msaaBtnMa.containsMouse ? Qt.lighter(panelColor, 1.5)
: Qt.darker(panelColor, 1.1)
border.color: borderColor
border.width: 1
Behavior on color { ColorAnimation { duration: 50 } }

Text {
anchors.centerIn: parent
text: modelData.label
color: textColor
font.pixelSize: 11
}

MouseArea {
id: msaaBtnMa
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
viewportTabColumn.msaaSelection = modelData.value
writeSetting("Viewport/fsaaSamples", modelData.value)
}
}
}
}
}

Text {
text: "Higher values smooth edges but cost more GPU time. Off may look jagged on high-DPI displays."
font.pixelSize: 11
font.italic: true
color: dimTextColor
wrapMode: Text.WordWrap
width: parent.width
}
}

// Camera speed
Column {
width: parent.width
Expand Down
3 changes: 2 additions & 1 deletion qtmesh.example.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ scan:
- "**/*.fbx"
- "**/*.glb"
- "**/*.gltf"
- "**/*.vrm"
- "**/*.obj"
exclude:
- "**/third_party/**"
Expand All @@ -32,7 +33,7 @@ scan:

rules:
# Format restrictions
allowed_formats: [fbx, glb, gltf, obj]
allowed_formats: [fbx, glb, gltf, vrm, obj]
forbidden_extensions: [dae, 3ds]

# Size & complexity limits
Expand Down
1 change: 1 addition & 0 deletions qtmesh.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ scan:
- "**/*.fbx"
- "**/*.glb"
- "**/*.gltf"
- "**/*.vrm"
- "**/*.obj"
- "**/*.mesh"
exclude:
Expand Down
70 changes: 70 additions & 0 deletions src/AppSettingsKeys.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/*
-----------------------------------------------------------------------------------
A QtMeshEditor file

Copyright (c) Fernando Tonon (https://github.com/fernandotonon)

The MIT License

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software
without restriction, including without limitation the rights to use, copy,
modify, merge, publish, distribute, sublicense, and/or sell copies of the
Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING WITHOUT LIMITATION THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
-----------------------------------------------------------------------------------
*/

#ifndef APP_SETTINGS_KEYS_H
#define APP_SETTINGS_KEYS_H

#include <QString>

namespace AppSettingsKeys
{

/** @brief Sentry on/off in Preferences (must match QML and SentryReporter). */
inline const QString& sentryEnabled()
{
static const QString k(QStringLiteral("Sentry/enabled"));

Check warning on line 41 in src/AppSettingsKeys.h

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Replace the redundant type with "auto".

See more on https://sonarcloud.io/project/issues?id=fernandotonon_QtMeshEditor&issues=AZ3CVVH5ksIvWbIG4rZE&open=AZ3CVVH5ksIvWbIG4rZE&pullRequest=309
return k;
}

/** @brief Telemetry on/off. */
inline const QString& telemetryEnabled()
{
static const QString k(QStringLiteral("Telemetry/enabled"));

Check warning on line 48 in src/AppSettingsKeys.h

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Replace the redundant type with "auto".

See more on https://sonarcloud.io/project/issues?id=fernandotonon_QtMeshEditor&issues=AZ3CVVH5ksIvWbIG4rZF&open=AZ3CVVH5ksIvWbIG4rZF&pullRequest=309
return k;
}

/** @brief Light/dark/system from Preferences. */
inline const QString& appearanceTheme()
{
static const QString k(QStringLiteral("Appearance/theme"));

Check warning on line 55 in src/AppSettingsKeys.h

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Replace the redundant type with "auto".

See more on https://sonarcloud.io/project/issues?id=fernandotonon_QtMeshEditor&issues=AZ3CVVH5ksIvWbIG4rZG&open=AZ3CVVH5ksIvWbIG4rZG&pullRequest=309
return k;
}

/**
* @brief Legacy / alternate key for theme (some paths write "palette").
*/
inline const QString& palette()
{
static const QString k(QStringLiteral("palette"));

Check warning on line 64 in src/AppSettingsKeys.h

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Replace the redundant type with "auto".

See more on https://sonarcloud.io/project/issues?id=fernandotonon_QtMeshEditor&issues=AZ3CVVH5ksIvWbIG4rZH&open=AZ3CVVH5ksIvWbIG4rZH&pullRequest=309
return k;
}

} // namespace AppSettingsKeys

#endif // APP_SETTINGS_KEYS_H
2 changes: 1 addition & 1 deletion src/AssetBrowserController.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
AssetBrowserController* AssetBrowserController::m_pSingleton = nullptr;

const QStringList AssetBrowserController::s_meshExtensions = {
"fbx", "gltf", "glb", "gltf2", "obj", "dae", "stl", "mesh", "3ds", "blend", "ply",
"fbx", "gltf", "glb", "gltf2", "vrm", "obj", "dae", "stl", "mesh", "3ds", "blend", "ply",
"x", "x3d", "lwo", "lws", "ac", "ms3d", "cob", "scn", "bvh", "irrmesh", "irr",
"mdl", "md2", "md3", "md5mesh", "smd", "ogex", "b3d", "q3d", "nff", "off",
"raw", "ter", "hmp", "assbin", "mesh.xml"
Expand Down
1 change: 1 addition & 0 deletions src/AssetBrowserController_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,7 @@ TEST_F(AssetBrowserControllerTests, FileTypeClassification) {
auto* abc = AssetBrowserController::instance();
EXPECT_EQ(abc->fileTypeForPath("/foo/bar.fbx"), "mesh");
EXPECT_EQ(abc->fileTypeForPath("/foo/bar.gltf"), "mesh");
EXPECT_EQ(abc->fileTypeForPath("/foo/bar.vrm"), "mesh");
EXPECT_EQ(abc->fileTypeForPath("/foo/bar.obj"), "mesh");
EXPECT_EQ(abc->fileTypeForPath("/foo/bar.png"), "texture");
EXPECT_EQ(abc->fileTypeForPath("/foo/bar.jpg"), "texture");
Expand Down
1 change: 1 addition & 0 deletions src/CLIPipeline.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -336,6 +336,7 @@ QString CLIPipeline::formatForExtension(const QString& path)
{".glb2", "glTF 2.0 Binary (*.glb2)"},
{".gltf", "glTF 2.0 (*.gltf)"},
{".gltf2", "glTF 2.0 (*.gltf2)"},
{".vrm", "VRM / glTF 2.0 (*.vrm)"},
{".dae", "Collada (*.dae)"},
{".obj", "OBJ (*.obj)"},
{".stl", "STL (*.stl)"},
Expand Down
1 change: 1 addition & 0 deletions src/CLIPipeline_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -497,6 +497,7 @@ TEST(CLIPipelineFormatForExtension, PathWithDirectories)
EXPECT_EQ(CLIPipeline::formatForExtension("/tmp/dir/model.glb"), "glTF 2.0 Binary (*.glb)");
EXPECT_EQ(CLIPipeline::formatForExtension("/tmp/dir/model.gltf"), "glTF 2.0 (*.gltf)");
EXPECT_EQ(CLIPipeline::formatForExtension("C:\\dir\\model.gltf2"), "glTF 2.0 (*.gltf2)");
EXPECT_EQ(CLIPipeline::formatForExtension("model.vrm"), "VRM / glTF 2.0 (*.vrm)");
}

// --- printUsage / printVersion smoke tests ---
Expand Down
12 changes: 12 additions & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ AnimationControlController.h
GlobalDefinitions.h
Euler.h
about.h
AppSettingsKeys.h
mainwindow.h
Manager.h
material.h
Expand All @@ -100,6 +101,7 @@ TransformOperator.h
PrimitivesWidget.h
PrimitiveObject.h
ViewportGrid.h
ViewportSettingsKeys.h
AnimationWidget.h
SelectionSet.h
SelectionBoxObject.h
Expand Down Expand Up @@ -354,6 +356,16 @@ else()
)
endif()

# Dev runs expect `media/` beside the executable: resources.cfg uses
# media/materials/* and RTShaderHelper adds media/RTShaderLib + media/Main
# (OgreUnifiedShader.h). Without this copy the viewport can stay black.
add_custom_command(TARGET ${CMAKE_PROJECT_NAME} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory
${CMAKE_SOURCE_DIR}/media
$<TARGET_FILE_DIR:${CMAKE_PROJECT_NAME}>/media
COMMENT "Copying media/ next to QtMeshEditor (materials + RTSS)"
)

##############################################################
# Linking the executable
##############################################################
Expand Down
2 changes: 1 addition & 1 deletion src/MCPServer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3501,7 +3501,7 @@ QJsonArray MCPServer::buildToolsList()
// open_scene
{
QJsonObject props;
props["file_path"] = QJsonObject{{"type", "string"}, {"description", "Absolute path to a scene file to open (*.scene.glb, *.scene.gltf, *.glb, *.gltf)"}};
props["file_path"] = QJsonObject{{"type", "string"}, {"description", "Absolute path to a scene file to open (*.scene.glb, *.scene.gltf, *.glb, *.gltf, *.vrm)"}};
appendTool(
"open_scene",
"Open a scene file, replacing the current scene. Loads all meshes with their transforms, materials, skeletons, and animations. "
Expand Down
2 changes: 1 addition & 1 deletion src/Manager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ Manager* Manager:: m_pSingleton = nullptr;

QString Manager::mValidFileExtention = ".mesh .dae .blend .3ds .ase .obj .ifc .xgl .zgl .ply .dxf .lwo "\
".lws .lxo .stl .x .ac .ms3d .cob .scn .bvh .csm .xml .irrmesh .irr .mdl .md2 .md3 "\
".pk3 .mdc .md5 .txt .smd .vta .m3 .3d .b3d .q3d .q3s .nff .nff .off .raw .ter .mdl .hmp .ndo .fbx .glb .gltf";
".pk3 .mdc .md5 .txt .smd .vta .m3 .3d .b3d .q3d .q3s .nff .nff .off .raw .ter .mdl .hmp .ndo .fbx .glb .gltf .vrm";

////////////////////////////////////////
/// Static Member to build & destroy
Expand Down
4 changes: 4 additions & 0 deletions src/Manager_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,9 @@ TEST_F(ManagerHeadlessTest, IsValidFileExtention)
QString glbFile = "model.glb";
EXPECT_TRUE(mgr->isValidFileExtention(glbFile));

QString vrmFile = "avatar.vrm";
EXPECT_TRUE(mgr->isValidFileExtention(vrmFile));

QString stlFile = "print.stl";
EXPECT_TRUE(mgr->isValidFileExtention(stlFile));

Expand All @@ -282,6 +285,7 @@ TEST_F(ManagerHeadlessTest, IsValidFileExtention)
EXPECT_FALSE(validExts.isEmpty());
EXPECT_TRUE(validExts.contains(".mesh"));
EXPECT_TRUE(validExts.contains(".fbx"));
EXPECT_TRUE(validExts.contains(".vrm"));
}

TEST_F(ManagerHeadlessTest, CreateEmptyScene)
Expand Down
5 changes: 5 additions & 0 deletions src/MeshImporterExporter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1027,6 +1027,11 @@ void MeshImporterExporter::importer(const QStringList &_uriList, unsigned int ad
if (en->getMesh() && en->getMesh()->getSkeleton())
AnimationMerger::registerSkeletonUpAxis(
en->getMesh()->getSkeleton()->getName(), importer.getSceneUpAxis());

// Same as .mesh/.xml path: ensure tangents exist and RTSS normal maps are
// applied after the Entity exists (import-time material setup can run before
// mesh data is finalized; Assimp sometimes omits tangents on awkward assets).
applyNormalMapsToEntity(en);
}

sn->setPosition(0,0,0);
Expand Down
14 changes: 2 additions & 12 deletions src/MeshTransform.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,20 +31,10 @@ THE SOFTWARE.
#include <limits>
#include "Manager.h"
#include "SkeletonTransform.h"
#include "TransformMath.h"

namespace {

Ogre::Quaternion buildRotationQuat(const Ogre::Vector3 &rotate)
{
if(rotate.x != 0)
return {Ogre::Degree(rotate.x), Ogre::Vector3::UNIT_Y};
if(rotate.y != 0)
return {Ogre::Degree(rotate.y), Ogre::Vector3::UNIT_Z};
if(rotate.z != 0)
return {Ogre::Degree(rotate.z), Ogre::Vector3::UNIT_X};
return Ogre::Quaternion::IDENTITY;
}

// Iterates all unique vertex data blocks in a mesh, calling transformFn(pos) for each
// vertex position. Writes back the transformed position and updates mesh bounds.
template<typename TransformFn>
Expand Down Expand Up @@ -143,7 +133,7 @@ void MeshTransform::translateMesh(const Ogre::Entity *_ent, const Ogre::Vector3

void MeshTransform::rotateMesh(const Ogre::Entity *_ent, const Ogre::Vector3 &_rotate)
{
rotateMesh(_ent, buildRotationQuat(_rotate));
rotateMesh(_ent, TransformMath::buildRotationQuat(_rotate));
}

void MeshTransform::rotateMesh(const Ogre::Entity *_ent, const Ogre::Quaternion &_quat)
Expand Down
Loading
Loading