HUD phase 1: full dpl2d API recovered + opcode-faithful 2D layer rework
Every dpl2d_ recorder in the binary self-identifies via its debug-name string
(part_010.c 0x487f34..0x4888c0) -- the complete API + opcode map is recorded
in phases/phase-02-dpl2d-reticle.md: point sets (2/3), closed polylines (4/5),
open line strips (6/7), AddPoint (8), AddCircle (9), SetColor (0xF),
Set/ConcatMatrix (0x10/0x11, 2x3 affine), Push/PopState (0x12/0x13),
SetLineWidth (0x15), CallDisplayList (nested glyphs), FullScreenClipRegion.
dpl2d.cpp reworked to that model: command-stream recorder (open-primitive
vertex runs, state commands, nested list calls) + a recursive ExecuteList
rasteriser (XYZRHW points/strips/loops/circles, 2x3 transform + state stacks,
save/restore). CORRECTION: the old "PushMatrix/MoveTo/PopMatrix" trio was a
misreading of OpenPolypoint/AddPoint/ClosePolypoint ("draw a point") -- kept
as aliases mapping to the true semantics.
Reticle findings recorded: the ctor (@004cc40c) builds ~15 display lists (the
dotted-cross reticle, tick ladders via FUN_004cd938, side arrows, arcs) plus a
3D marker chain and the PNAME1-8.bgf pip meshes (all ship). Next: transcribe
ctor+Execute, wire the 1996 caller's AddWeapon arguments, hook the draw.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
56f02b2176
commit
1cd8ca1a80
+378
-161
@@ -4,21 +4,31 @@
|
||||
//---------------------------------------------------------------------------//
|
||||
// libDPL 2D vector display-list layer, re-hosted over Direct3D 9. //
|
||||
// //
|
||||
// Replaces the inert dpl2d_* stubs that used to live in btstubs.cpp. The //
|
||||
// recorder calls (dpl2d_NewDisplayList / Begin / SetColor / Circle / //
|
||||
// PushMatrix / PopMatrix / MoveTo / End / Compile -- prototypes in //
|
||||
// btl4vid.hpp) build a small command list; dpl2d_ExecuteList (dpl2d.hpp) //
|
||||
// draws a compiled list with screen-space 2D primitives. //
|
||||
// OPCODE-FAITHFUL rework (task #35 / phase-02): every dpl2d_ recorder in the //
|
||||
// binary self-identifies via its debug-name string (part_010.c 0x487f34.. //
|
||||
// 0x4888c0) -- the full API + opcode map is recorded in //
|
||||
// phases/phase-02-dpl2d-reticle.md. This TU implements that model: //
|
||||
// //
|
||||
// The recorded calls match what the reconstructed reticle/PIP builder in //
|
||||
// btl4vid.cpp actually issues, e.g. //
|
||||
// dpl2d_Begin(list, 1); //
|
||||
// dpl2d_SetColor(list, r, g, b); //
|
||||
// dpl2d_Circle(list, x, y, 0.012f, 1); // filled blip //
|
||||
// dpl2d_SetColor(list, 0, 0, 0); //
|
||||
// dpl2d_Circle(list, x, y, 0.014f, 0); // dark outline ring //
|
||||
// dpl2d_PushMatrix(list); dpl2d_MoveTo(list, x, y); dpl2d_PopMatrix(); //
|
||||
// dpl2d_End(list); dpl2d_Compile(list); //
|
||||
// OpenDisplayList(mode) .. CloseDisplayList .. FlushDisplayList //
|
||||
// AddOpenPolypoint(2)/AddClosePolypoint(3) -- a POINT set //
|
||||
// AddOpenPolyline(4)/AddClosePolyline(5) -- a CLOSED loop //
|
||||
// AddOpenLines(6)/AddCloseLines(7) -- an OPEN line strip //
|
||||
// AddPoint(8, x, y) -- vertex of the open prim //
|
||||
// AddCircle(9, x, y, r, fill) //
|
||||
// AddSetColor(0xF, r, g, b) //
|
||||
// AddSetMatrix(0x10, 2x3) / AddConcatMatrix(0x11, 2x3) //
|
||||
// AddPushState(0x12) / AddPopState(0x13) //
|
||||
// AddSetLineWidth(0x15, w) //
|
||||
// AddCallDisplayList(list) -- nested glyph reuse //
|
||||
// AddFullScreenClipRegion -- clip reset (no-op here) //
|
||||
// //
|
||||
// Coordinates are normalised view space (the reticle builder's constants //
|
||||
// are fractions like 0.04/0.1/0.35); dpl2d_ExecuteList maps them to the //
|
||||
// current viewport and draws XYZRHW primitives with state save/restore. //
|
||||
// //
|
||||
// NOTE the OLD trio dpl2d_PushMatrix/MoveTo/PopMatrix was a misreading of //
|
||||
// AddOpenPolypoint/AddPoint/AddClosePolypoint ("draw a point"); the aliases //
|
||||
// below keep old callers compiling while mapping to the true semantics. //
|
||||
//===========================================================================//
|
||||
|
||||
#include <bt.hpp> // Scalar + engine prelude
|
||||
@@ -35,11 +45,6 @@
|
||||
#include <vector>
|
||||
#include <math.h>
|
||||
|
||||
//===========================================================================//
|
||||
// Pre-transformed 2D vertex -- mirrors MUNGA_L4/L4D3D.h L4VERTEX_2D and its
|
||||
// L4VERTEX_2D_FVF, redeclared locally so this TU does not need the MUNGA_L4
|
||||
// source dir on the include path (the engine lib uses the identical layout).
|
||||
//===========================================================================//
|
||||
namespace
|
||||
{
|
||||
struct Vertex2D
|
||||
@@ -49,7 +54,7 @@ namespace
|
||||
};
|
||||
const DWORD kVertex2DFVF = (D3DFVF_XYZRHW | D3DFVF_DIFFUSE);
|
||||
|
||||
const int kCircleSegments = 32; // tessellation of dpl2d_Circle
|
||||
const int kCircleSegments = 32; // tessellation of AddCircle
|
||||
const float kPi = 3.14159265358979323846f;
|
||||
|
||||
inline float ClampUnit(float v)
|
||||
@@ -57,44 +62,74 @@ namespace
|
||||
return (v < 0.0f) ? 0.0f : (v > 1.0f ? 1.0f : v);
|
||||
}
|
||||
|
||||
struct Vec2 { float x, y; };
|
||||
|
||||
// 2x3 affine (row vectors): [x y 1] * | m00 m01 |
|
||||
// | m10 m11 |
|
||||
// | m20 m21 |
|
||||
struct Mat2x3
|
||||
{
|
||||
float m[6]; // m00 m01 m10 m11 m20 m21
|
||||
void Identity() { m[0]=1; m[1]=0; m[2]=0; m[3]=1; m[4]=0; m[5]=0; }
|
||||
Vec2 Apply(float x, float y) const
|
||||
{
|
||||
Vec2 r;
|
||||
r.x = x * m[0] + y * m[2] + m[4];
|
||||
r.y = x * m[1] + y * m[3] + m[5];
|
||||
return r;
|
||||
}
|
||||
static Mat2x3 Mul(const Mat2x3 &a, const Mat2x3 &b) // a then b
|
||||
{
|
||||
Mat2x3 r;
|
||||
r.m[0] = a.m[0]*b.m[0] + a.m[1]*b.m[2];
|
||||
r.m[1] = a.m[0]*b.m[1] + a.m[1]*b.m[3];
|
||||
r.m[2] = a.m[2]*b.m[0] + a.m[3]*b.m[2];
|
||||
r.m[3] = a.m[2]*b.m[1] + a.m[3]*b.m[3];
|
||||
r.m[4] = a.m[4]*b.m[0] + a.m[5]*b.m[2] + b.m[4];
|
||||
r.m[5] = a.m[4]*b.m[1] + a.m[5]*b.m[3] + b.m[5];
|
||||
return r;
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
// One recorded drawing command. The dpl2d_ API the port uses only ever
|
||||
// emits coloured circles (filled disc or outline ring); the matrix stack
|
||||
// and pen move are tracked as a translation so any future MoveTo-relative
|
||||
// geometry lands in the right place.
|
||||
// One recorded command (the opcode stream, structurally).
|
||||
//
|
||||
struct Command
|
||||
{
|
||||
enum Kind { kCircleFill, kCircleOutline } kind;
|
||||
DWORD color;
|
||||
float x, y; // normalised view coords (already translated)
|
||||
float radius; // normalised (fraction of viewport width)
|
||||
enum Kind
|
||||
{
|
||||
kPoints, // vertex run: a point per vertex
|
||||
kLineStrip, // vertex run: open strip
|
||||
kPolyLoop, // vertex run: closed loop
|
||||
kCircleFill,
|
||||
kCircleOutline,
|
||||
kSetColor,
|
||||
kSetWidth,
|
||||
kPushState,
|
||||
kPopState,
|
||||
kSetMatrix,
|
||||
kConcatMatrix,
|
||||
kCallList
|
||||
} kind;
|
||||
DWORD color; // kSetColor (prebaked ARGB)
|
||||
float a, b, c, d; // circle x,y,r,fill / width in a
|
||||
Mat2x3 mat; // kSetMatrix/kConcatMatrix
|
||||
void *callee; // kCallList (Dpl2dList*)
|
||||
std::vector<Vec2> verts; // vertex runs
|
||||
};
|
||||
|
||||
struct Vec2 { float x, y; };
|
||||
}
|
||||
|
||||
//
|
||||
// The concrete display list. dpl2d_DISPLAY is an opaque empty class
|
||||
// (DPLSTUB.h), so the recorder allocates one of these and hands the caller a
|
||||
// reinterpret_cast handle; every dpl2d_ call casts it straight back.
|
||||
//
|
||||
struct Dpl2dList
|
||||
{
|
||||
bool recording;
|
||||
bool compiled;
|
||||
int mode;
|
||||
DWORD currentColor;
|
||||
float penX, penY; // current pen (MoveTo)
|
||||
float transX, transY; // current translation
|
||||
std::vector<Vec2> matrixStack; // saved translations (Push/Pop)
|
||||
std::vector<Command> commands;
|
||||
// the currently-open vertex primitive (between AddOpenX .. AddCloseX)
|
||||
int openKind; // -1 none, else Command::Kind
|
||||
std::vector<Vec2> openVerts;
|
||||
|
||||
Dpl2dList()
|
||||
: recording(false), compiled(false), mode(0),
|
||||
currentColor(0xFFFFFFFF),
|
||||
penX(0.0f), penY(0.0f), transX(0.0f), transY(0.0f)
|
||||
{}
|
||||
Dpl2dList() : recording(false), compiled(false), mode(0), openKind(-1) {}
|
||||
};
|
||||
|
||||
static inline Dpl2dList *AsList(dpl2d_DISPLAY *handle)
|
||||
@@ -102,8 +137,20 @@ static inline Dpl2dList *AsList(dpl2d_DISPLAY *handle)
|
||||
return reinterpret_cast<Dpl2dList *>(handle);
|
||||
}
|
||||
|
||||
static void FlushOpenPrimitive(Dpl2dList *self)
|
||||
{
|
||||
if (self->openKind < 0)
|
||||
return;
|
||||
Command cmd;
|
||||
cmd.kind = (Command::Kind)self->openKind;
|
||||
cmd.verts = self->openVerts;
|
||||
self->commands.push_back(cmd);
|
||||
self->openKind = -1;
|
||||
self->openVerts.clear();
|
||||
}
|
||||
|
||||
//===========================================================================//
|
||||
// Recorder -- the dpl2d_* entry points the game calls (declared in btl4vid.hpp)
|
||||
// Recorder -- the dpl2d_ entry points (prototypes in btl4vid.hpp)
|
||||
//===========================================================================//
|
||||
|
||||
dpl2d_DISPLAY *dpl2d_NewDisplayList()
|
||||
@@ -111,92 +158,310 @@ dpl2d_DISPLAY *dpl2d_NewDisplayList()
|
||||
return reinterpret_cast<dpl2d_DISPLAY *>(new Dpl2dList);
|
||||
}
|
||||
|
||||
void dpl2d_Begin(dpl2d_DISPLAY *list, int mode)
|
||||
void dpl2d_Begin(dpl2d_DISPLAY *list, int mode) // OpenDisplayList
|
||||
{
|
||||
Dpl2dList *self = AsList(list);
|
||||
if (self == 0) return;
|
||||
self->recording = true;
|
||||
self->compiled = false;
|
||||
self->mode = mode;
|
||||
self->currentColor= 0xFFFFFFFF;
|
||||
self->transX = self->transY = 0.0f;
|
||||
self->penX = self->penY = 0.0f;
|
||||
self->matrixStack.clear();
|
||||
self->recording = true;
|
||||
self->compiled = false;
|
||||
self->mode = mode;
|
||||
self->openKind = -1;
|
||||
self->openVerts.clear();
|
||||
self->commands.clear();
|
||||
}
|
||||
|
||||
void dpl2d_End(dpl2d_DISPLAY *list) // CloseDisplayList
|
||||
{
|
||||
Dpl2dList *self = AsList(list);
|
||||
if (self == 0) return;
|
||||
FlushOpenPrimitive(self);
|
||||
self->recording = false;
|
||||
}
|
||||
|
||||
void dpl2d_Compile(dpl2d_DISPLAY *list) // FlushDisplayList
|
||||
{
|
||||
Dpl2dList *self = AsList(list);
|
||||
if (self == 0) return;
|
||||
self->compiled = true;
|
||||
}
|
||||
|
||||
void dpl2d_SetColor(dpl2d_DISPLAY *list, Scalar red, Scalar green, Scalar blue)
|
||||
{
|
||||
Dpl2dList *self = AsList(list);
|
||||
if (self == 0) return;
|
||||
self->currentColor = D3DCOLOR_COLORVALUE(
|
||||
Command cmd;
|
||||
cmd.kind = Command::kSetColor;
|
||||
cmd.color = D3DCOLOR_COLORVALUE(
|
||||
ClampUnit((float)red), ClampUnit((float)green), ClampUnit((float)blue), 1.0f);
|
||||
self->commands.push_back(cmd);
|
||||
}
|
||||
|
||||
void dpl2d_SetLineWidth(dpl2d_DISPLAY *list, Scalar width)
|
||||
{
|
||||
Dpl2dList *self = AsList(list);
|
||||
if (self == 0) return;
|
||||
Command cmd;
|
||||
cmd.kind = Command::kSetWidth;
|
||||
cmd.a = (float)width;
|
||||
self->commands.push_back(cmd);
|
||||
}
|
||||
|
||||
void dpl2d_PushState(dpl2d_DISPLAY *list)
|
||||
{
|
||||
Dpl2dList *self = AsList(list);
|
||||
if (self == 0) return;
|
||||
Command cmd; cmd.kind = Command::kPushState;
|
||||
self->commands.push_back(cmd);
|
||||
}
|
||||
|
||||
void dpl2d_PopState(dpl2d_DISPLAY *list)
|
||||
{
|
||||
Dpl2dList *self = AsList(list);
|
||||
if (self == 0) return;
|
||||
Command cmd; cmd.kind = Command::kPopState;
|
||||
self->commands.push_back(cmd);
|
||||
}
|
||||
|
||||
void dpl2d_SetMatrix(dpl2d_DISPLAY *list, const Scalar *six)
|
||||
{
|
||||
Dpl2dList *self = AsList(list);
|
||||
if (self == 0 || six == 0) return;
|
||||
Command cmd; cmd.kind = Command::kSetMatrix;
|
||||
for (int i = 0; i < 6; ++i) cmd.mat.m[i] = (float)six[i];
|
||||
self->commands.push_back(cmd);
|
||||
}
|
||||
|
||||
void dpl2d_ConcatMatrix(dpl2d_DISPLAY *list, const Scalar *six)
|
||||
{
|
||||
Dpl2dList *self = AsList(list);
|
||||
if (self == 0 || six == 0) return;
|
||||
Command cmd; cmd.kind = Command::kConcatMatrix;
|
||||
for (int i = 0; i < 6; ++i) cmd.mat.m[i] = (float)six[i];
|
||||
self->commands.push_back(cmd);
|
||||
}
|
||||
|
||||
void dpl2d_CallList(dpl2d_DISPLAY *list, dpl2d_DISPLAY *callee)
|
||||
{
|
||||
Dpl2dList *self = AsList(list);
|
||||
if (self == 0 || callee == 0) return;
|
||||
Command cmd; cmd.kind = Command::kCallList;
|
||||
cmd.callee = AsList(callee);
|
||||
self->commands.push_back(cmd);
|
||||
}
|
||||
|
||||
void dpl2d_OpenPolypoint(dpl2d_DISPLAY *list)
|
||||
{
|
||||
Dpl2dList *self = AsList(list);
|
||||
if (self == 0) return;
|
||||
FlushOpenPrimitive(self);
|
||||
self->openKind = Command::kPoints;
|
||||
}
|
||||
|
||||
void dpl2d_ClosePolypoint(dpl2d_DISPLAY *list)
|
||||
{
|
||||
Dpl2dList *self = AsList(list);
|
||||
if (self == 0) return;
|
||||
FlushOpenPrimitive(self);
|
||||
}
|
||||
|
||||
void dpl2d_OpenLines(dpl2d_DISPLAY *list)
|
||||
{
|
||||
Dpl2dList *self = AsList(list);
|
||||
if (self == 0) return;
|
||||
FlushOpenPrimitive(self);
|
||||
self->openKind = Command::kLineStrip;
|
||||
}
|
||||
|
||||
void dpl2d_CloseLines(dpl2d_DISPLAY *list)
|
||||
{
|
||||
Dpl2dList *self = AsList(list);
|
||||
if (self == 0) return;
|
||||
FlushOpenPrimitive(self);
|
||||
}
|
||||
|
||||
void dpl2d_OpenPolyline(dpl2d_DISPLAY *list)
|
||||
{
|
||||
Dpl2dList *self = AsList(list);
|
||||
if (self == 0) return;
|
||||
FlushOpenPrimitive(self);
|
||||
self->openKind = Command::kPolyLoop;
|
||||
}
|
||||
|
||||
void dpl2d_ClosePolyline(dpl2d_DISPLAY *list)
|
||||
{
|
||||
Dpl2dList *self = AsList(list);
|
||||
if (self == 0) return;
|
||||
FlushOpenPrimitive(self);
|
||||
}
|
||||
|
||||
void dpl2d_AddPoint(dpl2d_DISPLAY *list, Scalar x, Scalar y)
|
||||
{
|
||||
Dpl2dList *self = AsList(list);
|
||||
if (self == 0) return;
|
||||
if (self->openKind < 0)
|
||||
self->openKind = Command::kPoints; // tolerate a stray vertex
|
||||
Vec2 v = { (float)x, (float)y };
|
||||
self->openVerts.push_back(v);
|
||||
}
|
||||
|
||||
void dpl2d_Circle(dpl2d_DISPLAY *list, Scalar x, Scalar y, Scalar radius, int fill)
|
||||
{
|
||||
Dpl2dList *self = AsList(list);
|
||||
if (self == 0) return;
|
||||
FlushOpenPrimitive(self);
|
||||
Command cmd;
|
||||
cmd.kind = fill ? Command::kCircleFill : Command::kCircleOutline;
|
||||
cmd.color = self->currentColor;
|
||||
cmd.x = (float)x + self->transX;
|
||||
cmd.y = (float)y + self->transY;
|
||||
cmd.radius = (float)radius;
|
||||
cmd.kind = fill ? Command::kCircleFill : Command::kCircleOutline;
|
||||
cmd.a = (float)x; cmd.b = (float)y; cmd.c = (float)radius;
|
||||
self->commands.push_back(cmd);
|
||||
}
|
||||
|
||||
void dpl2d_PushMatrix(dpl2d_DISPLAY *list)
|
||||
void dpl2d_FullScreenClip(dpl2d_DISPLAY * /*list*/)
|
||||
{
|
||||
Dpl2dList *self = AsList(list);
|
||||
if (self == 0) return;
|
||||
Vec2 saved = { self->transX, self->transY };
|
||||
self->matrixStack.push_back(saved);
|
||||
}
|
||||
|
||||
void dpl2d_PopMatrix(dpl2d_DISPLAY *list)
|
||||
{
|
||||
Dpl2dList *self = AsList(list);
|
||||
if (self == 0 || self->matrixStack.empty()) return;
|
||||
Vec2 saved = self->matrixStack.back();
|
||||
self->matrixStack.pop_back();
|
||||
self->transX = saved.x;
|
||||
self->transY = saved.y;
|
||||
// clip reset -- the executor always draws to the full viewport
|
||||
}
|
||||
|
||||
//
|
||||
// MoveTo: position the pen. In the recovered usage it is bracketed by
|
||||
// Push/Pop with no draw in between (a no-op marker stamp), so it only updates
|
||||
// the pen + the current translation; nothing is emitted.
|
||||
// LEGACY ALIASES (the earlier AddWeapon transcription used these names for
|
||||
// what the binary's opcodes reveal to be the point primitive).
|
||||
//
|
||||
void dpl2d_MoveTo(dpl2d_DISPLAY *list, Scalar x, Scalar y)
|
||||
{
|
||||
Dpl2dList *self = AsList(list);
|
||||
if (self == 0) return;
|
||||
self->penX = (float)x;
|
||||
self->penY = (float)y;
|
||||
self->transX = (float)x;
|
||||
self->transY = (float)y;
|
||||
}
|
||||
|
||||
void dpl2d_End(dpl2d_DISPLAY *list)
|
||||
{
|
||||
Dpl2dList *self = AsList(list);
|
||||
if (self == 0) return;
|
||||
self->recording = false;
|
||||
}
|
||||
|
||||
void dpl2d_Compile(dpl2d_DISPLAY *list)
|
||||
{
|
||||
Dpl2dList *self = AsList(list);
|
||||
if (self == 0) return;
|
||||
self->compiled = true; // geometry is tessellated lazily at execute time
|
||||
}
|
||||
void dpl2d_PushMatrix(dpl2d_DISPLAY *list) { dpl2d_OpenPolypoint(list); }
|
||||
void dpl2d_MoveTo(dpl2d_DISPLAY *list, Scalar x, Scalar y) { dpl2d_AddPoint(list, x, y); }
|
||||
void dpl2d_PopMatrix(dpl2d_DISPLAY *list) { dpl2d_ClosePolypoint(list); }
|
||||
|
||||
//===========================================================================//
|
||||
// Executor -- rasterise a compiled list on the supplied device.
|
||||
//===========================================================================//
|
||||
|
||||
namespace
|
||||
{
|
||||
struct ExecState
|
||||
{
|
||||
DWORD color;
|
||||
float width;
|
||||
Mat2x3 mat;
|
||||
};
|
||||
|
||||
void DrawVertexRun(IDirect3DDevice9 *device, const Command &cmd,
|
||||
const ExecState &st, float ox, float oy, float vw, float vh)
|
||||
{
|
||||
if (cmd.verts.empty())
|
||||
return;
|
||||
static std::vector<Vertex2D> verts;
|
||||
verts.clear();
|
||||
for (size_t i = 0; i < cmd.verts.size(); ++i)
|
||||
{
|
||||
Vec2 p = st.mat.Apply(cmd.verts[i].x, cmd.verts[i].y);
|
||||
Vertex2D v;
|
||||
v.x = ox + p.x * vw; v.y = oy + p.y * vh;
|
||||
v.z = 0.0f; v.rhw = 1.0f; v.color = st.color;
|
||||
verts.push_back(v);
|
||||
}
|
||||
if (cmd.kind == Command::kPoints)
|
||||
{
|
||||
// a point renders as a small filled quad (D3D9 point size is
|
||||
// unreliable on the fixed function path); ~width pixels square.
|
||||
float half = (st.width > 1.0f) ? st.width : 1.0f;
|
||||
for (size_t i = 0; i < verts.size(); ++i)
|
||||
{
|
||||
Vertex2D q[4] = { verts[i], verts[i], verts[i], verts[i] };
|
||||
q[0].x -= half; q[0].y -= half;
|
||||
q[1].x += half; q[1].y -= half;
|
||||
q[2].x -= half; q[2].y += half;
|
||||
q[3].x += half; q[3].y += half;
|
||||
device->DrawPrimitiveUP(D3DPT_TRIANGLESTRIP, 2, q, sizeof(Vertex2D));
|
||||
}
|
||||
}
|
||||
else if (cmd.kind == Command::kLineStrip)
|
||||
{
|
||||
if (verts.size() >= 2)
|
||||
device->DrawPrimitiveUP(D3DPT_LINESTRIP,
|
||||
(UINT)verts.size() - 1, &verts[0], sizeof(Vertex2D));
|
||||
}
|
||||
else // kPolyLoop -- closed
|
||||
{
|
||||
if (verts.size() >= 2)
|
||||
{
|
||||
verts.push_back(verts[0]);
|
||||
device->DrawPrimitiveUP(D3DPT_LINESTRIP,
|
||||
(UINT)verts.size() - 1, &verts[0], sizeof(Vertex2D));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ExecuteListInner(Dpl2dList *self, IDirect3DDevice9 *device,
|
||||
ExecState st, float ox, float oy, float vw, float vh, int depth)
|
||||
{
|
||||
if (self == 0 || depth > 8)
|
||||
return;
|
||||
std::vector<ExecState> stack;
|
||||
Vertex2D ring[kCircleSegments + 2];
|
||||
|
||||
for (size_t i = 0; i < self->commands.size(); ++i)
|
||||
{
|
||||
const Command &cmd = self->commands[i];
|
||||
switch (cmd.kind)
|
||||
{
|
||||
case Command::kSetColor: st.color = cmd.color; break;
|
||||
case Command::kSetWidth: st.width = cmd.a; break;
|
||||
case Command::kPushState: stack.push_back(st); break;
|
||||
case Command::kPopState:
|
||||
if (!stack.empty()) { st = stack.back(); stack.pop_back(); }
|
||||
break;
|
||||
case Command::kSetMatrix: st.mat = cmd.mat; break;
|
||||
case Command::kConcatMatrix:
|
||||
st.mat = Mat2x3::Mul(cmd.mat, st.mat);
|
||||
break;
|
||||
case Command::kCallList:
|
||||
ExecuteListInner((Dpl2dList *)cmd.callee, device,
|
||||
st, ox, oy, vw, vh, depth + 1);
|
||||
break;
|
||||
case Command::kPoints:
|
||||
case Command::kLineStrip:
|
||||
case Command::kPolyLoop:
|
||||
DrawVertexRun(device, cmd, st, ox, oy, vw, vh);
|
||||
break;
|
||||
case Command::kCircleFill:
|
||||
case Command::kCircleOutline:
|
||||
{
|
||||
Vec2 p = st.mat.Apply(cmd.a, cmd.b);
|
||||
const float cx = ox + p.x * vw;
|
||||
const float cy = oy + p.y * vh;
|
||||
const float r = cmd.c * vw; // width-relative -> stays round
|
||||
if (cmd.kind == Command::kCircleFill)
|
||||
{
|
||||
ring[0].x = cx; ring[0].y = cy; ring[0].z = 0.0f;
|
||||
ring[0].rhw = 1.0f; ring[0].color = st.color;
|
||||
for (int s = 0; s <= kCircleSegments; ++s)
|
||||
{
|
||||
float a = (2.0f * kPi * s) / kCircleSegments;
|
||||
ring[s + 1].x = cx + cosf(a) * r;
|
||||
ring[s + 1].y = cy + sinf(a) * r;
|
||||
ring[s + 1].z = 0.0f; ring[s + 1].rhw = 1.0f;
|
||||
ring[s + 1].color = st.color;
|
||||
}
|
||||
device->DrawPrimitiveUP(D3DPT_TRIANGLEFAN,
|
||||
kCircleSegments, ring, sizeof(Vertex2D));
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int s = 0; s <= kCircleSegments; ++s)
|
||||
{
|
||||
float a = (2.0f * kPi * s) / kCircleSegments;
|
||||
ring[s].x = cx + cosf(a) * r;
|
||||
ring[s].y = cy + sinf(a) * r;
|
||||
ring[s].z = 0.0f; ring[s].rhw = 1.0f;
|
||||
ring[s].color = st.color;
|
||||
}
|
||||
device->DrawPrimitiveUP(D3DPT_LINESTRIP,
|
||||
kCircleSegments, ring, sizeof(Vertex2D));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void dpl2d_ExecuteList(dpl2d_DISPLAY *list, IDirect3DDevice9 *device)
|
||||
{
|
||||
Dpl2dList *self = AsList(list);
|
||||
@@ -206,20 +471,15 @@ void dpl2d_ExecuteList(dpl2d_DISPLAY *list, IDirect3DDevice9 *device)
|
||||
D3DVIEWPORT9 vp;
|
||||
if (FAILED(device->GetViewport(&vp)))
|
||||
return;
|
||||
const float ox = (float)vp.X;
|
||||
const float oy = (float)vp.Y;
|
||||
const float vw = (float)vp.Width;
|
||||
const float vh = (float)vp.Height;
|
||||
|
||||
// --- save the render states we touch ---------------------------------
|
||||
DWORD oldLighting, oldZ, oldCull, oldAlpha, oldSrc, oldDst, oldFog;
|
||||
device->GetRenderState(D3DRS_LIGHTING, &oldLighting);
|
||||
device->GetRenderState(D3DRS_ZENABLE, &oldZ);
|
||||
device->GetRenderState(D3DRS_CULLMODE, &oldCull);
|
||||
device->GetRenderState(D3DRS_ALPHABLENDENABLE, &oldAlpha);
|
||||
device->GetRenderState(D3DRS_SRCBLEND, &oldSrc);
|
||||
device->GetRenderState(D3DRS_DESTBLEND, &oldDst);
|
||||
device->GetRenderState(D3DRS_FOGENABLE, &oldFog);
|
||||
device->GetRenderState(D3DRS_LIGHTING, &oldLighting);
|
||||
device->GetRenderState(D3DRS_ZENABLE, &oldZ);
|
||||
device->GetRenderState(D3DRS_CULLMODE, &oldCull);
|
||||
device->GetRenderState(D3DRS_ALPHABLENDENABLE, &oldAlpha);
|
||||
device->GetRenderState(D3DRS_SRCBLEND, &oldSrc);
|
||||
device->GetRenderState(D3DRS_DESTBLEND, &oldDst);
|
||||
device->GetRenderState(D3DRS_FOGENABLE, &oldFog);
|
||||
IDirect3DBaseTexture9 *oldTex = 0;
|
||||
device->GetTexture(0, &oldTex);
|
||||
|
||||
@@ -233,56 +493,13 @@ void dpl2d_ExecuteList(dpl2d_DISPLAY *list, IDirect3DDevice9 *device)
|
||||
device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
|
||||
device->SetFVF(kVertex2DFVF);
|
||||
|
||||
Vertex2D ring[kCircleSegments + 2];
|
||||
ExecState st;
|
||||
st.color = 0xFFFFFFFF;
|
||||
st.width = 1.0f;
|
||||
st.mat.Identity();
|
||||
ExecuteListInner(self, device,
|
||||
st, (float)vp.X, (float)vp.Y, (float)vp.Width, (float)vp.Height, 0);
|
||||
|
||||
for (size_t i = 0; i < self->commands.size(); ++i)
|
||||
{
|
||||
const Command &cmd = self->commands[i];
|
||||
|
||||
// normalised view coords -> pixels. radius scales with viewport width
|
||||
// so it stays round on a non-square viewport.
|
||||
const float cx = ox + cmd.x * vw;
|
||||
const float cy = oy + cmd.y * vh;
|
||||
const float rx = cmd.radius * vw;
|
||||
const float ry = cmd.radius * vw;
|
||||
|
||||
if (cmd.kind == Command::kCircleFill)
|
||||
{
|
||||
// triangle fan: centre + rim, closed back to the first rim vertex.
|
||||
ring[0].x = cx; ring[0].y = cy; ring[0].z = 0.0f; ring[0].rhw = 1.0f;
|
||||
ring[0].color = cmd.color;
|
||||
for (int s = 0; s <= kCircleSegments; ++s)
|
||||
{
|
||||
float a = (2.0f * kPi * s) / kCircleSegments;
|
||||
ring[s + 1].x = cx + cosf(a) * rx;
|
||||
ring[s + 1].y = cy + sinf(a) * ry;
|
||||
ring[s + 1].z = 0.0f;
|
||||
ring[s + 1].rhw = 1.0f;
|
||||
ring[s + 1].color = cmd.color;
|
||||
}
|
||||
device->DrawPrimitiveUP(
|
||||
D3DPT_TRIANGLEFAN, kCircleSegments,
|
||||
ring, sizeof(Vertex2D));
|
||||
}
|
||||
else // kCircleOutline
|
||||
{
|
||||
// line strip around the rim, closed.
|
||||
for (int s = 0; s <= kCircleSegments; ++s)
|
||||
{
|
||||
float a = (2.0f * kPi * s) / kCircleSegments;
|
||||
ring[s].x = cx + cosf(a) * rx;
|
||||
ring[s].y = cy + sinf(a) * ry;
|
||||
ring[s].z = 0.0f;
|
||||
ring[s].rhw = 1.0f;
|
||||
ring[s].color = cmd.color;
|
||||
}
|
||||
device->DrawPrimitiveUP(
|
||||
D3DPT_LINESTRIP, kCircleSegments,
|
||||
ring, sizeof(Vertex2D));
|
||||
}
|
||||
}
|
||||
|
||||
// --- restore ----------------------------------------------------------
|
||||
device->SetTexture(0, oldTex);
|
||||
if (oldTex) oldTex->Release();
|
||||
device->SetRenderState(D3DRS_LIGHTING, oldLighting);
|
||||
|
||||
Reference in New Issue
Block a user