2V0-f: run the real system.py app loop in Godot
- system.py runs app.Loop() on the script fiber; each UIUpdate is one CPythonApplication::Process(). Logo -> Login, popup ESC/click and app.Exit teardown (launcher cleared first, as in 40250 Main) work. - Image commands are base-texture quads with UVs (a .sub shares its parent texture); ImageInstance/ExpandedImageInstance OnRender and SetRenderingRect ported verbatim, so scale/rotation/rendering rect are drawn. - CGraphicBase::GetColor / grp.GenerateColor ported (ThinBoard background). - port.app_loop test, python_host_test app-loop checks, MT_TEST_MODE=python_ui. - TODO: .fnt/CGraphicText font pipeline, ExpandedImage blend modes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
5590714b16
commit
141206cb69
@@ -4,6 +4,7 @@
|
||||
#include "EterLib/GrpBase.h"
|
||||
|
||||
#include "../PlatformStub.h"
|
||||
#include <cstring>
|
||||
#include "UIRenderCommands.h"
|
||||
|
||||
auto CGraphicBase::GetAvailableTextureMemory() -> DWORD
|
||||
@@ -171,10 +172,20 @@ auto CGraphicBase::SetScreenEffectFlashing(float, const D3DXCOLOR &) -> void
|
||||
MT_PLATFORM_STUB();
|
||||
}
|
||||
|
||||
auto CGraphicBase::GetColor(float, float, float, float) -> DWORD
|
||||
// 40250 GrpBase.cpp:458: the bytes B, G, R, A of a little-endian D3DCOLOR.
|
||||
auto CGraphicBase::GetColor(float r, float g, float b, float a) -> DWORD
|
||||
{
|
||||
MT_PLATFORM_STUB();
|
||||
return mt_platform_stub_return<DWORD>();
|
||||
BYTE argb[4] =
|
||||
{
|
||||
(BYTE) (255.0f * b),
|
||||
(BYTE) (255.0f * g),
|
||||
(BYTE) (255.0f * r),
|
||||
(BYTE) (255.0f * a)
|
||||
};
|
||||
|
||||
DWORD color;
|
||||
std::memcpy(&color, argb, sizeof(color));
|
||||
return color;
|
||||
}
|
||||
|
||||
auto CGraphicBase::GetFaceCount() -> DWORD
|
||||
|
||||
@@ -5,6 +5,9 @@
|
||||
|
||||
#include "../PlatformStub.h"
|
||||
#include "EterBase/CRC32.h"
|
||||
#include "UIRenderCommands.h"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
auto CGraphicExpandedImageInstance::Type() -> DWORD
|
||||
{
|
||||
@@ -19,7 +22,7 @@ CGraphicExpandedImageInstance::CGraphicExpandedImageInstance()
|
||||
|
||||
CGraphicExpandedImageInstance::~CGraphicExpandedImageInstance()
|
||||
{
|
||||
MT_PLATFORM_STUB();
|
||||
Destroy();
|
||||
}
|
||||
|
||||
auto CGraphicExpandedImageInstance::Destroy() -> void
|
||||
@@ -53,10 +56,19 @@ auto CGraphicExpandedImageInstance::SetScale(float x, float y) -> void
|
||||
m_v2Scale = D3DXVECTOR2(x, y);
|
||||
}
|
||||
|
||||
auto CGraphicExpandedImageInstance::SetRenderingRect(float left, float top, float right, float bottom) -> void
|
||||
// In 40250, the arguments are fractions of the image size (uiToolTip gauges, SetPercentage).
|
||||
auto CGraphicExpandedImageInstance::SetRenderingRect(float fLeft, float fTop, float fRight, float fBottom) -> void
|
||||
{
|
||||
m_RenderingRect = {static_cast<LONG>(left), static_cast<LONG>(top),
|
||||
static_cast<LONG>(right), static_cast<LONG>(bottom)};
|
||||
if (IsEmpty())
|
||||
return;
|
||||
|
||||
float fWidth = float(GetWidth());
|
||||
float fHeight = float(GetHeight());
|
||||
|
||||
m_RenderingRect.left = fWidth * fLeft;
|
||||
m_RenderingRect.top = fHeight * fTop;
|
||||
m_RenderingRect.right = fWidth * fRight;
|
||||
m_RenderingRect.bottom = fHeight * fBottom;
|
||||
}
|
||||
|
||||
auto CGraphicExpandedImageInstance::SetRenderingMode(int mode) -> void
|
||||
@@ -74,9 +86,68 @@ auto CGraphicExpandedImageInstance::Initialize() -> void
|
||||
m_iRenderingMode = RENDERING_MODE_NORMAL;
|
||||
}
|
||||
|
||||
// 40250 GrpExpandedImageInstance.cpp:29-142. The vertex and texture-coordinate arithmetic is
|
||||
// verbatim; the D3D stream/blend-state calls become one UIRenderCommand with the four vertices.
|
||||
auto CGraphicExpandedImageInstance::OnRender() -> void
|
||||
{
|
||||
CGraphicImageInstance::OnRender();
|
||||
CGraphicImage * pImage = m_roImage.GetPointer();
|
||||
CGraphicTexture * pTexture = pImage->GetTexturePointer();
|
||||
if (pTexture->GetWidth() <= 0 || pTexture->GetHeight() <= 0)
|
||||
return;
|
||||
|
||||
const RECT& c_rRect = pImage->GetRectReference();
|
||||
float texReverseWidth = 1.0f / float(pTexture->GetWidth());
|
||||
float texReverseHeight = 1.0f / float(pTexture->GetHeight());
|
||||
float su = (c_rRect.left - m_RenderingRect.left) * texReverseWidth;
|
||||
float sv = (c_rRect.top - m_RenderingRect.top) * texReverseHeight;
|
||||
float eu = (c_rRect.left + m_RenderingRect.right + (c_rRect.right-c_rRect.left)) * texReverseWidth;
|
||||
float ev = (c_rRect.top + m_RenderingRect.bottom + (c_rRect.bottom-c_rRect.top)) * texReverseHeight;
|
||||
|
||||
float x[4], y[4];
|
||||
for (int i = 0; i < 4; ++i)
|
||||
{
|
||||
x[i] = m_v2Position.x-0.5f;
|
||||
y[i] = m_v2Position.y-0.5f;
|
||||
}
|
||||
|
||||
if (0.0f == m_fRotation)
|
||||
{
|
||||
float fimgWidth = float(pImage->GetWidth()) * m_v2Scale.x;
|
||||
float fimgHeight = float(pImage->GetHeight()) * m_v2Scale.y;
|
||||
|
||||
x[0] -= m_RenderingRect.left;
|
||||
y[0] -= m_RenderingRect.top;
|
||||
x[1] += fimgWidth + m_RenderingRect.right;
|
||||
y[1] -= m_RenderingRect.top;
|
||||
x[2] -= m_RenderingRect.left;
|
||||
y[2] += fimgHeight + m_RenderingRect.bottom;
|
||||
x[3] += fimgWidth + m_RenderingRect.right;
|
||||
y[3] += fimgHeight + m_RenderingRect.bottom;
|
||||
// PORT: 40250 flips D3DRS_CULLMODE for a mirrored scale; the Godot canvas has no culling.
|
||||
}
|
||||
else
|
||||
{
|
||||
float fimgHalfWidth = float(pImage->GetWidth())/2.0f * m_v2Scale.x;
|
||||
float fimgHalfHeight = float(pImage->GetHeight())/2.0f * m_v2Scale.y;
|
||||
|
||||
for (int i = 0; i < 4; ++i)
|
||||
{
|
||||
x[i] += m_v2Origin.x;
|
||||
y[i] += m_v2Origin.y;
|
||||
}
|
||||
|
||||
float fRadian = D3DXToRadian(m_fRotation);
|
||||
x[0] += (-fimgHalfWidth*cosf(fRadian)) - (-fimgHalfHeight*sinf(fRadian));
|
||||
y[0] += (-fimgHalfWidth*sinf(fRadian)) + (-fimgHalfHeight*cosf(fRadian));
|
||||
x[1] += (+fimgHalfWidth*cosf(fRadian)) - (-fimgHalfHeight*sinf(fRadian));
|
||||
y[1] += (+fimgHalfWidth*sinf(fRadian)) + (-fimgHalfHeight*cosf(fRadian));
|
||||
x[2] += (-fimgHalfWidth*cosf(fRadian)) - (+fimgHalfHeight*sinf(fRadian));
|
||||
y[2] += (-fimgHalfWidth*sinf(fRadian)) + (+fimgHalfHeight*cosf(fRadian));
|
||||
x[3] += (+fimgHalfWidth*cosf(fRadian)) - (+fimgHalfHeight*sinf(fRadian));
|
||||
y[3] += (+fimgHalfWidth*sinf(fRadian)) + (+fimgHalfHeight*cosf(fRadian));
|
||||
}
|
||||
|
||||
UIRenderAddImage(pTexture, x, y, su, sv, eu, ev, UIRenderColor(m_DiffuseColor), m_iRenderingMode);
|
||||
}
|
||||
|
||||
auto CGraphicExpandedImageInstance::OnSetImagePointer() -> void
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
#include "../PlatformStub.h"
|
||||
#include "UIRenderCommands.h"
|
||||
#include "EterBase/CRC32.h"
|
||||
#include <algorithm>
|
||||
|
||||
auto CGraphicImageInstance::Type() -> DWORD
|
||||
{
|
||||
@@ -27,12 +26,15 @@ CGraphicImageInstance::CGraphicImageInstance()
|
||||
|
||||
CGraphicImageInstance::~CGraphicImageInstance()
|
||||
{
|
||||
MT_PLATFORM_STUB();
|
||||
Destroy();
|
||||
}
|
||||
|
||||
// 40250 Destroy(): drop the image reference, then Initialize() (white, at the origin).
|
||||
auto CGraphicImageInstance::Destroy() -> void
|
||||
{
|
||||
m_roImage.Clear();
|
||||
m_DiffuseColor = D3DXCOLOR(1, 1, 1, 1);
|
||||
m_v2Position = D3DXVECTOR2(0, 0);
|
||||
}
|
||||
|
||||
auto CGraphicImageInstance::Render() -> void
|
||||
@@ -78,14 +80,13 @@ auto CGraphicImageInstance::GetHeight() -> int
|
||||
|
||||
auto CGraphicImageInstance::GetTexturePointer() -> CGraphicTexture *
|
||||
{
|
||||
MT_PLATFORM_STUB();
|
||||
return mt_platform_stub_return<CGraphicTexture *>();
|
||||
CGraphicImage* pkImage = m_roImage.GetPointer();
|
||||
return pkImage ? pkImage->GetTexturePointer() : NULL;
|
||||
}
|
||||
|
||||
auto CGraphicImageInstance::GetTextureReference() const -> const CGraphicTexture &
|
||||
{
|
||||
MT_PLATFORM_STUB();
|
||||
return mt_platform_stub_return<const CGraphicTexture &>();
|
||||
return m_roImage->GetTextureReference();
|
||||
}
|
||||
|
||||
auto CGraphicImageInstance::GetGraphicImagePointer() -> CGraphicImage *
|
||||
@@ -104,20 +105,31 @@ auto CGraphicImageInstance::Initialize() -> void
|
||||
MT_PLATFORM_STUB();
|
||||
}
|
||||
|
||||
// 40250 GrpImageInstance.cpp:44-96: the image's rect inside its texture (a .sub shares its parent's)
|
||||
// becomes the texture coordinates of one quad.
|
||||
auto CGraphicImageInstance::OnRender() -> void
|
||||
{
|
||||
CGraphicImage* image = m_roImage.GetPointer();
|
||||
if (!image) return;
|
||||
auto channel = [](float value) -> std::uint32_t {
|
||||
return static_cast<std::uint32_t>(std::clamp(value, 0.0f, 1.0f) * 255.0f + 0.5f);
|
||||
};
|
||||
const std::uint32_t color = (channel(m_DiffuseColor.a) << 24) |
|
||||
(channel(m_DiffuseColor.r) << 16) | (channel(m_DiffuseColor.g) << 8) |
|
||||
channel(m_DiffuseColor.b);
|
||||
UIRenderCommand command{UIRenderCommand::Image, m_v2Position.x, m_v2Position.y,
|
||||
m_v2Position.x + image->GetWidth(), m_v2Position.y + image->GetHeight(), color};
|
||||
command.text = image->GetFileName();
|
||||
UIRenderAdd(std::move(command));
|
||||
CGraphicImage * pImage = m_roImage.GetPointer();
|
||||
CGraphicTexture * pTexture = pImage->GetTexturePointer();
|
||||
if (pTexture->GetWidth() <= 0 || pTexture->GetHeight() <= 0)
|
||||
return;
|
||||
|
||||
float fimgWidth = pImage->GetWidth();
|
||||
float fimgHeight = pImage->GetHeight();
|
||||
|
||||
const RECT& c_rRect = pImage->GetRectReference();
|
||||
float texReverseWidth = 1.0f / float(pTexture->GetWidth());
|
||||
float texReverseHeight = 1.0f / float(pTexture->GetHeight());
|
||||
float su = c_rRect.left * texReverseWidth;
|
||||
float sv = c_rRect.top * texReverseHeight;
|
||||
float eu = (c_rRect.left + (c_rRect.right-c_rRect.left)) * texReverseWidth;
|
||||
float ev = (c_rRect.top + (c_rRect.bottom-c_rRect.top)) * texReverseHeight;
|
||||
|
||||
const float x[4] = {m_v2Position.x-0.5f, m_v2Position.x + fimgWidth-0.5f,
|
||||
m_v2Position.x-0.5f, m_v2Position.x + fimgWidth-0.5f};
|
||||
const float y[4] = {m_v2Position.y-0.5f, m_v2Position.y-0.5f,
|
||||
m_v2Position.y + fimgHeight-0.5f, m_v2Position.y + fimgHeight-0.5f};
|
||||
UIRenderAddImage(pTexture, x, y, su, sv, eu, ev, UIRenderColor(m_DiffuseColor));
|
||||
}
|
||||
|
||||
auto CGraphicImageInstance::OnSetImagePointer() -> void
|
||||
|
||||
@@ -9,7 +9,33 @@
|
||||
namespace {
|
||||
unsigned little16(const unsigned char* data) { return data[0] | (unsigned(data[1]) << 8); }
|
||||
unsigned little32(const unsigned char* data) { return little16(data) | (little16(data + 2) << 16); }
|
||||
unsigned big16(const unsigned char* data) { return (unsigned(data[0]) << 8) | data[1]; }
|
||||
unsigned big32(const unsigned char* data) { return (unsigned(data[0]) << 24) | (unsigned(data[1]) << 16) | (unsigned(data[2]) << 8) | data[3]; }
|
||||
|
||||
// Walks the JPEG markers to the first SOFn (the frame header holds the size), as D3DX does when it
|
||||
// sniffs a .jpg; login/loading backgrounds (serverlist.jpg, loading/*.jpg) are JPEGs.
|
||||
bool jpeg_size(const unsigned char* data, unsigned size, unsigned* width, unsigned* height)
|
||||
{
|
||||
if (size < 4 || data[0] != 0xFF || data[1] != 0xD8) return false;
|
||||
unsigned pos = 2;
|
||||
while (pos + 4 <= size) {
|
||||
if (data[pos] != 0xFF) return false;
|
||||
const unsigned char marker = data[pos + 1];
|
||||
if (marker == 0xFF) { ++pos; continue; }
|
||||
if (marker == 0xD8 || marker == 0x01 || (marker >= 0xD0 && marker <= 0xD7)) { pos += 2; continue; }
|
||||
const unsigned length = big16(data + pos + 2);
|
||||
if (length < 2) return false;
|
||||
const bool sof = marker >= 0xC0 && marker <= 0xCF && marker != 0xC4 && marker != 0xC8 && marker != 0xCC;
|
||||
if (sof) {
|
||||
if (pos + 9 > size) return false;
|
||||
*height = big16(data + pos + 5);
|
||||
*width = big16(data + pos + 7);
|
||||
return true;
|
||||
}
|
||||
pos += 2 + length;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
CGraphicImageTexture::CGraphicImageTexture()
|
||||
@@ -45,6 +71,9 @@ auto CGraphicImageTexture::CreateFromTexturePointer(const CGraphicTexture *sourc
|
||||
m_width = source->GetWidth();
|
||||
m_height = source->GetHeight();
|
||||
m_bEmpty = source->IsEmpty();
|
||||
// 40250 shares the source's D3D texture; the canvas names textures by pack path instead.
|
||||
if (const auto* image = dynamic_cast<const CGraphicImageTexture*>(source))
|
||||
m_stFileName = image->m_stFileName;
|
||||
}
|
||||
|
||||
auto CGraphicImageTexture::CreateFromDiskFile(const char *, D3DFORMAT, DWORD) -> bool
|
||||
@@ -62,6 +91,11 @@ auto CGraphicImageTexture::CreateFromMemoryFile(UINT size, const void *bytes, D3
|
||||
height = little32(data + 12); width = little32(data + 16);
|
||||
} else if (size >= 24 && std::memcmp(data, "\x89PNG\r\n\x1a\n", 8) == 0) {
|
||||
width = big32(data + 16); height = big32(data + 20);
|
||||
} else if (jpeg_size(data, size, &width, &height)) {
|
||||
} else if (size >= 26 && data[0] == 'B' && data[1] == 'M') {
|
||||
width = little32(data + 18);
|
||||
const int bmp_height = static_cast<int>(little32(data + 22)); // negative: top-down rows
|
||||
height = static_cast<unsigned>(bmp_height < 0 ? -bmp_height : bmp_height);
|
||||
} else if (size >= 18 && (data[2] == 2 || data[2] == 10 || data[2] == 3)) {
|
||||
width = little16(data + 12); height = little16(data + 14);
|
||||
}
|
||||
@@ -82,6 +116,17 @@ auto CGraphicImageTexture::SetFileName(const char *name) -> void
|
||||
m_stFileName = name ? name : "";
|
||||
}
|
||||
|
||||
// PORT: where 40250 binds pTexture->GetD3DTexture(), the canvas gets the pack path of the file the
|
||||
// texture was decoded from (m_stFileName is protected, hence the derived accessor).
|
||||
std::string UIRenderTextureName(const CGraphicTexture* texture)
|
||||
{
|
||||
struct Access : CGraphicImageTexture {
|
||||
static const std::string& name(const CGraphicImageTexture* t) { return t->*(&Access::m_stFileName); }
|
||||
};
|
||||
const auto* image = dynamic_cast<const CGraphicImageTexture*>(texture);
|
||||
return image ? Access::name(image) : std::string();
|
||||
}
|
||||
|
||||
auto CGraphicImageTexture::Lock(int *, void **, int) -> bool
|
||||
{
|
||||
MT_PLATFORM_STUB();
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#include "UIRenderCommands.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace {
|
||||
std::vector<UIRenderCommand> commands;
|
||||
int canvas_width = 0;
|
||||
@@ -34,3 +36,21 @@ 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) {
|
||||
UIRenderCommand command{UIRenderCommand::Image, 0, 0, 0, 0, argb};
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
command.qx[i] = x[i] + 0.5f;
|
||||
command.qy[i] = y[i] + 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));
|
||||
}
|
||||
|
||||
@@ -12,8 +12,32 @@ struct UIRenderCommand {
|
||||
std::uint32_t argb;
|
||||
float clip_x1 = 0, clip_y1 = 0, clip_x2 = 0, clip_y2 = 0;
|
||||
std::string text;
|
||||
// Image only: text is the texture's file, drawn as the quad (vertices 0..3 = TL, TR, BL, BR)
|
||||
// with texture coordinates su,sv..eu,ev; x1..y2 is the quad's bounding box.
|
||||
bool quad = false;
|
||||
float qx[4] = {}, qy[4] = {};
|
||||
float su = 0, sv = 0, eu = 1, ev = 1;
|
||||
int blend = 0; // CGraphicExpandedImageInstance::RENDERING_MODE_*
|
||||
};
|
||||
|
||||
// D3DXCOLOR (0..1 floats) -> the 0xAARRGGBB the commands carry.
|
||||
template <class Color>
|
||||
std::uint32_t UIRenderColor(const Color& color) {
|
||||
auto channel = [](float value) -> std::uint32_t {
|
||||
return static_cast<std::uint32_t>((value < 0 ? 0 : value > 1 ? 1 : value) * 255.0f + 0.5f);
|
||||
};
|
||||
return (channel(color.a) << 24) | (channel(color.r) << 16) | (channel(color.g) << 8) | channel(color.b);
|
||||
}
|
||||
|
||||
class CGraphicTexture;
|
||||
std::string UIRenderTextureName(const CGraphicTexture* texture);
|
||||
|
||||
// A textured quad from 40250's TPDTVertex[4] (TL, TR, BL, BR) positions and texture coordinates.
|
||||
// PORT: D3D8 puts pixel centres on integers, which is why 40250 subtracts 0.5 from every vertex; the
|
||||
// Godot canvas puts them on .5, so the quad is shifted back by +0.5 here.
|
||||
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 = 0);
|
||||
|
||||
void UIRenderSetSize(int width, int height);
|
||||
void UIRenderGetSize(unsigned* width, unsigned* height);
|
||||
void UIRenderBeginFrame();
|
||||
|
||||
Reference in New Issue
Block a user