Dessin et canevas
Deux voies : les écrans PIDS passent par l’API en chaîne Text / Texture / Rect ; le contenu au niveau pixel (LCD, écrans à matrice de points) passe par la rastérisation native GraphicsTexture + Gfx2D.
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 JS | Description |
|---|---|---|
.text(str[, len]) | .text() | texte UTF-8 |
.scale(s) | .scale() | mise à l’échelle globale |
.left_align() / .center_align() / .right_align() | homonyme | alignement (centré par défaut) |
.shadowed() / .bold() / .italic() | homonyme | décorations du texte |
.stretch_xy() / .scale_xy() / .wrap_text() / .marquee(duration) | homonyme | modes de débordement (le défilement marquee peut figer la progression via .with_marquee_progress(p)) |
.font(id) / .font_mc() | homonyme | police de ressource (par ex. .font("jsblock:shanghai")) / police intégrée de MC |
.color(argb) | homonyme | couleur (ARGB) |
.pos(x, y) / .size(w, h) | homonyme | coordonnées écran et boîte englobante |
.z_order(z) / .layer(l) | homonyme | hié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 Texture | Description |
|---|---|
.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 Rect | Description |
|---|---|
.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
| Membre | Description |
|---|---|
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());
}
| Membre | Description |
|---|---|
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) / destructeur | libère la texture GPU de l’hôte |
handle() / width() / height() / texture_bytes() | handle de texture côté hôte et informations de dimensions |
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();
| Primitive | Description |
|---|---|
fill_rect / fill_round_rect / draw_round_rect | rectangles (paramètre de rayon des angles arrondis) |
fill_oval / draw_oval | ellipse 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_clip | pile 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_text | texte (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é :
| Niveau | Couverture | Description |
|---|---|---|
| Police de ressource (recommandée) | tout UTF-8 | transmettez 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ôte | CJK, 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×7 | ASCII / latin | mise à 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é.