Commit Graph

4 Commits (gem-placement)

Author SHA1 Message Date
Amir Alexander Abdelbaki c711ae2831 Solve gem placement instead of dialling it in
Gem slots could not be positioned by hand because translation is not the
gem's position. The gem model is `item/generated`: a 2x2 sprite block at the
dead centre (8,8) of a 16x16 sheet, so its geometry sits 8px from the model
origin on every axis. MIAPI's Transform.toMatrix is T * Rx * Ry * Rz * S with
no centring offset, so rotation pivots on that origin and throws the gem up to
12px away. Translation is whatever cancels the throw.

    world = pivot + flip(T + R*S*c),  flip = negate x,y,  c = (8,8,8)

`flip` is the block-model(+y up) -> ModelPart(+y down) conversion. Inverting
it gives T from a desired anchor, so tools/solve_gems.py takes targets in
plain world pixels and asserts the result forward-evaluates back.

The helmet gem is the exception: it chains through the hat slot, which scales
by 1.25 and lifts by -3.925, and does NOT go through the flip. Solving it with
the flip inverts y and drops the gem past the chin to the floor. The chain is
recorded against two in-game sightings that pin it.

Also:

- Drop the stray no-origin model entry from all eight limb gem/case modules.
  Without an origin a model falls through the slot's z180 and lands mirrored
  in x and y - which is why a right-arm gem showed up above the left shoulder.
- Give the helmet and backtank gems origin-carrying modules. Their slots took
  `gem_armor_medium`, whose only members declare no origin, so the gem was
  drawn in the body pass and never appeared. Apothic cases included so
  repointing the slots loses nothing.
- Compensate the z180 in the helmet model UVs. Geometry in these models is
  already authored around the flip; UVs cannot be, so every face rendered
  rotated 180. The face that shows on top is the one authored `down`.
- Remove the misaligned placeholder bezels from the four limb sockets.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-26 20:28:07 +02:00
Amir Alexander Abdelbaki 8e5178ece6 A hue per object, and faces that cannot be confused with their opposite
Untextured, every model came out the same red. The hue was the box's index
within its model and almost every model here is one box, so a scene of eight
was eight objects sharing hue zero - the one thing the colour was supposed to
tell you was the one thing it did not. So `face_colour` takes a hue now, and
the quickstart hands each model its own: successive golden-ratio turns around
the wheel, so any two are far apart and the third is not squeezed between them,
indexed on the model's place in the whole list rather than on what happens to
be visible. The boxes of one model spread a tenth of a turn either side, near
enough to read as one object and far enough to count. Left alone the hue is
what it always was - the wheel split per shape - so ARMOUR_GUI and the unwrap
templates are unchanged.

The shades were the other half. Six faces were sharing three saturations, and
east and west were separated by nothing but a fifth of a value - which is the
worst place to be thin, because opposite faces are the pair you can only ever
see one of at a time and so have nothing to compare against. Each face now sits
at its own rung of both ladders, and each opposite pair is far apart on both:
up is pale and brightest against a down that is vivid and darkest, north vivid
and bright against a washed, dim south, west the palest against a vivid,
mid-dark east.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-24 21:23:45 +02:00
Amir Alexander Abdelbaki e7b211932f A viewport that draws, and a front door that opens with no arguments
The armour tools would not start and then would not draw, for two reasons that
looked like one. pyvista and pyvistaqt are packaged by hardly any distribution,
so `guiplatform` now keeps a virtualenv at `tools/.venv` and re-runs the tool
inside it when the interpreter it was started with cannot find the stack -
built with `--system-site-packages`, so the distribution's Qt stays the Qt in
use. Only an absent module sends us there; a PySide6 that will not load is a
system problem the venv shares, and it still gets its own message.

The black viewport was the other half, and it was ours. Handing pyvistaqt a
`vtkGenericOpenGLRenderWindow` is the right idea for VTK's C++ widget and wrong
for this one: the Python `QVTKRenderWindowInteractor` paints from `paintEvent`,
never implements `paintGL`, never binds Qt's framebuffer and never makes a
context current, so the render window it was given drew into no context at all.
Nothing raised - VTK would not even read its own buffer back. pyvistaqt makes
its own render window now, which is the only one it knows how to drive, and the
probe builds its viewport the same way the tools do rather than a way that
always failed. That probe had been timing out on every launch for the same
reason, so Wayland could never be chosen anywhere; it now says what actually
went wrong, X protocol errors included.

ARMOUR_QUICKSTART is the front door: no arguments, every model in the tree -
worn armour, icons, sword parts, the loose item models - listed with the first
one showing, and a jar's models listed after ours when one is passed, because a
socket is geometry cut into a plate that lives in Armory's jar. Where each
model goes comes from the mod's own module data, since the `origin` a module
names is the part MIAPI draws it under; without Armory's slot transforms a worn
model is flipped onto its part but not offset along it, and it says so.

The left panel is that model's MIAPI transform. Three arrows on the model's
origin move it, a toggle swaps them for three rotation rings, and the numbers
are the ones a module writes - a drag is unwound back through the pivot and the
flip before it reaches them, so what the panel reads is what goes in the JSON.
Nothing here writes: the number is what you leave with.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-24 20:53:07 +02:00
Amir Alexander Abdelbaki 7442ee7ac7 A front door for the armour editor, and names that say what to run
Running it needed a scene and a jar path known in advance, which is a poor way
to meet a tool. With no scene named it now asks: the four scenes listed with
their own docstring's first line as the description, so the blurb cannot drift
from what the scene builds, and the sources pre-filled with the repo's own
resources first and Armory's jar after, found by looking where the pack's jars
actually live. Textures, wearer, material variant and gem size are there too.

The dialog is Qt and nothing else. The viewport it leads to is a plotter with a
window of its own, so there is no shared GL context to get wrong and no reason
to sit through the Wayland probe before a scene has been picked; the
QApplication is shut down before the viewport opens rather than left to run a
second event loop. `--shot` still insists on a scene, because it renders
without a window and an unattended render should not stop to ask.

Widgets carry object names. That began as a way to drive them from a test after
addressing them by index quietly drove the wrong ones, and it is right anyway -
an index moves the next time a row is added above it.

The three tools you start are ALL CAPS now and the libraries are not, so the
directory says which is which. main() also stops adding the repo's resources a
second time when the dialog has already listed them.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-24 16:18:33 +02:00