PS1 Asset Optimizer
The PS1 Asset Optimizer creates a lower-poly runtime copy of a static OBJ or FBX while preserving the project source and a reproducible import recipe. It is intended for modern assets whose silhouette is useful but whose topology is too dense for a PlayStation 1 production budget.
The optimizer is a constrained simplifier, not a one-click art replacement. It stops before a requested target when reaching it would violate the enabled topology, UV, material, symmetry, normal, or vertex-displacement constraints.
Open the Optimizer
- Import an OBJ or static FBX into
Assets/Models/. - Select the source in the Project panel.
- In the Model Inspector, click Open PS1 Asset Optimizer.
- Choose the closest Asset Role.
- Start from Use Recommended Target, then adjust the logarithmic triangle slider if the asset needs a different trade-off.
- Inspect the source preview, compatibility report, and comparison counts in the same optimizer window, then click Generate Optimized Output.
- Keep the window open while generation runs. Compare Source and Generated with the shared preview controls, then adjust and generate another pass if needed.
The optimizer is a modeless, movable, and resizable workspace. It does not hide or lock the Inspector behind it, and it can always be closed with its title-bar X, Close Window, or Escape. Wide windows show preview/status and recipe side by side; compact windows switch them into tabs so controls are not clipped.
Generation runs as a background import. The optimizer keeps its action footer visible and shows progress plus a Cancel action; cancellation stops before a partial candidate is published. You may close the optimizer while the job continues. Other asset edits and imports wait for the model transaction, preventing two jobs from overwriting the asset database concurrently.
The Inspector automatically offers the optimizer for especially dense static
models. Imported files chosen outside the project are copied into
Assets/Models/ before processing. The source is never rewritten; only the
generated .msh under Library/ is replaced.
Recommendations and Asset Roles
The recommendation combines the model role with the project's configured per-mesh triangle budget.
| Role | Intended content | Default guidance |
|---|---|---|
| Auto | Unknown or mixed content | Uses source triangle count and project budget |
| Hero | Close-up focal object | Up to about 2,000 triangles |
| Character | Readable animated-style silhouette, imported as static | Up to about 1,800 triangles |
| Prop | Common world object | Up to about 800 triangles |
| Environment | Modular level section | Uses the project per-mesh soft limit, capped at 2,048 |
These values are recommendations, not new hardware limits. Scene triangle count, material changes, texture pages, overdraw, ordering-table behavior, and CPU work still determine whether the asset is appropriate.
Quality and Preservation Controls
The optimizer uses deterministic quadric-error edge collapse on the OpenMesh topology kernel with explicit Polygon Engine constraints. OpenMesh is bundled under its BSD license and listed in the product's third-party notices.
| Control | Effect |
|---|---|
| Quality | Selects edge sensitivity plus conservative, balanced, or aggressive normal/error defaults |
| Symmetry | Auto-detects or explicitly preserves an X, Y, or Z mirror plane |
| Preserve Boundaries | Prevents open borders from being collapsed away |
| Preserve UV Seams | Protects incompatible UV islands and rejects surviving UV triangles that collapse, flip, or exceed the allowed area-ratio change |
| Preserve Hard Edges | Protects sharp normal discontinuities |
| Preserve Material Boundaries | Keeps material regions separated |
| Max Normal Deviation | Stops collapses that rotate surfaces too far |
| Max Vertex Displacement | Caps accumulated collapsed-vertex displacement as a percentage of the source bounds diagonal |
Auto symmetry enables paired collapses only when the source has a reliable mirrored vertex and face correspondence. An explicit symmetry axis fails closed if that correspondence cannot be guaranteed. Center-plane vertices remain on the plane.
The optimizer always preserves the number of connected components. When Preserve Boundary Loops is enabled, it also preserves the number of boundary-loop components. In that mode, “no new holes” means an accepted result does not create or remove those loop components. A non-manifold or invalid source is rejected with an actionable message instead of being guessed at.
Preview and Result
The optimizer window keeps the asset path, source/generated/recommended counts,
complete PS1 compatibility report, import errors, preservation controls, and
preview together. When no valid generated candidate exists, the first optimizer
view selects Source automatically instead of showing an empty generated
preview. After generation, switch between Source and Generated with the
same orbit and display mode. Very dense sources are sampled deterministically to
keep the comparison responsive. The sampled source preview is cached under
Library/model_previews/, so the comparison remains available after restarting
the editor and is regenerated whenever the model is reimported. Use:
- Textured + Wireframe for topology over the final material;
- Wireframe for density and long triangles;
- Silhouette for the outer contour;
- UV Density for relative UV coverage differences; magenta marks a degenerate geometric or UV triangle;
- PS1 Quantized for the runtime-oriented vertex appearance.
The visible result status and stored metadata record whether the target was reached, detected symmetry, source/output vertices and triangles, the accumulated vertex-displacement bound, and a stop reason. If the target was not reached, lower the preservation constraints only after checking why the protected feature matters.
Source and Runtime Limits
The import working mesh uses 32-bit indices so a source above 65,535 vertices
can be parsed and simplified. The generated PS1 .msh still uses 16-bit
indices. Generation fails before replacing the prior output if the optimized
mesh remains above 65,535 vertices or contains invalid indices.
The generated mesh is also capped at the runtime's 2,048 sortable triangles. This is a hard output contract even if a project raises its authoring soft limit. T-junction repair is accounted for after simplification; if repair raises the final count above the requested target, the result is marked as the best valid result, and a result above 2,048 is rejected.
T-junction repair and detailed PS1 topology diagnostics run after simplification. Extremely dense candidates may skip expensive diagnostics and report that decision rather than freezing the editor.
Reimport and Recipes
Optimization is part of the model import recipe. The following choices are stored in source metadata and included in the importer settings hash:
- enabled state and requested triangle target;
- role and quality;
- symmetry mode;
- boundary, UV seam, hard-edge, and material-boundary preservation;
- normal-deviation and vertex-displacement limits.
Reimporting the source reproduces the recipe. Changing one of these settings invalidates the generated result. Mesh, optional new material, skeletal output, asset database, and metadata are published as a rollback-backed transaction. An invalid conversion or persistence failure keeps the previous generated set instead of mixing old and new artifacts.
Current Scope
- Static triangle meshes only.
- OBJ and static FBX sources supported by the normal model importer.
- Existing positions, normals, UVs, and material regions are considered.
- Sources with multiple material slots must keep Single Material enabled; material regions are protected during reduction, then collapsed to the one material currently published by the model importer.
- Skinned FBX optimization is blocked because topology reduction would also require deliberate bone-weight and animation remapping.
- The feature does not create UVs, retopologize arbitrary non-manifold input, optimize collision separately, or prove final draw ordering on hardware.
For animated content, reduce the mesh in the DCC and follow Skeletal Animation. For the runtime budget context, see PS1 Hardware Constraints & Budgets.
Troubleshooting
Target not reached
Read the stop reason. A protected boundary, material seam, UV seam, hard edge, symmetry pair, normal limit, or vertex-displacement limit may make the requested target unsafe. Raise the triangle target first. Disable a constraint only when you have inspected the relevant preview.
This is different from an import error: a valid result can contain more triangles than requested. UV seams, hard edges, and a high source polygon count are not, by themselves, proof that the model is broken. The final output must still fit the runtime limits.
Symmetry cannot be preserved
Identify: Check whether Symmetry is forced to X, Y, or Z. A model can look
symmetric while its vertices or face connections do not match across that axis.
The diagnostic may include Requested symmetry cannot be guaranteed.
Fix: Choose Auto or Off, then generate again. If exact symmetry is required, correct the mirrored geometry and topology in your modeling tool and re-export. Changing the triangle target does not repair an unmatched mirror pair.
Source is non-manifold or invalid
Start with the full error in the Model Inspector or optimizer window; use
Copy Import Error when reporting it. Some builds show the generic
Asset Optimizer requires orientable manifold triangle topology message.
The checks below help narrow down that error as well as more specific diagnostics.
Work on a backup of the source model, not the generated mesh in Library/.
When a diagnostic gives imported triangle or vertex numbers, they refer to the converted mesh, not Blender face IDs. Coordinates may include import transforms and welding. A duplicated quad can therefore be reported as two duplicate triangles. OBJ diagnostics that mention a line refer to the exported text file.
An open plane or room with inward-facing surfaces is allowed. It does not need thickness or outward normals just to satisfy the optimizer. The problem is an invalid connection or inconsistent orientation, not an intentional open border.
Duplicate or overlapping faces
Identify: Inspect the suspect surface in wireframe. Hide its object, or hide one selected face in Edit Mode, and check for another surface occupying the same space. Separate objects can contain identical geometry; they need not share a name.
Fix: Delete the unintended copy, keeping the intended surface and its UVs. Check both the object list and the geometry inside each object. Engine Weld Vertices merges vertices but does not delete duplicate faces. Blender's Merge by Distance can help clean duplicate vertices, but inspect the faces afterward. Shrinking or duplicating the same model does not remove its redundant polygons.
More than two faces share an edge
Identify: In Blender Edit Mode, use vertex or edge selection and Select > Select All by Trait > Non-Manifold. Inspect Multiple Faces; Boundaries also selects valid open borders, so those selections alone do not mean the model is invalid. See Blender's selection options.
Fix: Remove unintended internal faces, or rebuild/split the junction so an edge belongs to one boundary face or two adjacent faces. Keep separate surfaces separate. If welding reconnects them during import, inspect their positions and the Weld Threshold instead of repeatedly joining everything together.
Surfaces touch only at one vertex
Identify: Inspect highlighted non-manifold vertices and the surrounding wireframe. Look for otherwise separate patches joined at a single point rather than along an edge. A connection error can identify the triangle where processing stopped without proving which source edit created the problem.
Fix: Split the shared vertex if the patches should be independent, or rebuild the intended connection with proper faces and shared edges. Check that Weld Vertices does not recreate the unwanted connection on reimport. If independent patches must occupy the exact same point, use separate model assets or disable welding after checking the geometry. Splitting them into objects inside the same exported model may still let the importer weld them together. This is not a substitute for deleting unintended duplicate faces.
Neighboring faces have inconsistent orientation
Identify: Enable Blender's Face Orientation overlay and inspect adjacent faces from the intended visible side. Look for an isolated face facing the opposite way, not simply red faces on the back of a valid surface.
Fix: Select the affected connected geometry in Edit Mode and use Mesh > Normals > Recalculate Outside or Recalculate Inside, according to the intended surface. Inspect the result and flip individual faces if needed, especially on open planar geometry. Recalculating shading normals in the engine does not reverse face winding. Flipping the whole model also leaves a local orientation mismatch unresolved.
Collapsed faces or an excessive weld threshold
Identify: Look for a triangle that repeats a vertex, zero-length edges, or faces reduced to a line or point. If it fails only with Weld Vertices, compare the Weld Threshold with the size of the smallest intended detail.
Fix: Remove or rebuild the collapsed face. Blender's
Degenerate Dissolve
can help with selected degenerate geometry; use a small threshold and inspect the
result. Reduce Weld Threshold if it merges distinct corners. For the separate
collapses at PS1 vertex precision error, follow the
precision guidance.
Invalid vertex references, positions, normals, or UVs
Identify: Read which triangle, vertex, attribute, or OBJ line is named.
A missing vertex reference is damaged face data. NaN or infinity means a
position, normal, or UV contains an invalid numeric value; it does not just mean
the texture looks stretched. An empty-mesh error can mean only edges, curves, or
empty objects were exported.
Fix: Repair the affected face or geometry, check transforms and modifiers, recalculate invalid normals, or unwrap the affected UVs as appropriate. Export the selected mesh objects with faces to a fresh OBJ/FBX. If the new export still fails, report the exported file, full error, and import settings. Do not guess at numeric indices in the generated mesh file.
Skinned mesh supplied to the static optimizer
Identify: Check whether the FBX includes skin weights or bone bindings. A character exported in a still pose can still contain this data. The optimizer may report that static optimization is disabled for skinned models.
Fix: For animated content, use the normal FBX import workflow and reduce the mesh in your modeling tool while preserving skin weights; follow Skeletal Animation. The optimizer controls are disabled for skinned sources. If an existing asset retains a static optimization recipe, import the animated FBX as a new asset with default settings. For a static prop, export a separate static mesh without skin data. Do not remove the rig from the only copy of an animated source.
Verify the corrected model
- Export the corrected OBJ/FBX and update the source under
Assets/Models/. - Use Reimport, or Apply & Import after adjusting import settings.
- Generate the optimized output and confirm the error is cleared. Fixing one issue can reveal another, so repeat the relevant check if necessary.
- Inspect Generated in wireframe and textured modes, then check output counts and PS1 compatibility warnings.
- Build and test the affected scene in DuckStation and on intended hardware.
A failed conversion preserves the last successful output. An old preview still being visible does not prove the corrected source was accepted.
The output is valid but looks wrong on PS1
Check long triangles, material count, texture filtering and palettes, UV islands, coplanar overlap, near-plane crossings, and ordering-table behavior. Use the PS1 Budget dashboard, a packaged DuckStation build, and representative real hardware before shipping.
Production Checklist
- preserve the original source under
Assets/Models/; - begin with the recommended target and review the stop reason;
- inspect wireframe, silhouette, UV density, and PS1-quantized previews;
- compare source/output counts and confirm material assignment;
- re-check collision if it was authored from visual geometry;
- review the full scene in the PS1 Budget dashboard;
- validate packaged output in DuckStation and on intended hardware.