Dessin et canevas
Comment choisir entre les deux voies

Contenu en coordonnées écran (lignes de texte PIDS, blocs de texture) → Text/Texture : un enregistrement de dessin, rejoué par l’hôte via exactement le même chemin de code que JS. Contenu au niveau pixel (LCD de véhicule, écrans à matrice de points dynamiques, graphiques) → GraphicsTexture pour dessiner vous-mêmes les pixels, rastérisés via les primitives de Gfx2D, seuls les rectangles modifiés étant téléversés vers le GPU.

Text / Texture / Rect (PIDS)

<mtr/text.hpp> est le portage 1:1 de l’API en chaîne TextWrapper / TextureWrapper / Rect de JS — les noms de méthodes suivent la convention snake_case du C++, avec appels chaînés :

mtr::Text::create()
    .text("Tsim Sha Tsui")
    .scale(1.725)
    .size(100, 9)
    .stretch_xy()
    .font_mc()
    .color(0xFC9700)
    .pos(3, rowY)
    .draw(ctx);
Méthode TextÉquivalent JSDescription
.text(str[, len]).text()texte UTF-8
.scale(s).scale()mise à l’échelle globale
.left_align() / .center_align() / .right_align()homonymealignement (centré par défaut)
.shadowed() / .bold() / .italic()homonymedécorations du texte
.stretch_xy() / .scale_xy() / .wrap_text() / .marquee(duration)homonymemodes de débordement (le défilement marquee peut figer la progression via .with_marquee_progress(p))
.font(id) / .font_mc()homonymepolice de ressource (par ex. .font("jsblock:shanghai")) / police intégrée de MC
.color(argb)homonymecouleur (ARGB)
.pos(x, y) / .size(w, h)homonymecoordonnées écran et boîte englobante
.z_order(z) / .layer(l)homonymehiérarchie de couches ; avec z=-1, l’attribution est faite automatiquement par le contexte
.render_type("LIGHT_1").renderType()couche de rendu par nom (équivalent de QueuedRenderLayer.valueOf ; un nom inconnu lève une exception)
.natural_light().naturalLight()prend la lumière du monde au lieu de la luminosité maximale de rendu
Méthode TextureDescription
.texture(id)id de ressource de texture (par ex. "mtr:textures/block/pids.png")
.uv(u1, v1, u2, v2)sous-rectangle UV
.color(argb)teinte
.pos(x, y) / .size(w, h)position et taille
.z_order(z) / .layer(l)hiérarchie de couches
.render_type("LIGHT_1") / .natural_light()couche de rendu par nom / lumière du monde (comme Text)

Rect (rectangles pleins)

mtr::Rect est le miroir de la classe Rect du JS amont (nommée Rectangle avant l’amont v2.3.0-prerelease.2 ; le nom de la classe Java est inchangé) — elle dessine une facette mtr:white.png aux UV complets, teintée d’une couleur : le moyen le plus léger de construire des blocs de fond, des barres de séparation et des bandes de couleur d’état :

mtr::Rect::create()
    .color(0xFF00539F)          // teinte opaque (ARGB_WHITE par défaut)
    .pos(2, rowY + 18)
    .size(pids.width() - 4, 1)  // 20×20 par défaut
    .draw(ctx);
Méthode RectDescription
.color(argb)teinte (blanc ARGB_WHITE par défaut)
.pos(x, y) / .size(w, h)position et taille (20×20 par défaut)
.render_type("LIGHT_2")couche de rendu par nom (LIGHT_2 par défaut)
.natural_light() / .z_order(z)lumière du monde / ordre z (z=-1 attribué automatiquement)

Méthodes associées du contexte (PidsContext) :

  • ctx.set_auto_z_ordering(bool) — ordre z automatique (activé par défaut)
  • ctx.set_z_order_step(step) — pas de l’ordre z automatique (0.0002 par défaut)

Couches de rendu (énumération 2D MtrRenderLayer, indépendante de mtr::Matrices) : MTR_LAYER_TEXT (texte), MTR_LAYER_LIGHT (éléments lumineux), MTR_LAYER_EXTERIOR / MTR_LAYER_INTERIOR (extérieur / intérieur), alignées sur le QueuedRenderLayer de MTR.

Matrices et dessin de modèles

<mtr/matrices.hpp> est le miroir du Matrices global de JS : les transformations sont empilées, et compile() compile la pile courante en un jeu de matrices écrit dans la zone de matrices de la frame :

mtr::Matrices m;
m.translate(0.5f, 1.0f, 0.0f);
m.rotate_y_degrees(90.0f);
m.scale(1.0f, 1.0f, 1.0f);
ctx.draw_car_model(model_handle, car_index, &m);   // véhicule
ctx.draw_model(model_handle, &m);                   // eyecandy / ascenseur
MembreDescription
translate(x, y, z) / scale(x, y, z)translation / mise à l’échelle
rotate_x / rotate_y / rotate_z(radians)rotation (radians)
rotate_x_degrees / …_y… / …_z…(deg)rotation (degrés)
compile()compile la pose courante dans la frame (retourne l’indice du jeu de matrices ; à transmettre aux appels draw)

Le contexte véhicule propose en outre draw_bogie_model(model, car, bogie, matrices) (bogies), play_car_sound(sound, car, x, y, z, volume, pitch) et play_ann_sound(sound, volume, pitch) (annonces de station audibles uniquement par les joueurs à bord).

Les handles de modèle proviennent des services de l’hôte : ctx.host()->acquire_model(user, "namespace:path/model.json") pour l’acquisition, release_model(user, handle) pour la libération ; acquire_quad_model(...) construit une facette texturée détenue par l’hôte à partir de 4 sommets en espace modèle + UV (transformant une GraphicsTexture en écran applicable sur une voiture). Détails dans ABI C et services de l’hôte.

GraphicsTexture (canevas de pixels)

Dans <mtr/gfx.hpp>, GraphicsTexture garde le tampon de pixels dans votre module ; le jeu ne reçoit que des téléversements de rectangles modifiés — les données pixel ne traversent pas la frontière à chaque frame :

struct LcdState {
    mtr::GraphicsTexture tex;      // à placer dans State : même cycle de vie que l'instance
};

void create(mtr::VehicleContext& ctx, LcdState& s, const mtr::Vehicle&) {
    s.tex.create(ctx.input(), 512, 128);   // enregistre en même temps la texture GPU auprès de l'hôte
    // appliquer la texture sur la voiture : host->acquire_quad_model(...) → draw_car_model(...)
}

void render(mtr::VehicleContext& ctx, LcdState& s, const mtr::Vehicle& vehicle) {
    // 1. dessiner les pixels (Gfx2D ou set_pixel directement)
    // 2. ne téléverser que la zone modifiée :
    s.tex.upload(ctx.frame());
}
MembreDescription
create(input, w, h)alloue le tampon de pixels (BGRA8, ordre des lignes) et crée la texture GPU côté hôte ; au retour, l’intégralité est considérée comme modifiée
pixels() / pixels32()pointeurs pixels directs (uint8_t* / uint32_t*)
set_pixel(x, y, argb)écrit un pixel et cumule automatiquement la zone modifiée
upload(frame)écrit le rectangle modifié courant dans la zone de pixels de la frame (un enregistrement JCM_DRAW_TEXTURE_UPLOAD), puis réinitialise la zone modifiée
last_upload_bytes()nombre d’octets du dernier téléversement — l’outil d’observation de performance par excellence
close(input) / destructeurlibère la texture GPU de l’hôte
handle() / width() / height() / texture_bytes()handle de texture côté hôte et informations de dimensions
Point clé de performance

upload() ne transfère que la boîte englobante modifiée depuis le dernier téléversement. Le fond statique se dessine une fois ; à chaque frame, ne touchez que les bandes qui doivent bouger (marquee, segments clignotants, barre de progression). Si le clignotement ou le carrousel d’un LCD de véhicule ne modifie qu’une petite bande, le téléversement se limite à cette bande.

Le même en-tête fournit aussi LcdCanvas — un pinceau LCD fondé sur la police matricielle intégrée 5×7 (couleurs allumée/éteinte configurables), adapté aux écrans à matrice de points classiques.

Rastériseur logiciel Gfx2D

<mtr/gfx2d.hpp> réimplémente le sous-ensemble usuel de java.awt.Graphics2D en rastériseur logiciel déterministe, dessinant directement dans une GraphicsTexture. Les logiques fillRect / fillOval / drawLine / GeneralPath / clip / drawString des packs de ressources JS se portent mécaniquement :

mtr::Gfx2D g(s.tex, ctx.host(), "jsblock:shanghai");  // canevas + police de l'hôte

g.set_color(mtr::clr(12, 28, 44));                    // fond sombre
g.fill_rect(0, 0, W, H);

g.push_clip(8, 8, W - 16, 40);                       // pile de clipping
g.set_color(mtr::clra(255, 205, 66, 255));
g.draw_text_centered(W / 2, 30, 18.0, "下一站 Next");
g.pop_clip();
PrimitiveDescription
fill_rect / fill_round_rect / draw_round_rectrectangles (paramètre de rayon des angles arrondis)
fill_oval / draw_ovalellipse pleine / en contour (épaisseur de trait)
draw_line(x1, y1, x2, y2, lw)segment de droite
fill_polygon(xs, ys, n)remplissage de polygone (équivalent GeneralPath)
push_clip / pop_clippile de rectangles de clipping ; toutes les primitives sont clippées avant rastérisation, la zone modifiée est contrainte de la même façon
draw_text / draw_text_centered / draw_text_compressed / draw_mixed_texttexte (voir la section polices ci-dessous)
draw_logo_badge(x, y, w, h, bg, fg)fond de badge à angles arrondis
full_aa() / no_aa()commutateur d’anticrénelage à couverture d’un pixel (désactivé par défaut, en miroir de l’amont fullAA() / noAA()) : les bords des remplissages ovale / rectangle arrondi / contour / polygone reçoivent un mélange src-over entier ; le texte reste, lui, toujours anticrénelé par la police TTF de l’hôte

Aides couleurs : mtr::clr(r, g, b) / mtr::clra(r, g, b, a) (opaque / avec alpha), homonymes des fonctions du config.js de JS. Les couleurs semi-transparentes passent par une composition src-over ; les couleurs opaques bénéficient d’un chemin rapide.

Système de polices

Trois niveaux de polices, avec rétrogradation automatique selon la disponibilité :

NiveauCouvertureDescription
Police de ressource (recommandée)tout UTF-8transmettez l’id de police de ressource à Text::font("jsblock:shanghai") ou en troisième paramètre du constructeur de Gfx2D : l’hôte utilise ses vrais FontMetrics et drawString, en conservant crénage, ligne de base, compression, alpha et clipping matériel — le même pipeline de polices que côté JS
Rappel TTF de l’hôteCJK, etc.en l’absence de police de ressource, les glyphes CJK sont rastérisés via le service de l’hôte rasterize_text (miroir du jeu de polices TTF du FontManager de JCM)
Police bitmap intégrée 5×7ASCII / latinmise à l’échelle à la taille demandée, pas proportionnel selon la largeur du glyphe (étroit pour i/l/., large pour m/W) — text_width() et le dessin effectif utilisent les mêmes valeurs, les algorithmes de mise en page JS (taille adaptative, compression à 5 %) se portent tels quels ; sans hôte, les CJK se dégradent en cadre de substitution

Conventions de métriques de la police intégrée : ascent = 0.78 em, descent = 0.22 em (≈ Arial / HanSans) ; pas de 1.0 em pour les CJK pleine chasse. Les métriques des polices de ressource (service de l’hôte measure_font_text) sont calculées sur la police réelle.


Ensuite : mtr::Api et interopérabilité Java — tout ce qui n’est pas dans l’instantané.