绘制与画布
两条路线:PIDS 屏用 Text / Texture / Rect 流式 API 直出;像素级内容(LCD、点阵屏)用 GraphicsTexture + Gfx2D 原生光栅。
屏幕坐标系内容(PIDS 文本行、贴图块)→ Text/Texture:一条绘制记录,宿主用与 JS 完全相同的代码路径回放。像素级内容(车辆 LCD、动态点阵屏、图表)→ GraphicsTexture 自绘像素,经 Gfx2D 光栅化基元,只把脏矩形上传 GPU。
Text / Texture / Rect(PIDS)
<mtr/text.hpp> 是 JS TextWrapper / TextureWrapper / Rect 流式 API 的 1:1 移植——方法名按 C++ 惯例 snake_case,链式调用:
mtr::Text::create()
.text("Tsim Sha Tsui")
.scale(1.725)
.size(100, 9)
.stretch_xy()
.font_mc()
.color(0xFC9700)
.pos(3, rowY)
.draw(ctx);
| Text 方法 | 对应 JS | 说明 |
|---|---|---|
.text(str[, len]) | .text() | UTF-8 文本 |
.scale(s) | .scale() | 整体缩放 |
.left_align() / .center_align() / .right_align() | 同名 | 对齐(默认居中) |
.shadowed() / .bold() / .italic() | 同名 | 字饰 |
.stretch_xy() / .scale_xy() / .wrap_text() / .marquee(duration) | 同名 | 溢出模式(跑马灯可用 .with_marquee_progress(p) 固定进度) |
.font(id) / .font_mc() | 同名 | 资源字体(如 .font("jsblock:shanghai"))/ MC 内置字体 |
.color(argb) | 同名 | 颜色(ARGB) |
.pos(x, y) / .size(w, h) | 同名 | 屏幕坐标与包围盒 |
.z_order(z) / .layer(l) | 同名 | 层级;z=-1 时由上下文自动分配 |
.render_type("LIGHT_1") | .renderType() | 字符串渲染层(QueuedRenderLayer.valueOf 等价;未知名抛异常) |
.natural_light() | .naturalLight() | 取环境光照而非最大渲染亮度 |
| Texture 方法 | 说明 |
|---|---|
.texture(id) | 贴图资源 id(如 "mtr:textures/block/pids.png") |
.uv(u1, v1, u2, v2) | UV 子矩形 |
.color(argb) | 染色 |
.pos(x, y) / .size(w, h) | 位置与尺寸 |
.z_order(z) / .layer(l) | 层级 |
.render_type("LIGHT_1") / .natural_light() | 字符串渲染层 / 环境光照(同 Text) |
Rect(纯色矩形)
mtr::Rect 镜像上游 JS 的 Rect 类(原名 Rectangle,上游 v2.3.0-prerelease.2 起更名,Java 类名不变)——绘制 mtr:white.png 全 UV 四边形并整体染色,是底色块、分隔条、状态色带的最轻途径:
mtr::Rect::create()
.color(0xFF00539F) // 不透明染色(默认 ARGB_WHITE)
.pos(2, rowY + 18)
.size(pids.width() - 4, 1) // 默认 20×20
.draw(ctx);
| Rect 方法 | 说明 |
|---|---|
.color(argb) | 染色(默认白色 ARGB_WHITE) |
.pos(x, y) / .size(w, h) | 位置与尺寸(默认 20×20) |
.render_type("LIGHT_2") | 字符串渲染层(默认 LIGHT_2) |
.natural_light() / .z_order(z) | 环境光照 / 层级(z=-1 自动分配) |
配套上下文方法(PidsContext):
ctx.set_auto_z_ordering(bool)—— 自动 z 序(默认开)ctx.set_z_order_step(step)—— 自动 z 序步长(默认 0.0002)
渲染层(mtr::Matrices 无关的 2D 层枚举 MtrRenderLayer):MTR_LAYER_TEXT(文字)、MTR_LAYER_LIGHT(发光体)、MTR_LAYER_EXTERIOR / MTR_LAYER_INTERIOR(外观 / 内饰),与 MTR 的 QueuedRenderLayer 一致。
Matrices 与模型绘制
<mtr/matrices.hpp> 镜像 JS 的全局 Matrices:变换压栈、compile() 把当前栈编译为一组矩阵写进帧矩阵内存区:
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); // 车辆
ctx.draw_model(model_handle, &m); // 装饰 / 电梯
| 成员 | 说明 |
|---|---|
translate(x, y, z) / scale(x, y, z) | 平移 / 缩放 |
rotate_x / rotate_y / rotate_z(radians) | 旋转(弧度) |
rotate_x_degrees / …_y… / …_z…(deg) | 旋转(角度) |
compile() | 把当前姿态编译进帧(返回矩阵组索引;传给 draw 调用) |
车辆上下文另有 draw_bogie_model(model, car, bogie, matrices)(转向架)、play_car_sound(sound, car, x, y, z, volume, pitch) 与 play_ann_sound(sound, volume, pitch)(仅乘坐玩家可闻的报站广播)。
模型句柄来自宿主服务:ctx.host()->acquire_model(user, "namespace:path/model.json") 获取、release_model(user, handle) 释放;acquire_quad_model(...) 可用 4 个模型空间顶点 + UV 构建宿主持有的贴图面片(把 GraphicsTexture 变成可贴到车厢上的屏幕)。细节见 C ABI 与宿主服务。
GraphicsTexture(像素画布)
<mtr/gfx.hpp> 的 GraphicsTexture 把像素缓冲留在你的模块里,游戏侧只收脏矩形上传——像素数据不逐帧跨边界搬运:
struct LcdState {
mtr::GraphicsTexture tex; // 建议放 State:与实例同生命周期
};
void create(mtr::VehicleContext& ctx, LcdState& s, const mtr::Vehicle&) {
s.tex.create(ctx.input(), 512, 128); // 同时向宿主注册 GPU 纹理
// 把纹理贴到车厢:host->acquire_quad_model(...) → draw_car_model(...)
}
void render(mtr::VehicleContext& ctx, LcdState& s, const mtr::Vehicle& vehicle) {
// 1. 画像素(Gfx2D 或直接 set_pixel)
// 2. 只上传变化区域:
s.tex.upload(ctx.frame());
}
| 成员 | 说明 |
|---|---|
create(input, w, h) | 分配像素缓冲(BGRA8 行主序)并在宿主侧创建 GPU 纹理,返回后整体视为脏 |
pixels() / pixels32() | 直接像素指针(uint8_t* / uint32_t*) |
set_pixel(x, y, argb) | 写单像素并自动累计脏区 |
upload(frame) | 把当前脏矩形写入帧像素内存区(一条 JCM_DRAW_TEXTURE_UPLOAD 记录),随后脏区复位 |
last_upload_bytes() | 上次上传的字节数——性能观测利器 |
close(input) / 析构 | 释放宿主 GPU 纹理 |
handle() / width() / height() / texture_bytes() | 宿主纹理句柄与尺寸信息 |
upload() 只搬运自上次上传以来变化过的包围盒。静态背景画一次即可;每帧只动需要动的条带(跑马灯、闪烁段、进度条)。车辆 LCD 闪烁/轮播如果只改一小条,上传量就只有那一小条。
同头文件还提供 LcdCanvas——基于内置 5×7 点阵字体的 LCD 画笔(可配亮/灭色),适合经典点阵屏。
Gfx2D 软件光栅器
<mtr/gfx2d.hpp> 把 java.awt.Graphics2D 的常用子集实现为确定性软件光栅器,直接画在 GraphicsTexture 上。JS 资源包里的 fillRect / fillOval / drawLine / GeneralPath / clip / drawString 逻辑可以机械地移植过来:
mtr::Gfx2D g(s.tex, ctx.host(), "jsblock:shanghai"); // 画布 + 宿主字体
g.set_color(mtr::clr(12, 28, 44)); // 深底
g.fill_rect(0, 0, W, H);
g.push_clip(8, 8, W - 16, 40); // 裁剪栈
g.set_color(mtr::clra(255, 205, 66, 255));
g.draw_text_centered(W / 2, 30, 18.0, "下一站 Next");
g.pop_clip();
| 基元 | 说明 |
|---|---|
fill_rect / fill_round_rect / draw_round_rect | 矩形(圆角弧半径参数) |
fill_oval / draw_oval | 椭圆填充 / 描边(线宽) |
draw_line(x1, y1, x2, y2, lw) | 线段 |
fill_polygon(xs, ys, n) | 多边形填充(GeneralPath 等价) |
push_clip / pop_clip | 裁剪框栈;所有基元先裁剪再光栅,脏区同样被裁剪约束 |
draw_text / draw_text_centered / draw_text_compressed / draw_mixed_text | 文字(见下节字体) |
draw_logo_badge(x, y, w, h, bg, fg) | 圆角徽章底 |
full_aa() / no_aa() | 一像素覆盖率抗锯齿开关(默认关,镜像上游 fullAA() / noAA()):椭圆 / 圆角矩形 / 描边 / 多边形填充的边缘做整数 src-over 混合;文字恒为宿主 TTF 抗锯齿,不受影响 |
颜色助手:mtr::clr(r, g, b) / mtr::clra(r, g, b, a)(不透明 / 带 alpha),对应 JS config.js 里的同名函数。半透明颜色走 src-over 合成;不透明颜色有快速路径。
字体体系
三层字体,按可用性自动降级:
| 层级 | 覆盖 | 说明 |
|---|---|---|
| 资源字体(首选) | 全 UTF-8 | 给 Text::font("jsblock:shanghai")、Gfx2D 构造第三参传字体资源 id:用宿主的真实 FontMetrics 与 drawString,保留字距、基线、压缩、alpha 与设备裁剪——和 JS 端同一字体管线 |
| 宿主 TTF 回调 | CJK 等 | 无资源字体时,CJK 字形经宿主服务 rasterize_text 栅格化(镜像 JCM FontManager 的 TTF 集) |
| 内置 5×7 位图字体 | ASCII / 拉丁 | 按请求尺寸缩放、按字形宽窄给比例步进(窄 i/l/.,宽 m/W)——text_width() 与实际绘制用同一组数字,JS 的自适应字号 / 5% 压缩布局算法可原样移植;CJK 无宿主时退化为占位框 |
内置字体的度量约定:ascent = 0.78 em、descent = 0.22 em(≈ Arial / HanSans);CJK 全角 1.0 em 步进。资源字体的度量(measure_font_text 宿主服务)按真实字体计算。
接下来:mtr::Api 与 Java 互操作 —— 快照之外的一切。