Skip to content

quads chunk 3: importer quad detection (option A) - #329

Closed
fernandotonon wants to merge 1 commit into
feat/quads-2-gpu-uploadfrom
feat/quads-3-importer
Closed

quads chunk 3: importer quad detection (option A)#329
fernandotonon wants to merge 1 commit into
feat/quads-2-gpu-uploadfrom
feat/quads-3-importer

Conversation

@fernandotonon

Copy link
Copy Markdown
Owner

Summary

Chunk 3 of the quad migration (#326). Builds on chunks 1 (#327) and 2 (#328).

Adds the n-gon-aware re-import path agreed on the issue (option A): a new EditableMesh::loadFromAssimpFile(path) reads the original source file directly with Assimp's triangulation flag DISABLED, so source quads survive into EditableSubMesh::faces. The existing AssimpToOgreImporter pipeline is untouched.

Changes

  • MeshImporterExporter::importer caches the source file path on the imported Ogre::Mesh via UserObjectBindings("qtme.source_path"). Available everywhere the mesh is.
  • EditableMesh::loadFromAssimpFile(path) spins up an independent Assimp::Importer, drops aiProcess_Triangulate, walks aiMesh::mFaces and stores polygons (3/4/N) directly into EditableFace. Triangulation mirror is built afterwards via triangulateFaces() to keep the chunk-1 invariant. Skeleton/animation/material/tangent passes deliberately skipped — Edit Mode only needs geometry.

Deliberately NOT done in this chunk

EditModeController::enterEditMode is NOT yet wired to use this path. Doing so safely requires modification-tracking (so re-importing doesn't blow away prior edits). That's a follow-up chunk; this one keeps the risk surface bounded by adding the new code path without changing any existing flows.

Net user-visible behavior: zero change. Triangle-only assets behave identically; quad assets carry their polygon structure into the editor when (and only when) someone explicitly calls loadFromAssimpFile.

Tests (+6 standalone)

  • Empty path / missing file rejected.
  • OBJ with a single quad face → 1 EditableFace (4 verts) + 2 fan triangles.
  • Triangle-only OBJ leaves faces empty (chunk-1 invariant).
  • Mixed tri+quad OBJ produces both face types.
  • Non-empty EditableMesh is replaced on subsequent loads.

OBJ files written via QFile to QDir::tempPath() and removed after each test — no bundled binary assets.

Test plan

  • 197 standalone tests green (was 191; +6 new).
  • App builds clean.
  • Linux/macOS/Windows CI.

Targeting feat/quads-2-gpu-upload

Stacked on chunk 2 (#328). When chunks 1+2 land on feat/quads, this PR's base will auto-update.

Towards #326.

🤖 Generated with Claude Code

Adds the n-gon-aware re-import path discussed on #326. The existing
AssimpToOgreImporter pipeline is untouched; this chunk only:

  1. Caches the source file path on the imported Ogre::Mesh via
     UserObjectBindings("qtme.source_path"). MeshImporterExporter
     attaches it after every Assimp-backed import.
  2. Adds EditableMesh::loadFromAssimpFile(path) which spins up an
     independent Assimp::Importer with aiProcess_Triangulate
     deliberately OFF, so source quads survive into aiMesh::mFaces and
     get recorded as EditableFace entries. Vertex attributes
     (positions/normals/UVs/colors/bone weights) are read out of the
     same scene; skeleton, animation, material, and tangent processing
     are skipped — Edit Mode operates on geometry only.

Notes & deferred work:
  - Not yet wired into EditModeController::enterEditMode. Doing so
    safely needs a "user has modified this mesh" flag so re-importing
    doesn't discard prior edits — that's a follow-up chunk.
  - Re-import cost (~10–100ms typical, more on huge FBX) is acceptable
    as a one-time Tab-into-Edit-Mode cost.

Tests (+6, standalone):
  - empty path / missing file rejected
  - OBJ quad → 1 EditableFace (4 verts), 2 fan triangles
  - triangle-only OBJ leaves `faces` empty (chunk-1 invariant)
  - mixed tri+quad OBJ produces both face types
  - non-empty mesh is replaced

197 standalone tests green (was 191; +6).

Towards #326.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Apr 28, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: f0d83e40-3a6d-4c7e-b1da-8f343f899baa

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/quads-3-importer

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a8865acb6a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/EditableMesh.cpp
Comment on lines +160 to +164
aiProcess_JoinIdenticalVertices |
aiProcess_GenSmoothNormals |
aiProcess_ValidateDataStructure |
aiProcess_LimitBoneWeights |
aiProcess_GlobalScale;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Reuse import coordinate-conversion flags for Assimp reload

loadFromAssimpFile() uses a reduced flag set that omits the coordinate-system conversions used by the normal import path (notably left-handed conversion, and it also never applies the Z-up→Y-up bake done in MeshProcessor), so the editable vertex positions can be in a different basis than the live Ogre mesh. For non-.x assets and Z-up files, calling this path and then committing edits can write mirrored/rotated geometry back to the entity.

Useful? React with 👍 / 👎.

Comment thread src/EditableMesh.cpp
const aiVertexWeight& vw = bone->mWeights[w];
if (vw.mVertexId >= sub.vertices.size()) continue;
EditableBoneAssignment eba;
eba.boneIndex = static_cast<unsigned short>(b);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Resolve bone indices from skeleton names before storing weights

Bone assignments are recorded with boneIndex = b, which is only the mesh-local aiMesh::mBones array index, but downstream writeback (resizeEntityBuffers) treats EditableBoneAssignment::boneIndex as an Ogre skeleton handle. On skinned meshes where Assimp bone order differs from Ogre bone handles, this remaps vertex weights to the wrong bones and breaks deformation after topology updates.

Useful? React with 👍 / 👎.

@sonarqubecloud

Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant