layout: restructure into src/ tests/ android/ scripts/ tools/
- extension/src/{port,platform,codepage} -> src/; native_render -> src/host
(+ dxt, shaders/); libgr2 -> src/gr2; extension/third_party -> third_party
- extension/tests -> tests/port, libgr2/tests -> tests/gr2
- android-native -> android (build.sh, push-client.sh moved in)
- script -> scripts; tools/40250 -> tools/server; oracle -> tools/granny-oracle;
perf tools -> tools/perf
- all build trees under build/ (native, release, android, port-gate)
- xrender:: CMake aliases -> mt::; port-map ledger impl paths rewritten
No code changes. ctest 15/15, port_gate macos+android PASS, port_map check 0 errors.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
70710477cf
commit
a46093104c
@@ -0,0 +1,147 @@
|
||||
#include "EterLib/StdAfx.h"
|
||||
#include "UIRenderCommands.h"
|
||||
#include "RenderCommands3D.h"
|
||||
#include "EterLib/StateManager.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace {
|
||||
struct Point { float x, y; };
|
||||
std::vector<Point> clip_polygon(std::vector<Point> polygon, int edge, float bound) {
|
||||
std::vector<Point> result;
|
||||
if (polygon.empty()) return result;
|
||||
auto inside = [edge, bound](Point p) {
|
||||
return edge == 0 ? p.x >= bound : edge == 1 ? p.x <= bound
|
||||
: edge == 2 ? p.y >= bound : p.y <= bound;
|
||||
};
|
||||
auto intersect = [edge, bound](Point a, Point b) {
|
||||
const float t = edge < 2 ? (bound - a.x) / (b.x - a.x)
|
||||
: (bound - a.y) / (b.y - a.y);
|
||||
return Point{a.x + t * (b.x - a.x), a.y + t * (b.y - a.y)};
|
||||
};
|
||||
Point previous = polygon.back();
|
||||
for (Point current : polygon) {
|
||||
const bool was_inside = inside(previous), is_inside = inside(current);
|
||||
if (was_inside != is_inside) result.push_back(intersect(previous, current));
|
||||
if (is_inside) result.push_back(current);
|
||||
previous = current;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
std::vector<UIRenderCommand> commands;
|
||||
std::uint64_t frame_id = 0;
|
||||
int canvas_width = 0;
|
||||
int canvas_height = 0;
|
||||
float clip_x1 = 0, clip_y1 = 0, clip_x2 = 0, clip_y2 = 0;
|
||||
float saved_x1 = 0, saved_y1 = 0, saved_x2 = 0, saved_y2 = 0;
|
||||
float offset_x = 0;
|
||||
}
|
||||
|
||||
void UIRenderSetOffsetX(float x) { offset_x = x; }
|
||||
float UIRenderOffsetX() { return offset_x; }
|
||||
|
||||
void UIRenderSetSize(int width, int height) { canvas_width = width; canvas_height = height; }
|
||||
void UIRenderGetSize(unsigned* width, unsigned* height) {
|
||||
if (width) *width = canvas_width;
|
||||
if (height) *height = canvas_height;
|
||||
}
|
||||
void UIRenderBeginFrame() {
|
||||
++frame_id;
|
||||
commands.clear();
|
||||
clip_x1 = clip_y1 = 0;
|
||||
clip_x2 = canvas_width;
|
||||
clip_y2 = canvas_height;
|
||||
}
|
||||
std::uint64_t UIRenderFrameId() { return frame_id; }
|
||||
void UIRenderAdd(UIRenderCommand command) {
|
||||
if (offset_x != 0) {
|
||||
command.x1 += offset_x;
|
||||
command.x2 += offset_x;
|
||||
if (command.quad)
|
||||
for (float& x : command.qx) x += offset_x;
|
||||
}
|
||||
command.clip_x1 = clip_x1; command.clip_y1 = clip_y1;
|
||||
command.clip_x2 = clip_x2; command.clip_y2 = clip_y2;
|
||||
command.behind_3d = Render3DDraws().empty();
|
||||
if (command.kind == UIRenderCommand::Bar && command.quad) {
|
||||
// Cooldown fans are triangles. Clip their geometry before either renderer consumes it.
|
||||
std::vector<Point> polygon{{command.qx[0], command.qy[0]},
|
||||
{command.qx[1], command.qy[1]},
|
||||
{command.qx[2], command.qy[2]}};
|
||||
const float bounds[4] = {clip_x1, clip_x2, clip_y1, clip_y2};
|
||||
for (int edge = 0; edge < 4; ++edge)
|
||||
polygon = clip_polygon(std::move(polygon), edge, bounds[edge]);
|
||||
for (std::size_t i = 1; i + 1 < polygon.size(); ++i) {
|
||||
UIRenderCommand piece = command;
|
||||
const Point tri[3] = {polygon[0], polygon[i], polygon[i + 1]};
|
||||
const float area = (tri[1].x - tri[0].x) * (tri[2].y - tri[0].y) -
|
||||
(tri[1].y - tri[0].y) * (tri[2].x - tri[0].x);
|
||||
if (area == 0.0f) continue;
|
||||
for (int k = 0; k < 4; ++k) {
|
||||
piece.qx[k] = tri[k < 3 ? k : 2].x;
|
||||
piece.qy[k] = tri[k < 3 ? k : 2].y;
|
||||
}
|
||||
piece.x1 = std::min({tri[0].x, tri[1].x, tri[2].x});
|
||||
piece.y1 = std::min({tri[0].y, tri[1].y, tri[2].y});
|
||||
piece.x2 = std::max({tri[0].x, tri[1].x, tri[2].x});
|
||||
piece.y2 = std::max({tri[0].y, tri[1].y, tri[2].y});
|
||||
commands.push_back(std::move(piece));
|
||||
}
|
||||
return;
|
||||
}
|
||||
commands.push_back(command);
|
||||
}
|
||||
const std::vector<UIRenderCommand>& UIRenderCommands() { return commands; }
|
||||
void UIRenderSetClip(float x, float y, float width, float height) {
|
||||
saved_x1 = clip_x1; saved_y1 = clip_y1; saved_x2 = clip_x2; saved_y2 = clip_y2;
|
||||
clip_x1 = x + offset_x; clip_y1 = y;
|
||||
clip_x2 = x + offset_x + width; clip_y2 = y + height;
|
||||
}
|
||||
void UIRenderRestoreClip() {
|
||||
clip_x1 = saved_x1; clip_y1 = saved_y1;
|
||||
clip_x2 = saved_x2; clip_y2 = saved_y2;
|
||||
}
|
||||
|
||||
void UIRenderAddImage(const CGraphicTexture* texture, const float x[4], const float y[4],
|
||||
float su, float sv, float eu, float ev, std::uint32_t argb, int blend) {
|
||||
// 40250 draws the image quad through the device: the UI pass's world transform places it
|
||||
// (identity except where a caller sets one, e.g. CPythonMiniMap::RenderAtlas's window offset) and a
|
||||
// stage-0 MODULATE with D3DTA_TFACTOR tints it (the atlas's NPC/warp/waypoint marks).
|
||||
D3DXMATRIX world;
|
||||
D3DXMatrixIdentity(&world);
|
||||
if (CStateManager* state = CStateManager::InstancePtr()) {
|
||||
state->GetTransform(D3DTS_WORLD, &world);
|
||||
DWORD op = 0, arg1 = 0, arg2 = 0;
|
||||
state->GetTextureStageState(0, D3DTSS_COLOROP, &op);
|
||||
state->GetTextureStageState(0, D3DTSS_COLORARG1, &arg1);
|
||||
state->GetTextureStageState(0, D3DTSS_COLORARG2, &arg2);
|
||||
if (op == D3DTOP_MODULATE && (arg1 == D3DTA_TFACTOR || arg2 == D3DTA_TFACTOR)) {
|
||||
// TFACTOR x TEXTURE drops the vertex colour; TFACTOR x DIFFUSE scales it. The canvas
|
||||
// multiplies the texture in.
|
||||
const std::uint32_t factor = state->GetRenderState(D3DRS_TEXTUREFACTOR) & 0x00FFFFFFu;
|
||||
const DWORD other = arg1 == D3DTA_TFACTOR ? arg2 : arg1;
|
||||
if (other == D3DTA_DIFFUSE) {
|
||||
std::uint32_t tinted = argb & 0xFF000000u;
|
||||
for (int shift = 0; shift < 24; shift += 8)
|
||||
tinted |= ((((argb >> shift) & 255) * ((factor >> shift) & 255) + 127) / 255) << shift;
|
||||
argb = tinted;
|
||||
} else {
|
||||
argb = (argb & 0xFF000000u) | factor;
|
||||
}
|
||||
}
|
||||
}
|
||||
UIRenderCommand command{UIRenderCommand::Image, 0, 0, 0, 0, argb};
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
command.qx[i] = x[i] * world._11 + y[i] * world._21 + world._41 + 0.5f;
|
||||
command.qy[i] = x[i] * world._12 + y[i] * world._22 + world._42 + 0.5f;
|
||||
}
|
||||
command.x1 = std::min({command.qx[0], command.qx[1], command.qx[2], command.qx[3]});
|
||||
command.y1 = std::min({command.qy[0], command.qy[1], command.qy[2], command.qy[3]});
|
||||
command.x2 = std::max({command.qx[0], command.qx[1], command.qx[2], command.qx[3]});
|
||||
command.y2 = std::max({command.qy[0], command.qy[1], command.qy[2], command.qy[3]});
|
||||
command.text = UIRenderTextureName(texture);
|
||||
command.quad = true;
|
||||
command.su = su; command.sv = sv; command.eu = eu; command.ev = ev;
|
||||
command.blend = blend;
|
||||
UIRenderAdd(std::move(command));
|
||||
}
|
||||
Reference in New Issue
Block a user