paavohtl Claude Opus 4.6 (1M context) commited on
Commit
12a0785
·
1 Parent(s): 89a37f4

refactor: migrate all render queue commands to typed RenderMessage enum

Browse files

Replace legacy DrawXxxCmd structs with RenderMessage variants for all
10 Rust-produced command types. Variable-length commands (LineStrip,
Polygon) use alloc_aux() for vertex data with explicit pointers in the
enum, avoiding layout coupling. Remove legacy structs and command_type
constants module.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

crates/openwa-core/src/render/bungee_trail.rs CHANGED
@@ -10,7 +10,7 @@
10
  use crate::address::va;
11
  use crate::fixed::Fixed;
12
  use crate::rebase::rb;
13
- use crate::render::queue::*;
14
  use crate::render::SpriteOp;
15
  use crate::task::BungeeTrailTask;
16
  use crate::trig::trig_lookup;
@@ -46,17 +46,17 @@ pub unsafe fn draw_bungee_trail(task: *const BungeeTrailTask, style: u32, fill:
46
 
47
  let first_angle = *(seg_data.add(4) as *const i32);
48
 
49
- // Enqueue start sprite (command type 5 = local)
50
- if let Some(entry) = rq.alloc::<DrawSpriteCmd>() {
51
- *entry = DrawSpriteCmd {
52
- command_type: command_type::DRAW_SPRITE_LOCAL,
53
- layer: 0xDFFFF,
54
  x: Fixed(x).floor(),
55
  y: Fixed(y).floor(),
56
  sprite: SpriteOp(0x45),
57
  palette: (first_angle + 0x8100) as u32,
58
- };
59
- }
60
 
61
  // Build vertex array from trail segments
62
  const MAX_VERTICES: usize = 256;
@@ -91,38 +91,30 @@ pub unsafe fn draw_bungee_trail(task: *const BungeeTrailTask, style: u32, fill:
91
  vert_count += 1;
92
  }
93
 
94
- // Enqueue as polygon or line strip
95
- if fill != 0 {
96
- let total_size = vert_count * 0xC + 0x20;
97
- if let Some(ptr) = rq.alloc_raw(total_size) {
98
- let header = &mut *(ptr as *mut DrawPolygonHeader);
99
- *header = DrawPolygonHeader {
100
- command_type: command_type::DRAW_POLYGON,
101
- layer: 0xE_0000,
102
- count: vert_count as u32,
103
- color1: style,
104
- color2: fill,
105
- };
106
- core::ptr::copy_nonoverlapping(
107
- verts.as_ptr() as *const u8,
108
- ptr.add(core::mem::size_of::<DrawPolygonHeader>()),
109
- vert_count * 0xC,
110
  );
111
- }
112
- } else {
113
- let total_size = vert_count * 0xC + 0x1C;
114
- if let Some(ptr) = rq.alloc_raw(total_size) {
115
- let header = &mut *(ptr as *mut DrawLineStripHeader);
116
- *header = DrawLineStripHeader {
117
- command_type: command_type::DRAW_LINE_STRIP,
118
- layer: 0xE_0000,
119
- count: vert_count as u32,
120
- color: style,
121
- };
122
- core::ptr::copy_nonoverlapping(
123
- verts.as_ptr() as *const u8,
124
- ptr.add(core::mem::size_of::<DrawLineStripHeader>()),
125
- vert_count * 0xC,
126
  );
127
  }
128
  }
 
10
  use crate::address::va;
11
  use crate::fixed::Fixed;
12
  use crate::rebase::rb;
13
+ use crate::render::message::RenderMessage;
14
  use crate::render::SpriteOp;
15
  use crate::task::BungeeTrailTask;
16
  use crate::trig::trig_lookup;
 
46
 
47
  let first_angle = *(seg_data.add(4) as *const i32);
48
 
49
+ // Enqueue start sprite (screen-space)
50
+ let _ = rq.push_typed(
51
+ 0xDFFFF,
52
+ RenderMessage::Sprite {
53
+ local: true,
54
  x: Fixed(x).floor(),
55
  y: Fixed(y).floor(),
56
  sprite: SpriteOp(0x45),
57
  palette: (first_angle + 0x8100) as u32,
58
+ },
59
+ );
60
 
61
  // Build vertex array from trail segments
62
  const MAX_VERTICES: usize = 256;
 
91
  vert_count += 1;
92
  }
93
 
94
+ // Allocate vertex data in the arena and enqueue as polygon or line strip
95
+ let byte_len = vert_count * core::mem::size_of::<[i32; 3]>();
96
+ if let Some(vert_ptr) = rq.alloc_aux(byte_len) {
97
+ core::ptr::copy_nonoverlapping(verts.as_ptr() as *const u8, vert_ptr, byte_len);
98
+ let vert_slice = vert_ptr as *const [i32; 3];
99
+
100
+ if fill != 0 {
101
+ let _ = rq.push_typed(
102
+ 0xE_0000,
103
+ RenderMessage::Polygon {
104
+ count: vert_count as u32,
105
+ color1: style,
106
+ color2: fill,
107
+ vertices: vert_slice,
108
+ },
 
109
  );
110
+ } else {
111
+ let _ = rq.push_typed(
112
+ 0xE_0000,
113
+ RenderMessage::LineStrip {
114
+ count: vert_count as u32,
115
+ color: style,
116
+ vertices: vert_slice,
117
+ },
 
 
 
 
 
 
 
118
  );
119
  }
120
  }
crates/openwa-core/src/render/crosshair_line.rs CHANGED
@@ -11,7 +11,7 @@
11
  use crate::address::va;
12
  use crate::fixed::Fixed;
13
  use crate::rebase::rb;
14
- use crate::render::queue::*;
15
  use crate::render::SpriteOp;
16
  use crate::task::WeaponAimTask;
17
  use crate::trig::trig_lookup;
@@ -86,34 +86,31 @@ pub unsafe fn draw_crosshair_line(task: *const WeaponAimTask) {
86
  let poly_param_1 = ddgame.gfx_color_table[8]; // crosshair line style
87
  let poly_param_2 = ddgame.gfx_color_table[6]; // crosshair line color
88
  let verts: [[i32; 3]; 2] = [[start_x, start_y, 0], [endpoint_x, endpoint_y, 0]];
89
- let total_size = 2 * 0xC + 0x20;
90
- if let Some(ptr) = rq.alloc_raw(total_size) {
91
- let header = &mut *(ptr as *mut DrawPolygonHeader);
92
- *header = DrawPolygonHeader {
93
- command_type: command_type::DRAW_POLYGON,
94
- layer: 0xE_0000,
95
- count: 2,
96
- color1: poly_param_1,
97
- color2: poly_param_2,
98
- };
99
- core::ptr::copy_nonoverlapping(
100
- verts.as_ptr() as *const u8,
101
- ptr.add(core::mem::size_of::<DrawPolygonHeader>()),
102
- 2 * 0xC,
103
  );
104
  }
105
 
106
  // Draw crosshair sprite at endpoint (only if no overflow clamping)
107
  if !overflowed {
108
- if let Some(entry) = rq.alloc::<DrawSpriteCmd>() {
109
- *entry = DrawSpriteCmd {
110
- command_type: command_type::DRAW_SPRITE_LOCAL,
111
- layer: 0x4_0000,
112
  x: Fixed(endpoint_x).floor(),
113
  y: Fixed(endpoint_y).floor(),
114
  sprite: SpriteOp(0x44),
115
  palette: (0x8000u32).wrapping_sub(angle),
116
- };
117
- }
118
  }
119
  }
 
11
  use crate::address::va;
12
  use crate::fixed::Fixed;
13
  use crate::rebase::rb;
14
+ use crate::render::message::RenderMessage;
15
  use crate::render::SpriteOp;
16
  use crate::task::WeaponAimTask;
17
  use crate::trig::trig_lookup;
 
86
  let poly_param_1 = ddgame.gfx_color_table[8]; // crosshair line style
87
  let poly_param_2 = ddgame.gfx_color_table[6]; // crosshair line color
88
  let verts: [[i32; 3]; 2] = [[start_x, start_y, 0], [endpoint_x, endpoint_y, 0]];
89
+ let byte_len = 2 * core::mem::size_of::<[i32; 3]>();
90
+ if let Some(vert_ptr) = rq.alloc_aux(byte_len) {
91
+ core::ptr::copy_nonoverlapping(verts.as_ptr() as *const u8, vert_ptr, byte_len);
92
+ let _ = rq.push_typed(
93
+ 0xE_0000,
94
+ RenderMessage::Polygon {
95
+ count: 2,
96
+ color1: poly_param_1,
97
+ color2: poly_param_2,
98
+ vertices: vert_ptr as *const [i32; 3],
99
+ },
 
 
 
100
  );
101
  }
102
 
103
  // Draw crosshair sprite at endpoint (only if no overflow clamping)
104
  if !overflowed {
105
+ let _ = rq.push_typed(
106
+ 0x4_0000,
107
+ RenderMessage::Sprite {
108
+ local: true,
109
  x: Fixed(endpoint_x).floor(),
110
  y: Fixed(endpoint_y).floor(),
111
  sprite: SpriteOp(0x44),
112
  palette: (0x8000u32).wrapping_sub(angle),
113
+ },
114
+ );
115
  }
116
  }
crates/openwa-core/src/render/message.rs CHANGED
@@ -12,8 +12,18 @@
12
  //! `rq.push_typed(layer, RenderMessage::Xxx { ... })` one at a time.
13
  //! The dispatcher (`queue_dispatch.rs`) handles both legacy and typed
14
  //! commands simultaneously until all producers are migrated.
 
 
 
 
 
 
 
 
15
 
 
16
  use crate::fixed::Fixed;
 
17
  use crate::render::sprite::sprite_op::SpriteOp;
18
 
19
  /// Sentinel `command_type` value for [`TypedRenderCmd`]. Sits outside
@@ -47,11 +57,104 @@ pub enum RenderMessage {
47
  /// Semantics vary by producer (palette pointer, animation index, etc.).
48
  palette: u32,
49
  },
50
- // Future variants added here as producers migrate:
51
- // FillRect { ... }
52
- // Crosshair { ... }
53
- // TiledBitmap { ... }
54
- // etc.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
55
  }
56
 
57
  /// Wrapper that carries a [`RenderMessage`] inline in the render queue
@@ -66,3 +169,7 @@ pub struct TypedRenderCmd {
66
  /// The typed payload.
67
  pub message: RenderMessage,
68
  }
 
 
 
 
 
12
  //! `rq.push_typed(layer, RenderMessage::Xxx { ... })` one at a time.
13
  //! The dispatcher (`queue_dispatch.rs`) handles both legacy and typed
14
  //! commands simultaneously until all producers are migrated.
15
+ //!
16
+ //! ## Variable-length commands
17
+ //!
18
+ //! Commands with variable-length data (LineStrip, Polygon) allocate
19
+ //! their vertex arrays separately in the arena via
20
+ //! `RenderQueue::alloc_aux()` and store an explicit pointer in the
21
+ //! enum variant. This avoids layout coupling between the command and
22
+ //! its auxiliary data.
23
 
24
+ use crate::bitgrid::DisplayBitGrid;
25
  use crate::fixed::Fixed;
26
+ use crate::render::display::vtable::TiledBitmapSource;
27
  use crate::render::sprite::sprite_op::SpriteOp;
28
 
29
  /// Sentinel `command_type` value for [`TypedRenderCmd`]. Sits outside
 
57
  /// Semantics vary by producer (palette pointer, animation index, etc.).
58
  palette: u32,
59
  },
60
+
61
+ /// Replaces legacy type 0 (`DRAW_RECT`).
62
+ FillRect {
63
+ color: u32,
64
+ x1: Fixed,
65
+ y1: Fixed,
66
+ x2: Fixed,
67
+ y2: Fixed,
68
+ /// Perspective clip reference Z (`cmd[7]` in legacy format).
69
+ ref_z: i32,
70
+ },
71
+
72
+ /// Replaces legacy type 0xB (`DRAW_CROSSHAIR`).
73
+ ///
74
+ /// `ref_z` is always 0 in all known producers — omitted here,
75
+ /// the dispatcher passes 0 directly.
76
+ Crosshair {
77
+ color_fg: u32,
78
+ color_bg: u32,
79
+ x: Fixed,
80
+ y: Fixed,
81
+ },
82
+
83
+ /// Replaces legacy type 0xD (`DRAW_TILED_BITMAP`).
84
+ TiledBitmap {
85
+ x: Fixed,
86
+ y: Fixed,
87
+ source: *const TiledBitmapSource,
88
+ /// Bit 0 forces destination X to 0.
89
+ flags: u8,
90
+ },
91
+
92
+ /// Replaces legacy type 6 (`DRAW_SPRITE_OFFSET`).
93
+ ///
94
+ /// `ref_z` (first Z reference) is always 0 in all known producers —
95
+ /// omitted here, the dispatcher passes 0 directly.
96
+ SpriteOffset {
97
+ flags: u32,
98
+ x: Fixed,
99
+ y: Fixed,
100
+ /// Second Z reference for perspective clip (`cmd[6]` in legacy format).
101
+ ref_z_2: i32,
102
+ sprite: SpriteOp,
103
+ palette: u32,
104
+ },
105
+
106
+ /// Replaces legacy type 1 (`DRAW_BITMAP_GLOBAL`).
107
+ ///
108
+ /// `src_x` is always 0 in all known producers — omitted here.
109
+ BitmapGlobal {
110
+ x: Fixed,
111
+ y: Fixed,
112
+ bitmap: *mut DisplayBitGrid,
113
+ src_y: i32,
114
+ src_w: i32,
115
+ src_h: i32,
116
+ flags: u32,
117
+ },
118
+
119
+ /// Replaces legacy type 2 (`DRAW_TEXTBOX_LOCAL`).
120
+ ///
121
+ /// `mode`, `ref_z`, `ref_z_2`, `src_x`, `src_y` are always 0 in
122
+ /// all known producers — omitted here.
123
+ TextboxLocal {
124
+ x: Fixed,
125
+ y: Fixed,
126
+ bitmap: *mut DisplayBitGrid,
127
+ src_w: i32,
128
+ src_h: i32,
129
+ flags: u32,
130
+ },
131
+
132
+ /// Replaces legacy type 8 (`DRAW_LINE_STRIP`).
133
+ ///
134
+ /// Vertex data is allocated separately via `RenderQueue::alloc_aux()`
135
+ /// and referenced by the explicit `vertices` pointer.
136
+ /// Each vertex is `[x: i32, y: i32, z: i32]` in Fixed16.
137
+ LineStrip {
138
+ count: u32,
139
+ color: u32,
140
+ /// Pointer to `count` vertices in the arena. Allocated via
141
+ /// `alloc_aux()`, valid for the current frame only.
142
+ vertices: *const [i32; 3],
143
+ },
144
+
145
+ /// Replaces legacy type 9 (`DRAW_POLYGON`).
146
+ ///
147
+ /// Vertex data is allocated separately via `RenderQueue::alloc_aux()`
148
+ /// and referenced by the explicit `vertices` pointer.
149
+ /// Each vertex is `[x: i32, y: i32, z: i32]` in Fixed16.
150
+ Polygon {
151
+ count: u32,
152
+ color1: u32,
153
+ color2: u32,
154
+ /// Pointer to `count` vertices in the arena. Allocated via
155
+ /// `alloc_aux()`, valid for the current frame only.
156
+ vertices: *const [i32; 3],
157
+ },
158
  }
159
 
160
  /// Wrapper that carries a [`RenderMessage`] inline in the render queue
 
169
  /// The typed payload.
170
  pub message: RenderMessage,
171
  }
172
+
173
+ // Canary for buffer pressure — if this fires, revisit the size budget
174
+ // note in the plan (consider #[repr(u8)] discriminant or variant splitting).
175
+ const _: () = assert!(core::mem::size_of::<TypedRenderCmd>() <= 48);
crates/openwa-core/src/render/mod.rs CHANGED
@@ -20,10 +20,7 @@ pub use display::{
20
  pub use landscape::{DirtyRect, PCLandscape};
21
  pub use message::{RenderMessage, TypedRenderCmd, COMMAND_TYPE_TYPED};
22
  pub use opengl::OpenGLState;
23
- pub use queue::{
24
- DrawBitmapGlobalCmd, DrawCrosshairCmd, DrawLineStripHeader, DrawPolygonHeader, DrawRectCmd,
25
- DrawSpriteCmd, DrawSpriteOffsetCmd, DrawTextboxLocalCmd, DrawTiledBitmapCmd, RenderQueue,
26
- };
27
  pub use sprite::{
28
  KnownSpriteId, ParsedSprite, Sprite, SpriteBank, SpriteFlags, SpriteFrame, SpriteOp,
29
  };
 
20
  pub use landscape::{DirtyRect, PCLandscape};
21
  pub use message::{RenderMessage, TypedRenderCmd, COMMAND_TYPE_TYPED};
22
  pub use opengl::OpenGLState;
23
+ pub use queue::RenderQueue;
 
 
 
24
  pub use sprite::{
25
  KnownSpriteId, ParsedSprite, Sprite, SpriteBank, SpriteFlags, SpriteFrame, SpriteOp,
26
  };
crates/openwa-core/src/render/queue.rs CHANGED
@@ -1,43 +1,17 @@
1
- use crate::bitgrid::DisplayBitGrid;
2
- use crate::fixed::Fixed;
3
- use crate::render::display::vtable::TiledBitmapSource;
4
-
5
  use super::message::{RenderMessage, TypedRenderCmd, COMMAND_TYPE_TYPED};
6
- use super::sprite::sprite_op::SpriteOp;
7
-
8
- /// Render command entry (0x18 bytes).
9
- ///
10
- /// Enqueued by DrawSpriteGlobal (type 4), DrawSpriteLocal (type 5),
11
- /// and other drawing functions.
12
- #[repr(C)]
13
- pub struct DrawSpriteCmd {
14
- /// Command type: 4 = sprite global (world-space), 5 = sprite local (screen-space)
15
- pub command_type: u32,
16
- pub layer: u32,
17
- /// X position (Fixed16, fractional bits always zero — producers floor before storing).
18
- pub x: Fixed,
19
- /// Y position (Fixed16, fractional bits always zero — producers floor before storing).
20
- pub y: Fixed,
21
- pub sprite: SpriteOp,
22
- /// Palette context — passed to `blit_sprite` as the last arg.
23
- /// Semantics vary by producer (palette pointer, animation index, etc.).
24
- pub palette: u32,
25
- }
26
-
27
- const _: () = assert!(core::mem::size_of::<DrawSpriteCmd>() == 0x18);
28
 
29
- /// Render queue — manages a downward-growing buffer of DrawSpriteCmd entries.
30
  ///
31
- /// Passed as `this` (ECX) to DrawSpriteGlobal/Local. The buffer area sits
32
- /// between offset 0x04 and the entry metadata at 0x10000+. Entries are
33
- /// allocated from the end of the buffer downward (offset decrements by 0x18).
34
  ///
35
  /// Max 0x800 (2048) entries per frame.
36
  #[repr(C)]
37
  pub struct RenderQueue {
38
- /// 0x00000: Buffer write offset (i32, decrements by 0x18 per entry)
39
  pub buffer_offset: i32,
40
- /// 0x00004 - 0x10003: Buffer area for DrawSpriteCmd entries
41
  pub _buffer: [u8; 0x10000],
42
  /// 0x10004: Number of enqueued entries (max 0x800)
43
  pub entry_count: u32,
@@ -66,9 +40,28 @@ impl RenderQueue {
66
  Some(entry)
67
  }
68
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
69
  /// Allocate `size` bytes from the downward-growing buffer.
70
  ///
71
- /// Like `alloc<T>()` but for variable-size entries (e.g. DrawLineStrip, DrawPolygon).
 
72
  /// Returns `None` if the queue is full or buffer exhausted.
73
  pub unsafe fn alloc_raw(&mut self, size: usize) -> Option<*mut u8> {
74
  if self.entry_count >= 0x800 {
@@ -85,171 +78,7 @@ impl RenderQueue {
85
  self.entry_count += 1;
86
  Some(ptr)
87
  }
88
- }
89
-
90
- /// Type 0xD — tile-bitmap draw command (0x18 bytes).
91
- ///
92
- /// Enqueued by `RQ_EnqueueTiledBitmap` (0x541D60, formerly mis-labelled
93
- /// `RQ_DrawPixel`). The only known producer is `CTaskLand::RenderLandscape`,
94
- /// which uses it to draw cached landscape texture tiles.
95
- ///
96
- /// Inside `RenderDrawingQueue`, case 0xD dispatches to
97
- /// `DisplayGfx::draw_tiled_bitmap` (vtable slot 11, 0x56B8C0):
98
- /// ```text
99
- /// RQ_ClipCoordinates(0, x_fixed16, y_fixed16, &out_x, &out_y, ...);
100
- /// vtable[slot 11](
101
- /// out_x >> 16 (or 0 if `flags & 1`), // X in pixels
102
- /// out_y >> 16, // Y in pixels
103
- /// source_descriptor // sprite source struct *
104
- /// );
105
- /// ```
106
- ///
107
- /// The slot 11 method itself runs three phases on each call: lazily allocate
108
- /// 0x400-row tile surfaces from `source_descriptor->total_height` (cached in
109
- /// `DisplayGfx + 0x3580`), populate them from the source, then clipped-blit
110
- /// the tiles to the display.
111
- ///
112
- /// Field names below are corrected. Wire format is byte-identical to what
113
- /// the original `RQ_EnqueueTiledBitmap` writes.
114
- #[repr(C)]
115
- pub struct DrawTiledBitmapCmd {
116
- /// 0x00: Command type — always 0xD.
117
- pub command_type: u32,
118
- /// 0x04: Render layer — hardcoded 0x1B_0000 by the enqueue function.
119
- pub layer: u32,
120
- /// 0x08: Source X coordinate (Fixed16). Hardcoded 0xFF00_0000 (= -256.0)
121
- /// — the tile sheet always renders from off-screen-left, with the
122
- /// dispatcher's clipping pass producing the on-screen X.
123
- pub x: Fixed,
124
- /// 0x0C: Source Y coordinate (Fixed16). Caller-supplied; goes through
125
- /// `RQ_ClipCoordinates` and the high 16 bits become the destination Y.
126
- pub y: Fixed,
127
- /// 0x10: Pointer to a [`TiledBitmapSource`] descriptor — read by the
128
- /// slot 11 method with fields at `+0x08` (bpp, 8 or 0x40), `+0x10`
129
- /// (source row stride), `+0x14`, and `+0x18` (total source height).
130
- pub source: *const TiledBitmapSource,
131
- /// 0x14: Flag byte. Bit 0 forces the destination X to 0 (ignoring the
132
- /// clipped result), used for "always start at left edge" rendering.
133
- pub flags: u8,
134
- pub _pad: [u8; 3],
135
- }
136
-
137
- const _: () = assert!(core::mem::size_of::<DrawTiledBitmapCmd>() == 0x18);
138
-
139
- /// Type 0xB — draw crosshair (0x1C = 28 bytes).
140
- ///
141
- /// Dispatched by RenderDrawingQueue case 0xB → DisplayGfx::draw_crosshair (slot 16).
142
- /// The enqueue function at 0x541ED0 writes fields in non-sequential order:
143
- /// `[1]=layer, [4]=x, [5]=y, [2]=color_fg, [3]=color_bg, [6]=0`.
144
- #[repr(C)]
145
- pub struct DrawCrosshairCmd {
146
- pub command_type: u32, // 0xB
147
- pub layer: u32,
148
- pub color_fg: u32,
149
- pub color_bg: u32,
150
- pub x: Fixed,
151
- pub y: Fixed,
152
- pub ref_z: i32, // hardcoded 0 (perspective clip reference Z)
153
- }
154
-
155
- const _: () = assert!(core::mem::size_of::<DrawCrosshairCmd>() == 0x1C);
156
 
157
- /// Type 0 — filled rectangle (0x20 = 32 bytes).
158
- #[repr(C)]
159
- pub struct DrawRectCmd {
160
- pub command_type: u32, // 0
161
- pub layer: u32,
162
- pub color: u32,
163
- pub x1: Fixed,
164
- pub y1: Fixed,
165
- pub x2: Fixed,
166
- pub y2: Fixed,
167
- pub ref_z: i32, // perspective clip reference Z (EDX & 0xFFFF0000)
168
- }
169
-
170
- const _: () = assert!(core::mem::size_of::<DrawRectCmd>() == 0x20);
171
-
172
- /// Type 6 — sprite with offset/scaling (0x24 = 36 bytes).
173
- #[repr(C)]
174
- pub struct DrawSpriteOffsetCmd {
175
- pub command_type: u32, // 6
176
- pub layer: u32,
177
- pub flags: u32,
178
- pub x: Fixed,
179
- pub y: Fixed,
180
- pub ref_z: i32, // first Z reference for perspective clip (hardcoded 0)
181
- pub ref_z_2: i32, // second Z reference (EDX & 0xFFFF0000)
182
- pub sprite: SpriteOp,
183
- pub palette: u32,
184
- }
185
-
186
- const _: () = assert!(core::mem::size_of::<DrawSpriteOffsetCmd>() == 0x24);
187
-
188
- /// Type 1 — bitmap global (0x28 = 40 bytes).
189
- #[repr(C)]
190
- pub struct DrawBitmapGlobalCmd {
191
- pub command_type: u32, // 1
192
- pub layer: u32,
193
- pub x: Fixed,
194
- pub y: Fixed,
195
- pub bitmap: *mut DisplayBitGrid,
196
- pub src_x: i32, // hardcoded 0
197
- pub src_y: i32,
198
- pub src_w: i32,
199
- pub src_h: i32,
200
- pub flags: u32,
201
- }
202
-
203
- const _: () = assert!(core::mem::size_of::<DrawBitmapGlobalCmd>() == 0x28);
204
-
205
- /// Type 2 — textbox local (0x34 = 52 bytes).
206
- #[repr(C)]
207
- pub struct DrawTextboxLocalCmd {
208
- pub command_type: u32, // 2
209
- pub layer: u32,
210
- pub mode: u32,
211
- pub x: Fixed,
212
- pub y: Fixed,
213
- pub ref_z: i32, // perspective clip reference Z (hardcoded 0)
214
- pub ref_z_2: i32, // second Z reference (hardcoded 0)
215
- pub bitmap: *mut DisplayBitGrid,
216
- pub src_x: i32, // hardcoded 0
217
- pub src_y: i32, // hardcoded 0
218
- pub src_w: i32,
219
- pub src_h: i32,
220
- pub flags: u32,
221
- }
222
-
223
- const _: () = assert!(core::mem::size_of::<DrawTextboxLocalCmd>() == 0x34);
224
-
225
- /// Type 8 — line strip header (0x10 bytes, followed by count × 0xC vertex data).
226
- ///
227
- /// Total allocation: count × 0xC + 0x1C.
228
- #[repr(C)]
229
- pub struct DrawLineStripHeader {
230
- pub command_type: u32, // 8
231
- pub layer: u32, // hardcoded 0xE_0000
232
- pub count: u32, // vertex count (from EDI)
233
- pub color: u32, // line color
234
- }
235
-
236
- const _: () = assert!(core::mem::size_of::<DrawLineStripHeader>() == 0x10);
237
-
238
- /// Type 9 — polygon header (0x14 bytes, followed by count × 0xC vertex data).
239
- ///
240
- /// Total allocation: count × 0xC + 0x20.
241
- #[repr(C)]
242
- pub struct DrawPolygonHeader {
243
- pub command_type: u32, // 9
244
- pub layer: u32, // hardcoded 0xE_0000
245
- pub count: u32, // vertex count (from ESI)
246
- pub color1: u32, // line color 1
247
- pub color2: u32, // line color 2
248
- }
249
-
250
- const _: () = assert!(core::mem::size_of::<DrawPolygonHeader>() == 0x14);
251
-
252
- impl RenderQueue {
253
  /// Enqueue a typed render message. Constant-time, zero allocation —
254
  /// the message is stored inline in the existing per-frame buffer.
255
  ///
@@ -272,20 +101,3 @@ impl RenderQueue {
272
  }
273
  }
274
  }
275
-
276
- /// Render command type constants.
277
- pub mod command_type {
278
- pub const DRAW_RECT: u32 = 0;
279
- pub const DRAW_BITMAP_GLOBAL: u32 = 1;
280
- pub const DRAW_TEXTBOX_LOCAL: u32 = 2;
281
- pub const DRAW_SPRITE_GLOBAL: u32 = 4;
282
- pub const DRAW_SPRITE_LOCAL: u32 = 5;
283
- pub const DRAW_SPRITE_OFFSET: u32 = 6;
284
- pub const DRAW_LINE_STRIP: u32 = 8;
285
- pub const DRAW_POLYGON: u32 = 9;
286
- pub const DRAW_CROSSHAIR: u32 = 0xB;
287
- pub const DRAW_OUTLINED_PIXEL: u32 = 0xC;
288
- /// Tile-cached bitmap draw — dispatches to `DisplayGfx::draw_tiled_bitmap`
289
- /// (vtable slot 11). Formerly mis-labelled `DRAW_PIXEL`.
290
- pub const DRAW_TILED_BITMAP: u32 = 0xD;
291
- }
 
 
 
 
 
1
  use super::message::{RenderMessage, TypedRenderCmd, COMMAND_TYPE_TYPED};
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2
 
3
+ /// Render queue — manages a downward-growing arena buffer of draw commands.
4
  ///
5
+ /// Passed as `this` (ECX) to the original DrawSprite*/DrawRect/etc. enqueue
6
+ /// functions. The buffer area sits between offset 0x04 and the entry metadata
7
+ /// at 0x10000+. Entries are allocated from the end of the buffer downward.
8
  ///
9
  /// Max 0x800 (2048) entries per frame.
10
  #[repr(C)]
11
  pub struct RenderQueue {
12
+ /// 0x00000: Buffer write offset (i32, decrements per allocation)
13
  pub buffer_offset: i32,
14
+ /// 0x00004 - 0x10003: Buffer area for command entries (64KB)
15
  pub _buffer: [u8; 0x10000],
16
  /// 0x10004: Number of enqueued entries (max 0x800)
17
  pub entry_count: u32,
 
40
  Some(entry)
41
  }
42
 
43
+ /// Allocate `size` bytes from the buffer WITHOUT registering an entry pointer.
44
+ ///
45
+ /// Used for auxiliary data (vertex arrays, etc.) that will be referenced
46
+ /// from a typed command via an explicit pointer. Both the aux data and
47
+ /// the subsequent `push_typed()` command live in the same arena, freed
48
+ /// together on frame reset.
49
+ ///
50
+ /// Unlike `alloc_raw()`, this does NOT consume an `entry_ptrs` slot or
51
+ /// increment `entry_count`.
52
+ pub unsafe fn alloc_aux(&mut self, size: usize) -> Option<*mut u8> {
53
+ let new_offset = self.buffer_offset - size as i32;
54
+ if new_offset < 0 {
55
+ return None;
56
+ }
57
+ self.buffer_offset = new_offset;
58
+ Some(self._buffer.as_mut_ptr().add(new_offset as usize))
59
+ }
60
+
61
  /// Allocate `size` bytes from the downward-growing buffer.
62
  ///
63
+ /// Like `alloc<T>()` but for variable-size entries (e.g. legacy commands
64
+ /// still enqueued by unported WA.exe code).
65
  /// Returns `None` if the queue is full or buffer exhausted.
66
  pub unsafe fn alloc_raw(&mut self, size: usize) -> Option<*mut u8> {
67
  if self.entry_count >= 0x800 {
 
78
  self.entry_count += 1;
79
  Some(ptr)
80
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
81
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
82
  /// Enqueue a typed render message. Constant-time, zero allocation —
83
  /// the message is stored inline in the existing per-frame buffer.
84
  ///
 
101
  }
102
  }
103
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
crates/openwa-core/src/render/queue_dispatch.rs CHANGED
@@ -935,16 +935,258 @@ unsafe fn dispatch_typed(display: *mut DisplayGfx, clip: &ClipContext, msg: &Ren
935
  palette,
936
  } => {
937
  if local {
938
- // Screen-space: apply camera translation (same as case 5).
939
  let mut out_x = Fixed::ZERO;
940
  let mut out_y = Fixed::ZERO;
941
  rq_translate_coordinates(clip, x, y, &mut out_x, &mut out_y);
942
  DisplayGfx::blit_sprite_raw(display, out_x, out_y, sprite, palette);
943
  } else {
944
- // World-space: pass through directly (same as case 4).
945
  DisplayGfx::blit_sprite_raw(display, x, y, sprite, palette);
946
  }
947
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
948
  }
949
  }
950
 
 
935
  palette,
936
  } => {
937
  if local {
 
938
  let mut out_x = Fixed::ZERO;
939
  let mut out_y = Fixed::ZERO;
940
  rq_translate_coordinates(clip, x, y, &mut out_x, &mut out_y);
941
  DisplayGfx::blit_sprite_raw(display, out_x, out_y, sprite, palette);
942
  } else {
 
943
  DisplayGfx::blit_sprite_raw(display, x, y, sprite, palette);
944
  }
945
  }
946
+
947
+ RenderMessage::FillRect {
948
+ color,
949
+ x1,
950
+ y1,
951
+ x2,
952
+ y2,
953
+ ref_z,
954
+ } => {
955
+ let mut ox1 = Fixed::ZERO;
956
+ let mut oy1 = Fixed::ZERO;
957
+ let mut ox2 = Fixed::ZERO;
958
+ let mut oy2 = Fixed::ZERO;
959
+ let mut scale = Fixed::ZERO;
960
+ if !rq_clip_coordinates(clip, x1, y1, ref_z, &mut ox1, &mut oy1, &mut scale) {
961
+ return;
962
+ }
963
+ if !rq_clip_coordinates(clip, x2, y2, ref_z, &mut ox2, &mut oy2, &mut scale) {
964
+ return;
965
+ }
966
+ // Sentinel clamps for "infinite" screen-space rectangles.
967
+ if x1.0 == i32::MIN {
968
+ ox1 = Fixed(i32::MIN);
969
+ }
970
+ if y1.0 == i32::MIN {
971
+ oy1 = Fixed(i32::MIN);
972
+ }
973
+ if x2.0 == 0x7FFF_0000 {
974
+ ox2 = Fixed(i32::MAX);
975
+ }
976
+ if y2.0 == 0x7FFF_0000 {
977
+ oy2 = Fixed(i32::MAX);
978
+ }
979
+ DisplayGfx::fill_rect_raw(
980
+ display,
981
+ ox1.to_int(),
982
+ oy1.to_int(),
983
+ ox2.to_int(),
984
+ oy2.to_int(),
985
+ color,
986
+ );
987
+ }
988
+
989
+ RenderMessage::Crosshair {
990
+ color_fg,
991
+ color_bg,
992
+ x,
993
+ y,
994
+ } => {
995
+ let mut ox = Fixed::ZERO;
996
+ let mut oy = Fixed::ZERO;
997
+ let mut scale = Fixed::ZERO;
998
+ if !rq_clip_coordinates(clip, x, y, 0, &mut ox, &mut oy, &mut scale) {
999
+ return;
1000
+ }
1001
+ DisplayGfx::draw_crosshair_raw(display, ox.to_int(), oy.to_int(), color_fg, color_bg);
1002
+ }
1003
+
1004
+ RenderMessage::TiledBitmap {
1005
+ x,
1006
+ y,
1007
+ source,
1008
+ flags,
1009
+ } => {
1010
+ let mut ox = Fixed::ZERO;
1011
+ let mut oy = Fixed::ZERO;
1012
+ let mut scale = Fixed::ZERO;
1013
+ if !rq_clip_coordinates(clip, Fixed::ZERO, x, y.0, &mut ox, &mut oy, &mut scale) {
1014
+ return;
1015
+ }
1016
+ let dest_x = if flags & 1 != 0 { 0 } else { ox.to_int() };
1017
+ DisplayGfx::draw_tiled_bitmap_raw(display, dest_x, oy.to_int(), source);
1018
+ }
1019
+
1020
+ RenderMessage::SpriteOffset {
1021
+ flags,
1022
+ x,
1023
+ y,
1024
+ ref_z_2,
1025
+ sprite,
1026
+ palette,
1027
+ } => {
1028
+ let mut ox = Fixed::ZERO;
1029
+ let mut oy = Fixed::ZERO;
1030
+ let mut scale = Fixed::ZERO;
1031
+
1032
+ if flags == 0 {
1033
+ rq_translate_coordinates(clip, x, y, &mut ox, &mut oy);
1034
+ } else {
1035
+ let use_pivot = (flags & 4) != 0;
1036
+ let clip_fn: fn(
1037
+ &ClipContext,
1038
+ Fixed,
1039
+ Fixed,
1040
+ i32,
1041
+ &mut Fixed,
1042
+ &mut Fixed,
1043
+ &mut Fixed,
1044
+ ) -> bool = if use_pivot {
1045
+ rq_clip_coordinates_with_ref
1046
+ } else {
1047
+ rq_clip_coordinates
1048
+ };
1049
+ // ref_z (first Z reference) is always 0.
1050
+ if !clip_fn(clip, x, y, 0, &mut ox, &mut oy, &mut scale) {
1051
+ return;
1052
+ }
1053
+ let mut ox2 = Fixed::ZERO;
1054
+ let mut oy2 = Fixed::ZERO;
1055
+ if !clip_fn(clip, x, y, ref_z_2, &mut ox2, &mut oy2, &mut scale) {
1056
+ return;
1057
+ }
1058
+ if flags & 1 != 0 {
1059
+ ox = ox2;
1060
+ }
1061
+ if flags & 2 != 0 {
1062
+ oy = oy2;
1063
+ }
1064
+ }
1065
+
1066
+ DisplayGfx::blit_sprite_raw(display, ox, oy, sprite, palette);
1067
+ }
1068
+
1069
+ RenderMessage::BitmapGlobal {
1070
+ x,
1071
+ y,
1072
+ bitmap,
1073
+ src_y,
1074
+ src_w,
1075
+ src_h,
1076
+ flags,
1077
+ } => {
1078
+ // src_x is always 0 in all known producers.
1079
+ DisplayGfx::draw_scaled_sprite_raw(
1080
+ display, x, y, bitmap, 0, src_y, src_w, src_h, flags,
1081
+ );
1082
+ }
1083
+
1084
+ RenderMessage::TextboxLocal {
1085
+ x,
1086
+ y,
1087
+ bitmap,
1088
+ src_w,
1089
+ src_h,
1090
+ flags,
1091
+ } => {
1092
+ // mode is always 0, so simple one-clip path.
1093
+ // ref_z is always 0.
1094
+ let mut ox = Fixed::ZERO;
1095
+ let mut oy = Fixed::ZERO;
1096
+ let mut scale = Fixed::ZERO;
1097
+ if !rq_clip_coordinates(clip, x, y, 0, &mut ox, &mut oy, &mut scale) {
1098
+ return;
1099
+ }
1100
+ // src_x and src_y are always 0.
1101
+ DisplayGfx::draw_scaled_sprite_raw(display, ox, oy, bitmap, 0, 0, src_w, src_h, flags);
1102
+ }
1103
+
1104
+ RenderMessage::LineStrip {
1105
+ count,
1106
+ color,
1107
+ vertices,
1108
+ } => {
1109
+ if count <= 1 {
1110
+ return;
1111
+ }
1112
+ let verts = core::slice::from_raw_parts(vertices, count as usize);
1113
+ let mut x1 = Fixed::ZERO;
1114
+ let mut y1 = Fixed::ZERO;
1115
+ let mut scale = Fixed::ZERO;
1116
+ if !rq_clip_coordinates(
1117
+ clip,
1118
+ Fixed::from_raw(verts[0][0]),
1119
+ Fixed::from_raw(verts[0][1]),
1120
+ verts[0][2],
1121
+ &mut x1,
1122
+ &mut y1,
1123
+ &mut scale,
1124
+ ) {
1125
+ return;
1126
+ }
1127
+ for v in &verts[1..] {
1128
+ let mut x2 = Fixed::ZERO;
1129
+ let mut y2 = Fixed::ZERO;
1130
+ if !rq_clip_coordinates(
1131
+ clip,
1132
+ Fixed::from_raw(v[0]),
1133
+ Fixed::from_raw(v[1]),
1134
+ v[2],
1135
+ &mut x2,
1136
+ &mut y2,
1137
+ &mut scale,
1138
+ ) {
1139
+ break;
1140
+ }
1141
+ DisplayGfx::draw_line_clipped_raw(display, x1, y1, x2, y2, color);
1142
+ x1 = x2;
1143
+ y1 = y2;
1144
+ }
1145
+ }
1146
+
1147
+ RenderMessage::Polygon {
1148
+ count,
1149
+ color1,
1150
+ color2,
1151
+ vertices,
1152
+ } => {
1153
+ if count <= 1 {
1154
+ return;
1155
+ }
1156
+ let verts = core::slice::from_raw_parts(vertices, count as usize);
1157
+ let mut x1 = Fixed::ZERO;
1158
+ let mut y1 = Fixed::ZERO;
1159
+ let mut scale = Fixed::ZERO;
1160
+ if !rq_clip_coordinates(
1161
+ clip,
1162
+ Fixed::from_raw(verts[0][0]),
1163
+ Fixed::from_raw(verts[0][1]),
1164
+ verts[0][2],
1165
+ &mut x1,
1166
+ &mut y1,
1167
+ &mut scale,
1168
+ ) {
1169
+ return;
1170
+ }
1171
+ for v in &verts[1..] {
1172
+ let mut x2 = Fixed::ZERO;
1173
+ let mut y2 = Fixed::ZERO;
1174
+ if !rq_clip_coordinates(
1175
+ clip,
1176
+ Fixed::from_raw(v[0]),
1177
+ Fixed::from_raw(v[1]),
1178
+ v[2],
1179
+ &mut x2,
1180
+ &mut y2,
1181
+ &mut scale,
1182
+ ) {
1183
+ break;
1184
+ }
1185
+ DisplayGfx::draw_line_raw(display, x1, y1, x2, y2, color1, color2);
1186
+ x1 = x2;
1187
+ y1 = y2;
1188
+ }
1189
+ }
1190
  }
1191
  }
1192
 
crates/openwa-dll/src/replacements/render/render_queue.rs CHANGED
@@ -5,7 +5,8 @@ use openwa_core::bitgrid::DisplayBitGrid;
5
  use openwa_core::fixed::Fixed;
6
  use openwa_core::render::display::vtable::TiledBitmapSource;
7
  use openwa_core::render::display::DisplayGfx;
8
- use openwa_core::render::queue::*;
 
9
  use openwa_core::render::queue_dispatch::{render_drawing_queue, ClipContext};
10
  use openwa_core::render::SpriteOp;
11
  use openwa_core::task::{BungeeTrailTask, WeaponAimTask};
@@ -23,20 +24,18 @@ unsafe extern "cdecl" fn enqueue_tiled_bitmap_impl(
23
  source: *const TiledBitmapSource,
24
  flags: u32,
25
  ) {
26
- if let Some(entry) = (*queue).alloc::<DrawTiledBitmapCmd>() {
27
- *entry = DrawTiledBitmapCmd {
28
- command_type: command_type::DRAW_TILED_BITMAP,
29
- layer: 0x1B_0000,
30
  x: Fixed(0xFF00_0000u32 as i32),
31
  y,
32
  source,
33
  flags: flags as u8,
34
- _pad: [0; 3],
35
- };
36
- }
37
  }
38
 
39
- // DrawLineStrip (0x541DD0) — variable-size allocation: count * 0xC + 0x1C
40
 
41
  usercall_trampoline!(fn trampoline_draw_line_strip; impl_fn = draw_line_strip_impl;
42
  regs = [eax, edi]; stack_params = 2; ret_bytes = "0x8");
@@ -44,28 +43,25 @@ usercall_trampoline!(fn trampoline_draw_line_strip; impl_fn = draw_line_strip_im
44
  unsafe extern "cdecl" fn draw_line_strip_impl(
45
  queue: *mut RenderQueue,
46
  count: u32,
47
- vertices: *const u8,
48
  color: u32,
49
  ) {
50
- let total_size = count as usize * 0xC + 0x1C;
51
-
52
- if let Some(ptr) = (*queue).alloc_raw(total_size) {
53
- let header = &mut *(ptr as *mut DrawLineStripHeader);
54
- *header = DrawLineStripHeader {
55
- command_type: command_type::DRAW_LINE_STRIP,
56
- layer: 0xE_0000,
57
- count,
58
- color,
59
- };
60
- core::ptr::copy_nonoverlapping(
61
- vertices,
62
- ptr.add(core::mem::size_of::<DrawLineStripHeader>()),
63
- count as usize * 0xC,
64
  );
65
  }
66
  }
67
 
68
- // DrawPolygon (0x541E50) — variable-size allocation: count * 0xC + 0x20
69
 
70
  usercall_trampoline!(fn trampoline_draw_polygon; impl_fn = draw_polygon_impl;
71
  regs = [ecx, esi]; stack_params = 3; ret_bytes = "0xC");
@@ -73,25 +69,22 @@ usercall_trampoline!(fn trampoline_draw_polygon; impl_fn = draw_polygon_impl;
73
  unsafe extern "cdecl" fn draw_polygon_impl(
74
  queue: *mut RenderQueue,
75
  count: u32,
76
- vertices: *const u8,
77
  color1: u32,
78
  color2: u32,
79
  ) {
80
- let total_size = count as usize * 0xC + 0x20;
81
-
82
- if let Some(ptr) = (*queue).alloc_raw(total_size) {
83
- let header = &mut *(ptr as *mut DrawPolygonHeader);
84
- *header = DrawPolygonHeader {
85
- command_type: command_type::DRAW_POLYGON,
86
- layer: 0xE_0000,
87
- count,
88
- color1,
89
- color2,
90
- };
91
- core::ptr::copy_nonoverlapping(
92
- vertices,
93
- ptr.add(core::mem::size_of::<DrawPolygonHeader>()),
94
- count as usize * 0xC,
95
  );
96
  }
97
  }
@@ -109,17 +102,15 @@ unsafe extern "cdecl" fn draw_crosshair_impl(
109
  color_fg: u32,
110
  color_bg: u32,
111
  ) {
112
- if let Some(entry) = (*queue).alloc::<DrawCrosshairCmd>() {
113
- *entry = DrawCrosshairCmd {
114
- command_type: command_type::DRAW_CROSSHAIR,
115
- layer,
116
  color_fg,
117
  color_bg,
118
  x,
119
  y,
120
- ref_z: 0,
121
- };
122
- }
123
  }
124
 
125
  // DrawRect (0x541F40)
@@ -137,18 +128,17 @@ unsafe extern "cdecl" fn draw_rect_impl(
137
  y2: Fixed,
138
  color: u32,
139
  ) {
140
- if let Some(entry) = (*queue).alloc::<DrawRectCmd>() {
141
- *entry = DrawRectCmd {
142
- command_type: command_type::DRAW_RECT,
143
- layer,
144
  color,
145
  x1: x1.floor(),
146
  y1: y1.floor(),
147
  x2: x2.floor(),
148
  y2: y2.floor(),
149
  ref_z: y_clip.floor().0,
150
- };
151
- }
152
  }
153
 
154
  // DrawSpriteGlobal (0x541FE0)
@@ -164,16 +154,16 @@ unsafe extern "cdecl" fn draw_sprite_global_impl(
164
  sprite: SpriteOp,
165
  frame: u32,
166
  ) {
167
- if let Some(entry) = (*queue).alloc::<DrawSpriteCmd>() {
168
- *entry = DrawSpriteCmd {
169
- command_type: command_type::DRAW_SPRITE_GLOBAL,
170
- layer,
171
  x: x_pos.floor(),
172
  y: y_pos.floor(),
173
  sprite,
174
  palette: frame,
175
- };
176
- }
177
  }
178
 
179
  // DrawSpriteLocal (0x542060)
@@ -189,16 +179,16 @@ unsafe extern "cdecl" fn draw_sprite_local_impl(
189
  sprite: SpriteOp,
190
  frame: u32,
191
  ) {
192
- if let Some(entry) = (*queue).alloc::<DrawSpriteCmd>() {
193
- *entry = DrawSpriteCmd {
194
- command_type: command_type::DRAW_SPRITE_LOCAL,
195
- layer,
196
  x: x_pos.floor(),
197
  y: y_pos.floor(),
198
  sprite,
199
  palette: frame,
200
- };
201
- }
202
  }
203
 
204
  // DrawSpriteOffset (0x5420E0)
@@ -216,19 +206,17 @@ unsafe extern "cdecl" fn draw_sprite_offset_impl(
216
  sprite: SpriteOp,
217
  palette: u32,
218
  ) {
219
- if let Some(entry) = (*queue).alloc::<DrawSpriteOffsetCmd>() {
220
- *entry = DrawSpriteOffsetCmd {
221
- command_type: command_type::DRAW_SPRITE_OFFSET,
222
- layer,
223
  flags,
224
  x: x_pos.floor(),
225
  y: y_pos.floor(),
226
- ref_z: 0,
227
  ref_z_2: y_clip.floor().0,
228
  sprite,
229
  palette,
230
- };
231
- }
232
  }
233
 
234
  // DrawBitmapGlobal (0x542170)
@@ -247,20 +235,18 @@ unsafe extern "cdecl" fn draw_bitmap_global_impl(
247
  src_h: i32,
248
  flags: u32,
249
  ) {
250
- if let Some(entry) = (*queue).alloc::<DrawBitmapGlobalCmd>() {
251
- *entry = DrawBitmapGlobalCmd {
252
- command_type: command_type::DRAW_BITMAP_GLOBAL,
253
- layer,
254
  x: x_pos.floor(),
255
  y: y_pos.floor(),
256
  bitmap,
257
- src_x: 0,
258
  src_y,
259
  src_w,
260
  src_h,
261
  flags,
262
- };
263
- }
264
  }
265
 
266
  // DrawTextboxLocal (0x542200)
@@ -278,23 +264,17 @@ unsafe extern "cdecl" fn draw_textbox_local_impl(
278
  src_h: i32,
279
  flags: u32,
280
  ) {
281
- if let Some(entry) = (*q).alloc::<DrawTextboxLocalCmd>() {
282
- *entry = DrawTextboxLocalCmd {
283
- command_type: command_type::DRAW_TEXTBOX_LOCAL,
284
- layer,
285
- mode: 0,
286
  x: x_pos.floor(),
287
  y: y_pos.floor(),
288
- ref_z: 0,
289
- ref_z_2: 0,
290
  bitmap,
291
- src_x: 0,
292
- src_y: 0,
293
  src_w,
294
  src_h,
295
  flags,
296
- };
297
- }
298
  }
299
 
300
  // DrawBungeeTrail (0x500720)
 
5
  use openwa_core::fixed::Fixed;
6
  use openwa_core::render::display::vtable::TiledBitmapSource;
7
  use openwa_core::render::display::DisplayGfx;
8
+ use openwa_core::render::message::RenderMessage;
9
+ use openwa_core::render::queue::RenderQueue;
10
  use openwa_core::render::queue_dispatch::{render_drawing_queue, ClipContext};
11
  use openwa_core::render::SpriteOp;
12
  use openwa_core::task::{BungeeTrailTask, WeaponAimTask};
 
24
  source: *const TiledBitmapSource,
25
  flags: u32,
26
  ) {
27
+ let _ = (*queue).push_typed(
28
+ 0x1B_0000,
29
+ RenderMessage::TiledBitmap {
 
30
  x: Fixed(0xFF00_0000u32 as i32),
31
  y,
32
  source,
33
  flags: flags as u8,
34
+ },
35
+ );
 
36
  }
37
 
38
+ // DrawLineStrip (0x541DD0) — variable-size: vertex data via alloc_aux
39
 
40
  usercall_trampoline!(fn trampoline_draw_line_strip; impl_fn = draw_line_strip_impl;
41
  regs = [eax, edi]; stack_params = 2; ret_bytes = "0x8");
 
43
  unsafe extern "cdecl" fn draw_line_strip_impl(
44
  queue: *mut RenderQueue,
45
  count: u32,
46
+ vertices: *const [i32; 3],
47
  color: u32,
48
  ) {
49
+ let rq = &mut *queue;
50
+ let byte_len = count as usize * core::mem::size_of::<[i32; 3]>();
51
+ if let Some(vert_ptr) = rq.alloc_aux(byte_len) {
52
+ core::ptr::copy_nonoverlapping(vertices as *const u8, vert_ptr, byte_len);
53
+ let _ = rq.push_typed(
54
+ 0xE_0000,
55
+ RenderMessage::LineStrip {
56
+ count,
57
+ color,
58
+ vertices: vert_ptr as *const [i32; 3],
59
+ },
 
 
 
60
  );
61
  }
62
  }
63
 
64
+ // DrawPolygon (0x541E50) — variable-size: vertex data via alloc_aux
65
 
66
  usercall_trampoline!(fn trampoline_draw_polygon; impl_fn = draw_polygon_impl;
67
  regs = [ecx, esi]; stack_params = 3; ret_bytes = "0xC");
 
69
  unsafe extern "cdecl" fn draw_polygon_impl(
70
  queue: *mut RenderQueue,
71
  count: u32,
72
+ vertices: *const [i32; 3],
73
  color1: u32,
74
  color2: u32,
75
  ) {
76
+ let rq = &mut *queue;
77
+ let byte_len = count as usize * core::mem::size_of::<[i32; 3]>();
78
+ if let Some(vert_ptr) = rq.alloc_aux(byte_len) {
79
+ core::ptr::copy_nonoverlapping(vertices as *const u8, vert_ptr, byte_len);
80
+ let _ = rq.push_typed(
81
+ 0xE_0000,
82
+ RenderMessage::Polygon {
83
+ count,
84
+ color1,
85
+ color2,
86
+ vertices: vert_ptr as *const [i32; 3],
87
+ },
 
 
 
88
  );
89
  }
90
  }
 
102
  color_fg: u32,
103
  color_bg: u32,
104
  ) {
105
+ let _ = (*queue).push_typed(
106
+ layer,
107
+ RenderMessage::Crosshair {
 
108
  color_fg,
109
  color_bg,
110
  x,
111
  y,
112
+ },
113
+ );
 
114
  }
115
 
116
  // DrawRect (0x541F40)
 
128
  y2: Fixed,
129
  color: u32,
130
  ) {
131
+ let _ = (*queue).push_typed(
132
+ layer,
133
+ RenderMessage::FillRect {
 
134
  color,
135
  x1: x1.floor(),
136
  y1: y1.floor(),
137
  x2: x2.floor(),
138
  y2: y2.floor(),
139
  ref_z: y_clip.floor().0,
140
+ },
141
+ );
142
  }
143
 
144
  // DrawSpriteGlobal (0x541FE0)
 
154
  sprite: SpriteOp,
155
  frame: u32,
156
  ) {
157
+ let _ = (*queue).push_typed(
158
+ layer,
159
+ RenderMessage::Sprite {
160
+ local: false,
161
  x: x_pos.floor(),
162
  y: y_pos.floor(),
163
  sprite,
164
  palette: frame,
165
+ },
166
+ );
167
  }
168
 
169
  // DrawSpriteLocal (0x542060)
 
179
  sprite: SpriteOp,
180
  frame: u32,
181
  ) {
182
+ let _ = (*queue).push_typed(
183
+ layer,
184
+ RenderMessage::Sprite {
185
+ local: true,
186
  x: x_pos.floor(),
187
  y: y_pos.floor(),
188
  sprite,
189
  palette: frame,
190
+ },
191
+ );
192
  }
193
 
194
  // DrawSpriteOffset (0x5420E0)
 
206
  sprite: SpriteOp,
207
  palette: u32,
208
  ) {
209
+ let _ = (*queue).push_typed(
210
+ layer,
211
+ RenderMessage::SpriteOffset {
 
212
  flags,
213
  x: x_pos.floor(),
214
  y: y_pos.floor(),
 
215
  ref_z_2: y_clip.floor().0,
216
  sprite,
217
  palette,
218
+ },
219
+ );
220
  }
221
 
222
  // DrawBitmapGlobal (0x542170)
 
235
  src_h: i32,
236
  flags: u32,
237
  ) {
238
+ let _ = (*queue).push_typed(
239
+ layer,
240
+ RenderMessage::BitmapGlobal {
 
241
  x: x_pos.floor(),
242
  y: y_pos.floor(),
243
  bitmap,
 
244
  src_y,
245
  src_w,
246
  src_h,
247
  flags,
248
+ },
249
+ );
250
  }
251
 
252
  // DrawTextboxLocal (0x542200)
 
264
  src_h: i32,
265
  flags: u32,
266
  ) {
267
+ let _ = (*q).push_typed(
268
+ layer,
269
+ RenderMessage::TextboxLocal {
 
 
270
  x: x_pos.floor(),
271
  y: y_pos.floor(),
 
 
272
  bitmap,
 
 
273
  src_w,
274
  src_h,
275
  flags,
276
+ },
277
+ );
278
  }
279
 
280
  // DrawBungeeTrail (0x500720)