Saltar al contenido
Inicio

Side projects / PSXIFY

PSXIFY

Desarrollador en solitario · Herramientas de desarrollo

2026 | Publicado

Postmortem de una herramienta en Rust que hornea modelos de alto poligonaje en assets estilo PS1, con un pipeline de cachés por etapas, un lector de Blender que no necesita Blender y el diseño de culling que surgió del manejo de transparencias.

Stack

Rust, egui, glTF, FBX, meshoptimizer, rayon

Enlaces

Probar el juego ↗

PSXIFY cover

Resumen

PSXIFY toma un modelo de alto poligonaje y produce una malla de bajo polígono con una textura pequeña de paleta limitada. La parte difícil no es un paso concreto. Cada etapa depende de la anterior, y el usuario necesita ajustar el resultado en tiempo real, así que el pipeline no puede rehacer trabajo que no ha cambiado.

Este postmortem repasa cómo está organizado el pipeline, los tres problemas que más cambiaron el diseño y lo que la herramienta todavía no hace.

Pipeline como cadena de cachés

El procesamiento tiene tres etapas. Cada una guarda su resultado bajo una clave formada por los parámetros que la afectan:

  1. Geometría. La malla se suelda, se limpian las piezas pequeñas, las caras ocultas se eliminan con un raycast sobre una BVH y se decima con meshoptimizer. La clave incluye el modo de reducción, el objetivo, el flag de piezas, el umbral de limpieza y el modo de culling.
  2. Bake. Se desenvuelven y empaquetan UVs nuevas y se hornean albedo, oclusión, normales, metal y emisión desde el modelo original. La clave añade el tamaño de textura, porque la resolución de bake es cuatro veces el tamaño final, con un máximo de 1024.
  3. Composición y cuantización. Los sliders de AO, relieve y luz se aplican sobre el bake guardado, la imagen se reduce y la paleta se cuantiza con dither. Esta etapa es la única que se recalcula al moverlos.

El resultado es que mover el número de colores o el dither cuesta milisegundos, porque se reutilizan la geometría y el bake. El coste es clasificar bien cada parámetro: si una clave incluye algo que no afecta a la etapa, se recalcula de más, y si le falta algo, el resultado guardado queda obsoleto sin avisar. Por eso hay tests para esos casos límite, por ejemplo para comprobar que el objetivo en triángulos no invalida la geometría cuando el modo activo es porcentaje.

Leer .blend sin Blender

Los archivos de Blender son una entrada habitual, y exigir una instalación local de Blender habría dejado la herramienta fuera del alcance de cualquiera que no la tuviera. Por eso la app lee los .blend directamente.

Cada archivo incluye su propia descripción de estructuras internas (SDNA), así que el lector busca los campos por nombre en lugar de usar desplazamientos fijos. Eso permite que un mismo lector cubra datos de malla de Blender 3.x (MVert, MPoly, MLoop) y el almacenamiento por atributos de 4.x y 5.x, comprimidos o no. El lector cubre lo que el pipeline necesita: datos de malla, jerarquías de objetos con los modos de rotación habituales y las entradas del Principled BSDF que alimentan el bake. Lo demás se ignora con un aviso visible en la app, para que el fallo no sea silencioso.

Tarjetas transparentes y culling

El culling de caras ocultas parte de una suposición: lo que está delante de una superficie es sólido. En vallas o follaje esa suposición no se cumple. Una tarjeta transparente delante de una superficie la taparía en el test de visibilidad, y se eliminaría geometría que sí se ve a través de los recortes.

Por eso la etapa de geometría comprueba has_transparency() en el modelo. Si es verdadero, el culling se desactiva, pero la limpieza de piezas y el decimado siguen ejecutándose.

El bake también deja pasar la luz según la opacidad, para que la AO no oscurezca zonas que se ven a través de un recorte. La preview usa profundidad para los texels opacos y composición ordenada por triángulos para los transparentes. Las superficies transparentes que se intersecan pueden mostrar artefactos, que es la limitación habitual de ese orden.

Reducción que no borra piezas

Al decimar el modelo completo aparece un fallo previsible: las piezas pequeñas y separadas, como las tarjetas de un follaje, pueden quedarse sin triángulos cuando el presupuesto es ajustado. En modo Pieces cada componente conectado conserva al menos cuatro triángulos, o todos si tenía menos. Si el decimador baja de ese mínimo, el presupuesto de esa pieza se sube hasta que sobreviva.

La consecuencia es que un objetivo muy bajo puede producir más triángulos de los pedidos. La interfaz y el README lo dicen así, porque la alternativa era borrar geometría en silencio. Un test comprueba que diez tarjetas de dos caras sobreviven a la reducción, al bake y a un cambio entre modos de caché.

Si la reducción elimina todas las caras, el pipeline devuelve un error explícito en lugar de una malla vacía, y el mensaje sugiere subir el objetivo o activar Pieces.

Resultados

El payphone de Poly Haven que aparece en el vídeo promocional es un modelo de 48.132 triángulos con texturas PBR de 2K. El resultado tiene 798 triángulos, una textura de 256 px y 15 colores. Modelo de Poly Haven, licencia CC0, autor UM JOORIN.

El mismo modelo antes y después: 48.132 triángulos con PBR de 2K a la izquierda, y 798 triángulos, textura de 256 px y 15 colores a la derecha.
El flujo completo en la app: el modelo se arrastra, se hornea y se muestra en la preview de estilo PS1.

Límites

  • Solo mallas estáticas. Esqueletos, animación y texturas UDIM quedan fuera.
  • Los materiales procedurales de Blender no se evalúan; solo se usa el nodo de color base.
  • De los modificadores de Blender solo se aplica Mirror. Subdivision, Edge Split, Weighted Normal, Triangulate y Smooth by Angle no cambian el resultado y se omiten. Los demás se ignoran con aviso, así que Array, Boolean y Geometry Nodes deben aplicarse antes en Blender.
  • El build de macOS es universal. Windows y Linux están marcados como beta porque se han probado menos.

Mi aportación

Diseñé y desarrollé la herramienta: el pipeline y sus claves de caché, el lector de .blend, el tratamiento de transparencias, el shader de la preview, los presets de exportación para Unity, Unreal, Godot y Blender, la interfaz de línea de comandos y la publicación.

Stack

  • Rust, con eframe / egui para la interfaz y la preview
  • ufbx para FBX y OBJ, y gltf para glTF y GLB
  • meshopt (meshoptimizer) para el decimado, rayon para el paralelismo y glam para las matemáticas
  • ruzstd para los .blend comprimidos, y image, png y exr para las texturas
  • objc2 y AppKit para los diálogos nativos de macOS

Referencias