Skip to content

feat(PS1/RSD): textured RSD import/export round-trip#496

Open
fernandotonon wants to merge 4 commits into
masterfrom
feat/rsd-texture-support
Open

feat(PS1/RSD): textured RSD import/export round-trip#496
fernandotonon wants to merge 4 commits into
masterfrom
feat/rsd-texture-support

Conversation

@fernandotonon
Copy link
Copy Markdown
Owner

@fernandotonon fernandotonon commented May 12, 2026

Summary

Adds full texture support to the Sony Psy-Q RSD importer and exporter so assets produced by the PlayStation-RSD Blender exporter (e.g. Wood.jpg referenced by Example Project.rsd) round-trip with materials and UVs preserved.

Highlights

  • PS1MAT: MatEntry now captures the full Psy-Q material description (shading char, type char, texture index, per-corner UVs, per-vertex colours). Parser/writer cover all material types: C (flat solid), G (smooth), T (textured), D (textured + flat colour), H (textured + smooth).
  • PS1PLY: New importPsyqPlyWithFaceMaterials() builds one submesh per texture bucket (plus one for untextured faces) with per-corner UVs and vertex colours; submeshes are named PLY/<mesh>_texN / PLY/<mesh>_solid so the RSD importer can rebind Ogre textures by parsing the suffix. exportPsyqPlyFromEntity() gained an outFaceTextures sink that captures per-face textured flag + source submesh + corner UVs, and textured submeshes skip heuristic quad-merge to keep UVs intact.
  • MeshImporterExporter (RSD path): Loads non-TIM textures (JPG/PNG/BMP/...) via Ogre codecs with a QImage fallback, routes parsed MAT entries through the new textured PLY path when texture slots are populated, and binds the right Ogre texture resource to each _texN submesh after entity creation. Export synthesises T/C MAT entries, copies referenced images next to the .rsd, and writes NTEX + TEX[] descriptor entries.
  • test_main.cpp: Forces QT_QPA_PLATFORM=xcb on Linux so Ogre's externalWindowHandle path gets a valid X11 XID under Xvfb / Wayland sessions; without this, UnitTests was failing locally with tryInitOgre() failed.

Manual verification

Using the user-supplied PlayStation-RSD-exporter-for-Blender-3.2.1/Example Project.rsd:

```
$ qtmesh info "Example Project.rsd" --json
{
"materials": ["PLY/Example Project_rsd_ply_solid", "PLY/Example Project_rsd_ply_tex0"],
"submeshes": 2,
"textures": ["Wood.jpg"],
"triangles": 110,
"vertices": 160,
...
}

$ qtmesh convert "Example Project.rsd" -o "RoundTrip.rsd"

Writes RoundTrip.{rsd,ply,mat} + Wood.png next to it.

```

Tests added

  • PS1MAT.ParseFlatColorEntries / ParseSmoothColorQuadEntries / ParseTexturedQuadEntry_HType / ParseTexturedTriEntry_TType / WriteAndRoundTrip_MixedEntries / RejectsMissingHeader — full parser/writer coverage for the new MAT types.
  • PS1RSD.ParseBlenderExporterTextureLayout — RSD parser accepts NTEX + external image references (Wood.jpg).
  • PS1PLY.ImportWithFaceMaterials_SplitsTexturedAndSolidIntoSubmeshes — verifies multi-submesh layout with UV element only on textured submeshes.
  • PS1PLY.TexturedPlyRoundTrip_ExportRecoversPerFaceUvAndTextureFlag — end-to-end: import textured+solid mesh, export, confirm per-face UV envelope and submesh routing survive.

Full focused-suite run (PS1*:MeshImporterExporter*:ScanEngine*): 164 / 164 passing under xvfb-run.

Test plan

  • CI Linux UnitTests pass (all PS1 tests + MeshImporterExporter regression).
  • CI macOS + Windows build succeed (no platform-specific includes added; xcb override is gated on Q_OS_LINUX).
  • qtmesh info + qtmesh convert on Example Project.rsd round-trip cleanly (manual smoke).
  • SonarCloud quality gate passes.

Made with Cursor

Summary by CodeRabbit

  • New Features
    • Full PlayStation RSD import/export with textured PLY+MAT: per-face UVs, per-vertex colors, textured submesh handling, and automatic PNG texture sidecars for round-trip fidelity.
  • Bug Fixes
    • Improved render-window handle behavior on Linux to avoid display failures.
  • Tests
    • Added unit and integration tests for MAT parsing, PLY textured import/export, and RSD texture layouts.

Review Change Stack

Adds full texture support to the Sony Psy-Q RSD importer and exporter so
assets produced by the PlayStation-RSD Blender exporter (e.g. Wood.jpg
referenced by Example Project.rsd) round-trip with materials and UVs
preserved.

PS1MAT
- Promote MatEntry from a single QColor to a full material description:
  shadingChar (F/S/G), typeChar (C/G/T/D/H), textured flag, textureIndex,
  per-corner UVs (PS1 pixel coordinates), and 1-4 per-vertex colours.
- Rewrite parseMatFile and writeMatFile to handle every Psy-Q material
  type with its correct payload (single colour C, smooth-shaded G, plain
  texture T, textured-flat-colour D, textured-smooth-colour H), tolerating
  the slight count variants tris/quads use in the wild.

PS1PLY
- Add importPsyqPlyWithFaceMaterials(): one submesh per textureIndex
  bucket plus one for untextured faces, each with per-corner positions,
  normals, UVs and vertex colours. Submesh materials are named
  PLY/<mesh>_texN / PLY/<mesh>_solid so the RSD importer can rebind
  Ogre textures.
- Extend exportPsyqPlyFromEntity() with an optional outFaceTextures sink
  that captures per-face textured flag, source submesh index, corner count
  and UVs. Textured submeshes skip the heuristic quad-merge so UVs stay
  intact, while untextured paths keep existing behaviour.

MeshImporterExporter
- Load non-TIM RSD textures (JPG/PNG/BMP/...) via Ogre codecs with a
  QImage fallback and stash dimensions so UVs can be normalised on import.
- When the parsed MAT exposes textured entries, build PS1PLY::FaceMaterial
  per face and route imports through the new textured PLY path; after the
  entity is created, bind the right Ogre texture to each _texN submesh.
- On RSD export, synthesise MAT entries (T for textured faces, C for
  untextured), copy referenced texture images next to the .rsd, and emit
  matching NTEX / TEX[] descriptor entries.

Tests
- New PS1MAT_test.cpp covering C/G/T/H parse paths plus a mixed
  write-and-reread round trip.
- PS1RSD: new ParseBlenderExporterTextureLayout case for the NTEX +
  external JPG layout produced by the Blender exporter.
- PS1PLY: new Ogre-backed tests
  (ImportWithFaceMaterials_SplitsTexturedAndSolidIntoSubmeshes,
  TexturedPlyRoundTrip_ExportRecoversPerFaceUvAndTextureFlag) exercising
  the textured import + export sinks end-to-end.
- test_main.cpp: force QT_QPA_PLATFORM=xcb on Linux so Ogre's
  externalWindowHandle path gets a valid X11 XID under Xvfb / Wayland
  sessions — without this, UnitTests was failing locally with
  "tryInitOgre() failed".

Manual verification with the user-supplied
'PlayStation-RSD-exporter-for-Blender-3.2.1/Example Project.rsd': qtmesh
info reports 2 submeshes (solid + tex0) with Wood.jpg in textures; qtmesh
convert round-trips the file and emits RoundTrip.rsd referencing
RoundTrip.{ply,mat} and a copied Wood.png.

Co-authored-by: Cursor <cursoragent@cursor.com>
@chatgpt-codex-connector
Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, you can upgrade your account or add credits to your account and enable them for code reviews in your settings.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 12, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 5fccc057-f122-4e6c-a357-30086271fd0b

📥 Commits

Reviewing files that changed from the base of the PR and between 4d6fade and 03961d2.

📒 Files selected for processing (1)
  • src/MeshImporterExporter.cpp
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/MeshImporterExporter.cpp

📝 Walkthrough

Walkthrough

This PR preserves per-face UVs and texture bindings across MAT/PLY/RSD: MAT entries gain typed fields (UVs, textureIndex, vertex colors), PLY gains textured import/export using per-face descriptors, RSD preloads and binds textures during import and emits synthesized MAT + PNG sidecars on export.

Changes

Textured Material Support for PS1 PLY/MAT/RSD Import/Export

Layer / File(s) Summary
MAT Format Data Model and Codec
src/PS1/PS1MAT.h, src/PS1/PS1MAT.cpp, src/PS1/PS1MAT_test.cpp
MatEntry expanded to include shading/type chars, textured flag/index, per-entry UVs and per-vertex colors; parser rewritten with type-aware decoding (C/G/T/D/H) and writer emits padded UV/color blocks. Unit tests added for parsing, writing, and round-trip.
PLY Textured Import/Export Implementation
src/PS1/PS1PLY.h, src/PS1/PS1PLY.cpp, src/PS1/PS1PLY_test.cpp
Adds FaceMaterial and ExportFaceTexture types, importPsyqPlyWithFaceMaterials to build textured meshes from per-face UVs/colors, and extends exportPsyqPlyFromEntity to emit per-face UV/texture metadata; exporter disables quad-merge when UVs present. Tests validate textured vs solid submeshes and UV preservation.
RSD Texture Loading and Import/Export Integration
src/MeshImporterExporter.cpp, src/PS1/PS1RSD_test.cpp
Adds scoped resource naming and loadExternalTextureForRsd (Ogre codec then QImage fallback), preloads rsd.textures into a slot table (resource + dims), preserves full MAT entries, routes import to textured PLY path when valid slots found, patches materials by _texN suffix, and exports synthesized .mat plus PNG sidecars and ntex/textures. Test added for non-.tim RSD textures.
Qt Platform Compatibility for Linux/Ogre
src/test_main.cpp
Sets QT_QPA_PLATFORM=xcb on Linux before QApplication init to avoid Wayland/Xvfb window-handle issues with Ogre.

Sequence Diagrams

sequenceDiagram
  participant User
  participant MeshImporter as MeshImporterExporter
  participant TexturePreloader as TexturePreloader
  participant SlotTable as SlotTable(resource+dims)
  participant PLYImporter as importPsyqPlyWithFaceMaterials
  participant MaterialPatcher as MaterialPatcher
  User->>MeshImporter: open .rsd (descriptor + .mat/.ply)
  MeshImporter->>TexturePreloader: read rsd.textures list
  TexturePreloader->>SlotTable: load textures (Ogre codec -> QImage fallback) and record dims
  MeshImporter->>PLYImporter: provide per-face FaceMaterial[] (when MAT entries reference slots)
  SlotTable-->>PLYImporter: supply texture dimensions for UV normalization
  PLYImporter-->>MeshImporter: produce Ogre mesh (textured/untextured submeshes)
  MeshImporter->>MaterialPatcher: bind preloaded textures to *_texN materials and recompile
  MeshImporter->>User: return imported Ogre::Entity
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Poem

🐇 I hopped through MATs and traced each UV,
I stitched PLY corners where textures should be.
RSD gave me sprites; I loaded them right,
PNG sidecars bundled, materials bright.
Rabbit cheers—render, hop, and delight!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 38.64% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title directly and specifically describes the main feature added: textured RSD import/export round-trip support with full texture handling.
Description check ✅ Passed The description includes a clear Summary, detailed Technical Details with Features section covering all major changes (PS1MAT, PS1PLY, MeshImporterExporter, test_main.cpp), comprehensive test cases, manual verification with examples, and a test plan.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/rsd-texture-support

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.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🧹 Nitpick comments (1)
src/MeshImporterExporter.cpp (1)

1538-1551: ⚡ Quick win

Avoid re-decoding the texture file just to capture its dimensions.

loadExternalTextureForRsd already decoded the image and uploaded it to Ogre's TextureManager. Re-opening the same file via QImage qprobe(texPath) re-runs the codec for the second time per texture (and a third time, since the helper itself can also pay a QImage decode internally on the fallback path). Query the just-loaded TexturePtr instead — it has the dimensions on hand:

♻️ Suggested change
-                    } else {
-                        // Non-TIM raster format (PNG/JPG/BMP/TGA…)
-                        QString extErr;
-                        if (loadExternalTextureForRsd(texPath, ogreName, &extErr)) {
-                            const QImage qprobe(texPath);
-                            rsdTexSlots[ti].resourceName = resName;
-                            rsdTexSlots[ti].width = qprobe.isNull() ? 0 : qprobe.width();
-                            rsdTexSlots[ti].height = qprobe.isNull() ? 0 : qprobe.height();
-                        } else {
+                    } else {
+                        // Non-TIM raster format (PNG/JPG/BMP/TGA…)
+                        QString extErr;
+                        if (loadExternalTextureForRsd(texPath, ogreName, &extErr)) {
+                            rsdTexSlots[ti].resourceName = resName;
+                            if (auto tex = Ogre::TextureManager::getSingleton().getByName(
+                                    ogreName, Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME)) {
+                                rsdTexSlots[ti].width = static_cast<int>(tex->getWidth());
+                                rsdTexSlots[ti].height = static_cast<int>(tex->getHeight());
+                            }
+                        } else {

Alternatively, give loadExternalTextureForRsd an (int* outW, int* outH) out-param that returns the dimensions from the Ogre::Image it already populated, so the dimensions don't depend on a second TextureManager::getByName() round-trip either.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/MeshImporterExporter.cpp` around lines 1538 - 1551, The code re-opens the
image file with QImage after calling loadExternalTextureForRsd, causing a
redundant decode; instead retrieve the just-loaded Ogre::Texture (via
Ogre::TextureManager::getByName or by returning it from
loadExternalTextureForRsd) and read its width/height to populate
rsdTexSlots[ti].width and .height (or modify loadExternalTextureForRsd to accept
outW/outH or return image/texture and use those values), and remove the QImage
qprobe(texPath) re-decode and stringly-constructed size logic.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/MeshImporterExporter.cpp`:
- Around line 2225-2236: The QImage is being constructed assuming img data is
PF_BYTE_RGBA which can cause OOB reads or channel/alpha misordering; before
building QImage in MeshImporterExporter.cpp (around tex->convertToImage(img,
true) and the QImage(...) call) check img.getFormat() and if it is not
Ogre::PixelFormat::PF_BYTE_RGBA perform a bulk conversion to PF_BYTE_RGBA (using
Ogre::PixelUtil::bulkPixelConversion into a temporary Ogre::Image or buffer) so
the buffer layout is guaranteed, then use that converted data to create the
QImage and save the PNG; reference tex->convertToImage, Ogre::Image img,
img.getFormat(), Ogre::PixelUtil::bulkPixelConversion and ensure the constructed
QImage uses the converted PF_BYTE_RGBA buffer.

In `@src/PS1/PS1MAT.cpp`:
- Around line 234-236: Add Sentry breadcrumbs around the MAT file I/O: when
attempting to open the MAT file (the QFile f(matPath) / if (!f.open(...)) block)
call SentryReporter::addBreadcrumb("file.import", QStringLiteral("open MAT %1
failed").arg(matPath)) (include the error text or outError when available), and
also add a success breadcrumb on successful open like
SentryReporter::addBreadcrumb("file.import", QStringLiteral("open MAT %1
succeeded").arg(matPath)); likewise instrument the MAT write/export code path
referenced around the f.write / close block (the export block at lines ~289-291)
with SentryReporter::addBreadcrumb("file.export", ...) for both success and
failure cases, using the exact symbols QFile f(matPath), f.open, outError and
the export write functions to locate where to insert the calls.

In `@src/PS1/PS1PLY.cpp`:
- Around line 1459-1463: The file I/O blocks that open and read/write files
(e.g., the QFile f(filePath) read path and the other similar block around lines
2046-2054) are missing Sentry breadcrumbs; add
SentryReporter::addBreadcrumb(...) calls immediately before (or after a
successful) open/read and before a successful write/close using category
"file.import" for reads and "file.export" for writes, and include a descriptive
message containing the operation and the filePath (for example:
SentryReporter::addBreadcrumb("file.import", QString("Importing file:
%1").arg(filePath));). Ensure these breadcrumbs are placed in the same functions
that call QFile::open (and the function that calls the export write) so
readNonEmptyLines usage and the corresponding export routine are both
instrumented.
- Around line 1493-1494: The code coerces negative textureIndex into slot 0
causing wrong bindings; change the logic around fm.textured and textureIndex
used to compute submeshKey (the variable and expression using fm.textured,
fm.textureIndex, submeshKey and the call to getSoup) so that when fm.textured is
true but fm.textureIndex is < 0 you treat it as untextured (use -1) or fail
validation instead of mapping to 0; update any callers/consumers of
getSoup/TexturedSubmeshSoup accordingly and consider adding a debug/logging
check to surface invalid textureIndex values.
- Around line 2028-2039: The ngon export path is failing to set
PsyqExportFace::hasUv and UV fields, so when you later build ExportFaceTexture
from allExportFaces the textured flag and UVs are lost; fix by ensuring the ngon
branch populates PsyqExportFace::hasUv and its u/v_uv (or otherwise preserves
per-face UV state from the original source faces) when constructing
allExportFaces (used by outFaceTextures), so that in the ExportFaceTexture
creation (where you read ef.hasUv, ef.u, ef.v_uv) the correct textured/UV values
are present; adjust the code that handles useNgonExport to copy UV presence and
coordinates into each PsyqExportFace.

---

Nitpick comments:
In `@src/MeshImporterExporter.cpp`:
- Around line 1538-1551: The code re-opens the image file with QImage after
calling loadExternalTextureForRsd, causing a redundant decode; instead retrieve
the just-loaded Ogre::Texture (via Ogre::TextureManager::getByName or by
returning it from loadExternalTextureForRsd) and read its width/height to
populate rsdTexSlots[ti].width and .height (or modify loadExternalTextureForRsd
to accept outW/outH or return image/texture and use those values), and remove
the QImage qprobe(texPath) re-decode and stringly-constructed size logic.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: b9425540-1472-41d3-aa61-805db44b3d92

📥 Commits

Reviewing files that changed from the base of the PR and between 0382fa4 and cf1d1a3.

📒 Files selected for processing (9)
  • src/MeshImporterExporter.cpp
  • src/PS1/PS1MAT.cpp
  • src/PS1/PS1MAT.h
  • src/PS1/PS1MAT_test.cpp
  • src/PS1/PS1PLY.cpp
  • src/PS1/PS1PLY.h
  • src/PS1/PS1PLY_test.cpp
  • src/PS1/PS1RSD_test.cpp
  • src/test_main.cpp

Comment thread src/MeshImporterExporter.cpp Outdated
Comment thread src/PS1/PS1MAT.cpp
Comment thread src/PS1/PS1PLY.cpp
Comment thread src/PS1/PS1PLY.cpp Outdated
Comment thread src/PS1/PS1PLY.cpp
Correctness:
- PS1PLY: reject negative textureIndex in importPsyqPlyWithFaceMaterials
  (route invalid index to untextured bucket instead of slot 0).
- PS1PLY: preserve per-corner UVs in the Ngon export path so polygons and
  fanned triangles populate PsyqExportFace::hasUv + u/v_uv.
- MeshImporterExporter: convert non-RGBA Ogre image data via
  PixelUtil::bulkPixelConversion before constructing the QImage used for
  texture PNG sidecars, and pass an explicit row stride.

Nitpick:
- MeshImporterExporter: read texture width/height from the loaded
  Ogre::TexturePtr instead of redecoding the source file with QImage.

Instrumentation:
- Add Sentry breadcrumbs (file.import / file.export) around PS1 MAT and
  PS1 PLY open/parse/write success and failure paths.

Co-authored-by: Cursor <cursoragent@cursor.com>
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/MeshImporterExporter.cpp`:
- Around line 2151-2154: OutTex instances created in the texture export path
(see OutTex ot; ot.resourceName = QString::fromStdString(texName); ot.outFile =
ot.resourceName; and the Ogre::TextureManager::getSingleton().getByName(texName)
usage) set ot.outFile to the raw resource name which lets exists() short‑circuit
and leaves copied sidecars stale; change ot.outFile to a normalized basename
(strip path/dirs) and write the exported copy to that basename, explicitly
overwrite any existing sidecar or generate a unique renamed file before writing
so the export always refreshes the sidecar; apply the same change to the other
OutTex creation block with identical logic.
- Around line 1740-1747: The code in importPsyqPlyWithFaceMaterials()
unconditionally disables vertex colour tracking via
pass->setVertexColourTracking(Ogre::TVC_NONE), which wipes out the per-corner
colours you just imported; change this so vertex colours are preserved/modulate
the texture (e.g., use Ogre::TVC_DIFFUSE or Ogre::TVC_AMBIENT depending on your
intended modulation) instead of TVC_NONE, or conditionally set TVC_NONE only
when the mesh truly has no vertex colours—update the call to
pass->setVertexColourTracking(...) near the texture unit setup (and keep
mat->compile()) so textured faces that rely on vertex colour modulation render
correctly.
- Around line 1522-1535: resName is built from only the source filename so
different assets referencing the same basename (e.g., "Wood.jpg") collide in
Ogre's global texture registry; change the texture naming so resources are
scoped per-asset (e.g., include the asset ID, RSD path, or a stable hash of
texPath/parent asset) instead of just QFileInfo::completeBaseName(), then use
that scoped name for ogreName and all subsequent TextureManager calls
(resourceExists, remove, loadImage); ensure you no longer remove a
globally-named texture that might belong to another asset by deriving the unique
name in the resName construction and using that everywhere (symbols to update:
resName, texPath, ogreName,
Ogre::TextureManager::getSingleton().resourceExists/remove/loadImage).
- Around line 1491-1556: The per-texture RSD load loop (the for loop over ti
that uses resolve, PS1TIM::loadTimToOgreImage, and loadExternalTextureForRsd and
fills RsdTextureSlot) needs Sentry breadcrumbs for each significant action; add
SentryReporter::addBreadcrumb("file.import", "...") calls for: starting to
resolve/load a texture (include texPath or texRel), successful TIM load (after
loadTimToOgreImage and after creating tex via Ogre::TextureManager::loadImage),
successful external load (after loadExternalTextureForRsd and after fetching tex
via getByName), and failures (TIM load failed and external load failed, include
timErr/extErr), using the loop variable ti and resName in messages to uniquely
identify entries.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 7e983cb4-3e9d-4f1f-9150-fbd1c7ac10d3

📥 Commits

Reviewing files that changed from the base of the PR and between cf1d1a3 and cb8f2e7.

📒 Files selected for processing (3)
  • src/MeshImporterExporter.cpp
  • src/PS1/PS1MAT.cpp
  • src/PS1/PS1PLY.cpp
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/PS1/PS1MAT.cpp
  • src/PS1/PS1PLY.cpp

Comment thread src/MeshImporterExporter.cpp
Comment thread src/MeshImporterExporter.cpp Outdated
Comment thread src/MeshImporterExporter.cpp
Comment thread src/MeshImporterExporter.cpp
Correctness:
- Scope preloaded RSD texture resources by absolute path. Adds
  scopedRsdResourceName() (hash-suffixed: `Wood__abcd1234.jpg`) so two
  RSDs that both ship a `Wood.jpg` don't collide in Ogre's global
  TextureManager registry. rsdResourceNameToBasename() recovers the
  clean basename on export.
- Preserve per-corner vertex colours on textured submeshes after
  applying the RSD texture: only force TVC_NONE when the submesh has
  no VES_DIFFUSE stream; otherwise leave TVC_DIFFUSE so Psy-Q H/D/G
  materials still tint the texture.
- Always refresh texture sidecars on export. Drops the QFileInfo::exists
  short-circuit (which silently left the .rsd referencing stale content
  from a previous export) and normalises ot.outFile via
  rsdResourceNameToBasename() so scoped resource names land as plain
  filenames next to the .rsd.

Instrumentation:
- Add per-texture Sentry breadcrumbs (file.import) for missing,
  TIM-loaded, externally-loaded, and failed cases inside the RSD
  texture preload loop.

Verified with the Blender exporter Example Project.rsd: import + round-
trip now generates Wood.png next to RoundTrip.rsd and re-import returns
2 submeshes / 110 triangles as expected.

Co-authored-by: Cursor <cursoragent@cursor.com>
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/MeshImporterExporter.cpp`:
- Around line 1703-1735: The MatEntry values for texture usage are copied into
PS1PLY::FaceMaterial (fm) without validating that the textureIndex actually
points into rsdTexSlots, which lets a single invalid textured face cause
importPsyqPlyWithFaceMaterials(...) to fail; before assigning fm.textured and
fm.textureIndex (and before filling uv coords) validate me.textureIndex against
rsdTexSlots and if invalid set fm.textured = false and fm.textureIndex = -1 (and
avoid assigning u/v), then populate faceMats[fi] and call
importPsyqPlyWithFaceMaterials; preserve the existing fallback to
importPsyqPly/importPsyqPlyWithFaceColors if the textured importer rejects the
file.
- Around line 2327-2331: The code unconditionally assigns ot.outFile to the PNG
name even if QImage::save failed; change the logic in the texture export loop
around the qi.copy().save(png, "PNG") call to check its boolean return value and
only update ot.outFile = QFileInfo(png).fileName() when save(...) returns true
(otherwise leave ot.outFile unchanged and emit an error/warning via the existing
logging mechanism). Locate this in MeshImporterExporter.cpp where
qi.copy().save(png, "PNG") and ot.outFile are handled and perform the
conditional assignment and logging there.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 102ed635-1ab0-457e-beb5-cf59263bf05c

📥 Commits

Reviewing files that changed from the base of the PR and between cb8f2e7 and 4d6fade.

📒 Files selected for processing (1)
  • src/MeshImporterExporter.cpp

Comment thread src/MeshImporterExporter.cpp
Comment thread src/MeshImporterExporter.cpp Outdated
Correctness:
- Sanitise per-face MAT texture references before calling the textured
  PLY importer: a single entry that claims to be textured but points at
  a missing slot now downgrades to solid (textureIndex=-1) for that
  face only, instead of forcing importPsyqPlyWithFaceMaterials() to
  reject the whole batch and drop textures on every face.
- Verify QImage::save() succeeded before mutating ot.outFile. QImage
  signals write failure via the return value (no exception), so the
  previous unconditional update could leave the .rsd referencing a
  sidecar that was never written. Failures now log an Ogre error and
  emit a Sentry breadcrumb; ot.outFile keeps its previous basename so
  callers can fall back to the originally referenced texture.

Co-authored-by: Cursor <cursoragent@cursor.com>
@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