102 Patches: Detours to the Rescue
C reference for DttR maintainers and modders.
Loading...
Searching...
No Matches
dttr_util_worldview.h
Go to the documentation of this file.
1
12
13#ifndef DTTR_UTIL_WORLDVIEW_H
14#define DTTR_UTIL_WORLDVIEW_H
15
16#include <stdbool.h>
17#include <stddef.h>
18#include <stdint.h>
19
20#include <dttr_core.h>
21#ifndef DTTR_SDK_ENABLE_UNSTABLE
22#error "Define DTTR_SDK_ENABLE_UNSTABLE before including dttr_util_worldview.h"
23#endif
24#include <dttr_pcdogs.h>
25#include <dttr_util_mem.h>
26
27#ifdef __cplusplus
28extern "C" {
29#endif
30
32#define DTTR_UTIL_WORLDVIEW_FIXED_ONE 4096
33
36#define DTTR_UTIL_WORLDVIEW_MAX_POLYGON_POINTS 8u
37
58
59static inline uint32_t dttr_util_worldview_isqrt(uint64_t value) {
60 uint64_t result = 0;
61 uint64_t bit = (uint64_t)1 << 62;
62
63 while (bit > value) {
64 bit >>= 2;
65 }
66
67 while (bit != 0) {
68 if (value >= result + bit) {
69 value -= result + bit;
70 result = (result >> 1) + bit;
71 } else {
72 result >>= 1;
73 }
74
75 bit >>= 2;
76 }
77
78 return (uint32_t)result;
79}
80
81static inline int16_t dttr_util_worldview_norm_q12(int64_t component, uint32_t length) {
82 return (int16_t)((component * DTTR_UTIL_WORLDVIEW_FIXED_ONE) / (int64_t)length);
83}
84
91static inline bool DTTR_Util_WorldView_Refresh(
94) {
95 if (!view) {
96 return false;
97 }
98
99 view->valid = false;
100 if (!target || target->width == 0 || target->height == 0
103 return false;
104 }
105
107 if (!DTTR_StatusOK(
109 )
110 || !list_state || !DTTR_Util_MemReadable(list_state, sizeof(*list_state))) {
111 return false;
112 }
113
114 const DTTR_PCDOGS_T_Math_Vec3I32 eye = {
115 .x = list_state->eye_pos.x,
116 .y = list_state->eye_pos.z,
117 .z = list_state->eye_pos.y,
118 };
119
120 const DTTR_PCDOGS_T_Math_Vec3I32 look_at = {
121 .x = list_state->target_pos.x,
122 .y = list_state->target_pos.z,
123 .z = list_state->target_pos.y,
124 };
125
126 const int64_t fx_raw = (int64_t)look_at.x - eye.x;
127 const int64_t fy_raw = (int64_t)look_at.y - eye.y;
128 const int64_t fz_raw = (int64_t)look_at.z - eye.z;
129 const uint64_t fx2 = (uint64_t)(fx_raw * fx_raw);
130 const uint64_t fy2 = (uint64_t)(fy_raw * fy_raw);
131 const uint64_t fz2 = (uint64_t)(fz_raw * fz_raw);
132 const uint32_t f_len = dttr_util_worldview_isqrt(fx2 + fy2 + fz2);
133 const uint32_t h_len = dttr_util_worldview_isqrt(fx2 + fz2);
134
135 if (f_len == 0 || h_len == 0) {
136 return false;
137 }
138
139 const DTTR_PCDOGS_T_Math_Vec3I16 forward = {
140 .x = dttr_util_worldview_norm_q12(fx_raw, f_len),
141 .y = dttr_util_worldview_norm_q12(fy_raw, f_len),
142 .z = dttr_util_worldview_norm_q12(fz_raw, f_len),
143 };
144
145 // The right vector stays horizontal, flipped for the game's handedness.
146 const DTTR_PCDOGS_T_Math_Vec3I16 right = {
147 .x = (int16_t)-dttr_util_worldview_norm_q12(fz_raw, h_len),
148 .y = 0,
149 .z = dttr_util_worldview_norm_q12(fx_raw, h_len),
150 };
151
152 const DTTR_PCDOGS_T_Math_Vec3I16 up = {
153 .x = (int16_t)(((int32_t)forward.y * right.z) / DTTR_UTIL_WORLDVIEW_FIXED_ONE),
154 .y = (int16_t)(((int32_t)forward.z * right.x - (int32_t)forward.x * right.z)
156 .z = (int16_t)(-((int32_t)forward.y * right.x) / DTTR_UTIL_WORLDVIEW_FIXED_ONE),
157 };
158 int32_t near_fp = list_state->projection_near_fp;
159 if (near_fp < DTTR_UTIL_WORLDVIEW_FIXED_ONE / 4
160 || near_fp > DTTR_UTIL_WORLDVIEW_FIXED_ONE * 64) {
161 near_fp = DTTR_UTIL_WORLDVIEW_FIXED_ONE / 4;
162 }
163
164 int32_t focal = list_state->focal_distance;
165 if (focal <= 0 || focal > DTTR_UTIL_WORLDVIEW_FIXED_ONE * 4096) {
166 focal = DTTR_UTIL_WORLDVIEW_FIXED_ONE * 240;
167 }
168
169 // Rescale focal length from reference resolution to draw rectangle, per axis.
170 double focal_x = (double)focal;
171 double focal_y = (double)focal;
172 const int32_t cam_ref_w = list_state->screen_half.width;
173 const int32_t cam_ref_h = list_state->screen_half.height;
174 if (cam_ref_w >= 64 && cam_ref_w <= 4096) {
175 focal_x = (double)focal * (double)target->width / (double)cam_ref_w;
176 }
177
178 if (cam_ref_h >= 64 && cam_ref_h <= 4096) {
179 focal_y = (double)focal * (double)target->height / (double)cam_ref_h;
180 }
181
182 *view = (DTTR_Util_WorldView){
183 .valid = true,
184 .eye = eye,
185 .forward = forward,
186 .right = right,
187 .up = up,
188 .near_fp = near_fp,
189 .center = {
190 .x = (float)target->x + (float)target->width * 0.5f,
191 .y = (float)target->y + (float)target->height * 0.5f,
192 },
193 .focal = {
194 .x = (float)focal_x,
195 .y = (float)focal_y,
196 }
197 };
198
199 return true;
200}
201
204static inline bool DTTR_Util_WorldView_ToView(
205 const DTTR_Util_WorldView *view,
206 const DTTR_PCDOGS_T_Math_Vec3I32 *world,
208) {
209 if (!view || !view->valid || !world || !out) {
210 return false;
211 }
212
213 const int64_t dx = (int64_t)world->x - view->eye.x;
214 const int64_t dy = (int64_t)world->y - view->eye.y;
215 const int64_t dz = (int64_t)world->z - view->eye.z;
216
217 out->x = (int32_t)((dx * view->right.x + dy * view->right.y + dz * view->right.z)
219 out->y = (int32_t)((dx * view->up.x + dy * view->up.y + dz * view->up.z)
221 out->z = (int32_t)((dx * view->forward.x + dy * view->forward.y
222 + dz * view->forward.z)
224
225 return true;
226}
227
232 const DTTR_Util_WorldView *view,
235) {
236 if (!view || !view->valid || !p || !out || p->z <= 0) {
237 return false;
238 }
239
240 const double max_screen_coord = 1.0e9;
241 // Scale the depth to match the focal length's Q12 units.
242 const double depth = (double)p->z * DTTR_UTIL_WORLDVIEW_FIXED_ONE;
243 const double inv_z = 1.0 / depth;
244 const double sx = view->center.x + (double)p->x * view->focal.x * inv_z;
245 const double sy = view->center.y + (double)p->y * view->focal.y * inv_z;
246 if (sx < -max_screen_coord || sx > max_screen_coord || sy < -max_screen_coord
247 || sy > max_screen_coord) {
248 return false;
249 }
250
251 out->x = (float)sx;
252 out->y = (float)sy;
253 return true;
254}
255
259 const DTTR_Util_WorldView *view,
260 const DTTR_PCDOGS_T_Math_Vec3I32 *world,
262 float *out_view_z
263) {
265 if (!DTTR_Util_WorldView_ToView(view, world, &p) || p.z <= view->near_fp
266 || !DTTR_Util_WorldView_ViewToScreen(view, &p, out)) {
267 return false;
268 }
269
270 if (out_view_z) {
271 *out_view_z = (float)p.z;
272 }
273
274 return true;
275}
276
277// Interpolate along segment a-b to the near-plane crossing.
281 int32_t near_z
282) {
283 const int64_t dz = (int64_t)b->z - a->z;
284 if (dz == 0) {
285 return *a;
286 }
287
288 const int64_t t_num = (int64_t)near_z - a->z;
290 .x = (int32_t)(a->x + (((int64_t)b->x - a->x) * t_num) / dz),
291 .y = (int32_t)(a->y + (((int64_t)b->y - a->y) * t_num) / dz),
292 .z = near_z,
293 };
294}
295
300 const DTTR_Util_WorldView *view,
301 const DTTR_PCDOGS_T_Math_Vec3I32 *world,
302 uint32_t world_count,
303 DTTR_PCDOGS_T_Math_Vec2F *out_points,
304 float *out_view_z
305) {
306 if (!view || !view->valid || !world || !out_points || world_count < 3u
307 || world_count >= DTTR_UTIL_WORLDVIEW_MAX_POLYGON_POINTS) {
308 return 0;
309 }
310
312 for (uint32_t i = 0; i < world_count; ++i) {
313 if (!DTTR_Util_WorldView_ToView(view, &world[i], &in[i])) {
314 return 0;
315 }
316 }
317
318 const int32_t near_z = view->near_fp + 1;
320 uint32_t clipped_count = 0;
321 for (uint32_t i = 0; i < world_count; ++i) {
322 const DTTR_PCDOGS_T_Math_Vec3I32 *a = &in[i];
323 const DTTR_PCDOGS_T_Math_Vec3I32 *b = &in[(i + 1u) % world_count];
324 const bool a_in = a->z >= near_z;
325 const bool b_in = b->z >= near_z;
326 if (a_in && clipped_count < DTTR_UTIL_WORLDVIEW_MAX_POLYGON_POINTS) {
327 clipped[clipped_count++] = *a;
328 }
329
330 if (a_in != b_in && clipped_count < DTTR_UTIL_WORLDVIEW_MAX_POLYGON_POINTS) {
331 clipped[clipped_count++] = dttr_util_worldview_clip_segment(a, b, near_z);
332 }
333 }
334
335 if (clipped_count < 3u) {
336 return 0;
337 }
338
339 uint32_t projected = 0;
340 for (uint32_t i = 0; i < clipped_count; ++i) {
342 if (!DTTR_Util_WorldView_ViewToScreen(view, &clipped[i], &s)) {
343 return 0;
344 }
345
346 // Same clip inputs yield bit-identical floats, so == is safe for dedup.
347 if (projected > 0 && s.x == out_points[projected - 1u].x
348 && s.y == out_points[projected - 1u].y) {
349 continue;
350 }
351
352 out_points[projected] = s;
353 if (out_view_z) {
354 out_view_z[projected] = (float)clipped[i].z;
355 }
356
357 ++projected;
358 }
359
360 if (projected >= 3u && out_points[0].x == out_points[projected - 1u].x
361 && out_points[0].y == out_points[projected - 1u].y) {
362 --projected;
363 }
364
365 return projected >= 3u ? projected : 0;
366}
367
368#ifdef __cplusplus
369}
370#endif
371
372#endif // DTTR_UTIL_WORLDVIEW_H
DTTR_Graphics_COM_Direct3DDevice7 DWORD block DTTR_Graphics_COM_Direct3DDevice7 DWORD block DTTR_Graphics_COM_Direct3DDevice7 void void void void DWORD f DTTR_Graphics_COM_Direct3DDevice7 DWORD idx float
DTTR_Graphics_COM_Direct3DDevice7 void DWORD flags DWORD void DWORD DWORD float z
DTTR_Graphics_COM_DirectDrawSurface7 DWORD x
DTTR_Graphics_COM_DirectDrawSurface7 DWORD DWORD y
DTTR_Graphics_COM_DirectDrawSurface7 DWORD flags void NULL
DTTR_Graphics_COM_DirectDrawSurface7 DWORD DWORD void void DWORD flags DTTR_Graphics_COM_DirectDrawSurface7 void void *cb void * target
DTTR_PCDOGS_API const struct DTTR_PCDOGS_D_Graphics_AdjustLevelScale_ListState_type *const DTTR_PCDOGS_D_Graphics_AdjustLevelScale_ListState
bool DTTR_StatusOK(DTTR_Status status)
Definition core.c:70
static bool DTTR_Util_MemReadable(const void *ptr, size_t size)
#define DTTR_UTIL_WORLDVIEW_MAX_POLYGON_POINTS
static bool DTTR_Util_WorldView_ToView(const DTTR_Util_WorldView *view, const DTTR_PCDOGS_T_Math_Vec3I32 *world, DTTR_PCDOGS_T_Math_Vec3I32 *out)
static int16_t dttr_util_worldview_norm_q12(int64_t component, uint32_t length)
static bool DTTR_Util_WorldView_ViewToScreen(const DTTR_Util_WorldView *view, const DTTR_PCDOGS_T_Math_Vec3I32 *p, DTTR_PCDOGS_T_Math_Vec2F *out)
static bool DTTR_Util_WorldView_ProjectPoint(const DTTR_Util_WorldView *view, const DTTR_PCDOGS_T_Math_Vec3I32 *world, DTTR_PCDOGS_T_Math_Vec2F *out, float *out_view_z)
static uint32_t DTTR_Util_WorldView_ProjectPolygon(const DTTR_Util_WorldView *view, const DTTR_PCDOGS_T_Math_Vec3I32 *world, uint32_t world_count, DTTR_PCDOGS_T_Math_Vec2F *out_points, float *out_view_z)
static DTTR_PCDOGS_T_Math_Vec3I32 dttr_util_worldview_clip_segment(const DTTR_PCDOGS_T_Math_Vec3I32 *a, const DTTR_PCDOGS_T_Math_Vec3I32 *b, int32_t near_z)
static bool DTTR_Util_WorldView_Refresh(DTTR_Util_WorldView *view, const DTTR_PCDOGS_T_Math_RectI32 *target)
#define DTTR_UTIL_WORLDVIEW_FIXED_ONE
One logical unit spans this many world fixed-point units.
static uint32_t dttr_util_worldview_isqrt(uint64_t value)
DTTR_PCDOGS_T_Math_Vec3I32XZY eye_pos
Offset 0x10.
int32_t focal_distance
Offset 0xC.
DTTR_PCDOGS_T_Math_Vec3I32XZY target_pos
Offset 0x1C.
DTTR_PCDOGS_T_Math_SizeI16 screen_half
This holds the full display-mode size (640x480). Offset 0x2C.
int32_t projection_near_fp
Fixed-point near-plane threshold. Offset 0xB4.
This rect is an origin plus unsigned size.
int16_t width
Offset 0x0.
int16_t height
Offset 0x2.
DTTR_PCDOGS_T_Math_Vec3I32 eye
The camera eye is logical world XYZ, already converted from XZY storage.
int32_t near_fp
The near plane in world fixed point.
DTTR_PCDOGS_T_Math_Vec3I16 forward
The view basis vectors (Q12).
DTTR_PCDOGS_T_Math_Vec2F center
The screen center and per-axis focal lengths are in draw-space pixels.
DTTR_PCDOGS_T_Math_Vec2F focal
DTTR_PCDOGS_T_Math_Vec3I16 up
DTTR_PCDOGS_T_Math_Vec3I16 right