diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 12be217f..aa3d5502 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -388,39 +388,48 @@ You can use tabs to show different variations of the same content, for example i This is how you use tabs: ```html {% raw %} -
-
-
- Contents of Tab 1 -
-
- -
-
- Contents of Tab 2 -
-
-
+{% capture tab_1_content %} +Contents of Tab 1 +{% endcapture %} + +{% capture tab_2_content %} +Contents of Tab 2 +{% endcapture %} + +{% capture example_tabs %} +{% include rr-tab.html title="Tab 1 Title" default=true content=tab_1_content %} +{% include rr-tab.html title="Tab 2 Title" content=tab_2_content %} +{% endcapture %} + +{% include rr-tabs.html group="example-group" tabs=example_tabs %} {% endraw %} ``` +Tab content now defaults to a vertically scrollable area with `max-height: 50vh`. +The `maxHeight` argument is optional when you want to override that default, for example `maxHeight="50vw"`. + Here is an example of what it will render: {% raw %} -
-
+{% capture c_code_example %} # Heading **Default** - C Code would be here ```c sleep(1); ``` -
- -
- **Assembly** code would be here -
-
+{% endcapture %} + +{% capture asm_code_example %} +**Assembly** code would be here +{% endcapture %} + +{% capture code_example_tabs %} +{% include rr-tab.html title="C Code Example" default=true content=c_code_example %} +{% include rr-tab.html title="Assembly Code" content=asm_code_example %} +{% endcapture %} + +{% include rr-tabs.html group="example-group" tabs=code_example_tabs %} {% endraw %} --- diff --git a/_includes/rr-tab.html b/_includes/rr-tab.html new file mode 100644 index 00000000..b64d86ba --- /dev/null +++ b/_includes/rr-tab.html @@ -0,0 +1,9 @@ +{% comment %} +Render one rr-tab pane. The inner content is wrapped in a markdown-enabled div so +pages can pass normal Markdown via a capture block. +{% endcomment %} +
+
+{{ include.content }} +
+
diff --git a/_includes/rr-tabs.html b/_includes/rr-tabs.html new file mode 100644 index 00000000..657bbddc --- /dev/null +++ b/_includes/rr-tabs.html @@ -0,0 +1,17 @@ +{% comment %} +Wrap one or more rr-tab includes in the DOM structure expected by the site tab JS. + +Usage: +{% raw %} +{% capture tab_items %} +{% include rr-tab.html title="Types" default=true content=types_content %} +{% include rr-tab.html title="Functions" content=functions_content %} +{% endcapture %} +{% include rr-tabs.html group="example-group" tabs=tab_items %} +{% endraw %} +{% endcomment %} +
+
+{{ include.tabs }} +
+
diff --git a/codex/skills/retroreversing-contributing/SKILL.md b/codex/skills/retroreversing-contributing/SKILL.md index 95bcaa23..6a39fe14 100644 --- a/codex/skills/retroreversing-contributing/SKILL.md +++ b/codex/skills/retroreversing-contributing/SKILL.md @@ -5,6 +5,7 @@ Use this skill whenever you create or edit content in the `retroReversing` repo * Target audience is technical: assume programming literacy, don't assume reverse-engineering literacy. * Prefer linking to high-quality sources over duplicating other sites' content; still provide a short summary so the page reads coherently without clicking out. * Reference sources for technical claims and dates; Wikipedia only as a last resort. +* Do not remove YouTube iframes and the reference information, they were added to the page for a reason, but you may move them to another section if required. ### Workflow (content edits) 1) Decide fit: does this belong on RetroReversing (retro game dev + reverse engineering / digital archaeology; ~1980–2015)? @@ -43,6 +44,8 @@ Use this skill whenever you create or edit content in the `retroReversing` repo 8) Code examples: * Use TypeScript when it's runnable interactively in-browser; otherwise use Python for local scripts. * Use fenced code blocks with a language tag; use `nasm` for assembly highlighting. + * On concept-heavy reference pages, place code examples directly under the section they illustrate rather than collecting them in a separate generic example section. + * Only use a standalone "examples" section when the page is explicitly structured as a tutorial, walkthrough, or end-to-end implementation guide. * Mermaid diagrams: use sparingly; keep labels short; bold the first line in nodes when there's a title + explanation. ### Preferred Site Components (use instead of ad-hoc HTML) @@ -52,7 +55,9 @@ Use this skill whenever you create or edit content in the `retroReversing` repo * Source-file callouts: `source-code-card.html` / `source-code-card-grid.html` when actually showing real file contents/symbols. * The `functions`, `variables`, and `lines` fields must be exact numeric counts from the file. * Omit the card if you can't provide exact counts yet. -* Tabs: prefer the `rr-tabs` pattern shown in `CONTRIBUTING.md` when comparing variants (e.g., C vs ASM). +* Tabs: prefer the `rr-tabs.html` and `rr-tab.html` includes shown in `CONTRIBUTING.md` when comparing variants (e.g., C vs ASM). + * `rr-tabs.html` defaults to a scrollable content area with `max-height: 50vh`. + * Use the optional `maxHeight` argument on `rr-tabs.html` when you need to override that default for a specific page. ### Images * Prefer linking externally hosted box-art (e.g. MobyGames) for "section about a specific game" figures when appropriate. @@ -69,5 +74,13 @@ Use this skill whenever you create or edit content in the `retroReversing` repo * Use cspell blocks when needed: * `` / `` +### Videos +Make sure to have a sentence before any embedded youtube video explaining who created the video and what it contains, e.g: +```markdown +[PothOnProgramming](https://www.youtube.com/watch?v=b5TjpTBW6yw) offers a technical breakdown of the 2D dot product and its critical applications in game design and engine logic. The video highlights several fundamental applications of the dot product used in game development and vector math. + + +``` + ### Reminder * `CONTRIBUTING.md` is the source of truth for edge-cases and newer patterns; consult it if unsure. diff --git a/pages/general/maths/Matrix.md b/pages/general/maths/Matrix.md index 6d870949..5e2a9871 100644 --- a/pages/general/maths/Matrix.md +++ b/pages/general/maths/Matrix.md @@ -41,10 +41,7 @@ A Vector is basically a Matrix with just 1 row, you can find out more about Vect # Nintendo DS The Nintendo DS Operating System has a basic Matrix library defined by the header file **IrisMTX.h**. This file was leaked as part of the September 2020 "Platinum leak" as it is part of the Nintendo DS Boot ROM. -
-
- -
+{% capture matrix_types_tab %} Here are the types it provides to the developer: ```c @@ -90,11 +87,9 @@ typedef union { typedef vl Mtx44 vMtx44; ``` -
-
+{% endcapture %} -
-
+{% capture matrix_functions_tab %} Here are a all of the functions it provides: ```c @@ -292,7 +287,10 @@ void MTX_MultVecArraySR( const Mtx *mult, Vec *srcBasep, Vec *dstBasep, u32 c void MTX44_MultVecArraySR(const Mtx44 *mult, Vec *srcBasep, Vec *dstBasep, u32 count); ``` -
-
-
+{% endcapture %} +{% capture matrix_tabs %} +{% include rr-tab.html title="DS Matrix Types" default=true content=matrix_types_tab %} +{% include rr-tab.html title="DS Martix Functions" content=matrix_functions_tab %} +{% endcapture %} +{% include rr-tabs.html group="group1" tabs=matrix_tabs %} diff --git a/pages/general/maths/Quaternions.md b/pages/general/maths/Quaternions.md index 74287f5e..bed70679 100644 --- a/pages/general/maths/Quaternions.md +++ b/pages/general/maths/Quaternions.md @@ -52,9 +52,7 @@ This is an excellent video which covers how to write your own Quaternion library # Nintendo DS The Nintendo DS Operating System has a small Quaternion helper library defined in the header file **IrisQUAT.h**. This file was leaked as part of the September 2020 "Platinum leak" as it is part of the Nintendo DS Boot ROM. -
-
-
+{% capture quaternion_types_tab %} Here are the types it provides to the developer: ```c @@ -78,11 +76,9 @@ typedef struct { typedef vl Quat32 vQuat32; ``` -
-
+{% endcapture %} -
-
+{% capture quaternion_functions_tab %} Here are all of the functions it provides: ```c @@ -116,7 +112,10 @@ void QUAT_Lerp( Quat *p, Quat *q, Quat *d, s32 t); void QUAT_Slerp(Quat *p, Quat *q, Quat *d, s32 t); ``` -
-
-
+{% endcapture %} +{% capture quaternion_tabs %} +{% include rr-tab.html title="DS Types" default=true content=quaternion_types_tab %} +{% include rr-tab.html title="DS Functions" content=quaternion_functions_tab %} +{% endcapture %} +{% include rr-tabs.html group="group1" tabs=quaternion_tabs %} diff --git a/pages/general/maths/Vectors.md b/pages/general/maths/Vectors.md index c20792f2..eb6caa6c 100644 --- a/pages/general/maths/Vectors.md +++ b/pages/general/maths/Vectors.md @@ -1,6 +1,6 @@ --- layout: post -tags: +tags: - maths title: Vectors (Maths for Game Developers) category: maths @@ -8,51 +8,244 @@ permalink: /Vectors breadcrumbs: - name: Home url: / + - name: Introduction + url: /introduction - name: Maths for Game Developers url: /maths - name: Vectors url: # -recommend: +recommend: - maths - introduction - sdk -editlink: /articles/maths/Vectors.md +editlink: /pages/general/maths/Vectors.md +updatedAt: '2026-04-26' --- # Introduction to Vectors -Vectors in game developer are really just a structure with 2 or more elements in it, with each element representing something like a coordinate. +A vector describes magnitude and direction [^1]. +In game code, that usually means a small structure such as `Vector2` or `Vector3`, but the storage type is only the implementation detail. +The useful part is that a vector can represent both where something is pointing and how strong that movement, force, or offset is. - +Vectors appear everywhere in game engines: +* **Position offsets** - The displacement from one object to another +* **Velocity** - Speed plus direction +* **Acceleration** - Forces such as gravity or recoil +* **Surface normals** - Perpendicular directions used for lighting and collision +* **Facing directions** - Where a camera, enemy, or projectile is aimed -Concepts in games that are commonly represented with Vectors: -* Position -* Gravity -* Velocity -* Normals +--- +## Core Operations +A few vector operations appear in almost every game codebase: + +### Addition and subtraction +Adding vectors combines offsets or forces [^1]. +Subtracting `b - a` gives the vector from point `a` to point `b`, which is why subtraction is usually the first step for steering, target tracking, and range checks [^1]. + +This small example shows both operations in a gameplay-style context: + +```ts +type Vec3 = { x: number; y: number; z: number }; + +function add(a: Vec3, b: Vec3): Vec3 { + return { x: a.x + b.x, y: a.y + b.y, z: a.z + b.z }; +} + +function subtract(a: Vec3, b: Vec3): Vec3 { + return { x: a.x - b.x, y: a.y - b.y, z: a.z - b.z }; +} + +const playerPos = { x: 4, y: 0, z: 1 }; +const step = { x: 1, y: 0, z: 0 }; +const enemyPos = { x: 10, y: 0, z: 5 }; + +const nextPlayerPos = add(playerPos, step); +const toEnemy = subtract(enemyPos, playerPos); +``` + +In the above example `nextPlayerPos` becomes `{ x: 5, y: 0, z: 1 }`, while `toEnemy` becomes `{ x: 6, y: 0, z: 4 }`. + +### Magnitude and normalization +The magnitude is the length of the vector. +Normalization keeps the direction but rescales the length to `1`, which is useful when you want a direction without carrying the original speed or distance [^2]. +If the vector has zero length, normalization has to be handled carefully because there is no valid direction to preserve [^2]. + +This short example shows how magnitude-related helpers are usually written in engine code: + +```ts +type Vec3 = { x: number; y: number; z: number }; + +function magnitude(v: Vec3): number { + return Math.sqrt(v.x * v.x + v.y * v.y + v.z * v.z); +} + +function normalize(v: Vec3): Vec3 { + const len = magnitude(v); + if (len === 0) { + return { x: 0, y: 0, z: 0 }; + } + + return { x: v.x / len, y: v.y / len, z: v.z / len }; +} + +const velocity = { x: 3, y: 4, z: 0 }; + +const speed = magnitude(velocity); +const direction = normalize(velocity); +``` + +The result of the above is `speed = 5`, while `direction` keeps the same heading but rescales the vector to unit length so it is `{ x: 0.6, y: 0.8, z: 0 }`. +The zero-vector branch here is just a defensive programming choice for the example, because a vector with no length does not have a meaningful direction to normalize. + +### Distance and squared distance +Distance between two points is the magnitude of their difference, so `distance(a, b) = length(b - a)` [^3]. +Many engines also expose squared distance because it avoids the square root step that a true magnitude calculation needs [^1][^3]. +That matters in hot code such as proximity checks, AI perception, and broad collision filtering. +For example, an enemy AI can check whether the player has entered its aggro radius, or a pickup can check whether the player is close enough to collect it, without paying for a square root every frame. + +The [Brackeys](https://www.youtube.com/watch?v=wXI9_olSrqo) video below is a good companion for this section because it walks through displacement vectors, distance, and the cost of repeated square root operations in engine code: + + + +When you only need a comparison, prefer patterns like these: +* **Range checks** - Compare `distanceSq < maxRange * maxRange` +* **Sorting nearby objects** - Compare squared distances directly +* **Cheap rejection tests** - Reject obvious misses before doing more expensive work + +This is the same optimization pattern exposed later on this page by `VEC_SquareMagnitude` and `VEC_SquareDistance`. + +Here is the same idea in code using a player and enemy position: + +```ts +type Vec3 = { x: number; y: number; z: number }; + +function subtract(a: Vec3, b: Vec3): Vec3 { + return { x: a.x - b.x, y: a.y - b.y, z: a.z - b.z }; +} + +function lengthSq(v: Vec3): number { + return v.x * v.x + v.y * v.y + v.z * v.z; +} + +const enemyPos = { x: 10, y: 0, z: 5 }; +const playerPos = { x: 4, y: 0, z: 1 }; + +// `toEnemy` is the displacement vector from the player to the enemy. +const toEnemy = subtract(enemyPos, playerPos); // result: { x: 6, y: 0, z: 4 } +// `inRange` uses squared distance to avoid an unnecessary square root. +const inRange = lengthSq(toEnemy) < 64; // 8 units squared, result: true +``` + +In this example `lengthSq(toEnemy)` is `52`, so `inRange` is `true`. + +### Dot product +The dot product measures how much one vector points in the direction of another [^1][^4]. +For normalized vectors, it collapses to the cosine of the angle between them [^4]. +It is also often called the **inner product**, especially in more formal math libraries and SDKs such as the Sony PSP VFPU headers. + +The sign and size of the result usually mean: +* **Positive** - The vectors point in roughly the same direction +* **Zero** - The vectors are perpendicular +* **Negative** - The vectors point in opposite hemispheres + +This makes dot products useful for: +* **Field-of-view checks** - Is a target broadly in front of an actor? +* **Back-face culling** - Is a triangle facing away from the camera? +* **Lighting** - How closely does a surface normal align with a light direction? +* **Signed speed tests** - How much of a velocity vector lies along a forward axis? + +[PothOnProgramming](https://www.youtube.com/watch?v=b5TjpTBW6yw) offers a technical breakdown of the 2D dot product and its critical applications in game design and engine logic. The video highlights several fundamental applications of the dot product used in game development and vector math. + + + +This snippet shows a typical field-of-view style use of the dot product: + +```ts +type Vec3 = { x: number; y: number; z: number }; + +function dot(a: Vec3, b: Vec3): number { + return a.x * b.x + a.y * b.y + a.z * b.z; +} + +function normalize(v: Vec3): Vec3 { + const len = Math.sqrt(v.x * v.x + v.y * v.y + v.z * v.z); + if (len === 0) { + return { x: 0, y: 0, z: 0 }; + } + + return { x: v.x / len, y: v.y / len, z: v.z / len }; +} + +const playerForward = normalize({ x: 1, y: 0, z: 0 }); +const toEnemy = normalize({ x: 6, y: 0, z: 4 }); +// `facingScore` moves toward `1` when the player is looking directly at the enemy and toward `-1` when facing away. +const facingScore = dot(playerForward, toEnemy); +``` + +In this example it is about `0.83`, which means the enemy is clearly in front of the player but not perfectly centered. + +### Cross product +The cross product only makes sense in 3D [^1][^5]. +It returns a new vector perpendicular to both inputs, with a magnitude based on the sine of the angle between them [^5]. +Some libraries also call this an **outer product**, although in broader linear algebra that term can mean a different operation. +In practice, game programmers use it to build triangle normals, construct camera bases, and derive `right`, `up`, or `forward` axes from each other. + +[Jorge Rodriguez](https://www.youtube.com/watch?v=FT7MShdqK6w) has a good video on how to derive a character's "right" vector from their "forward" and "up" vectors using the cross product. This video demonstrates how to implement lateral movement (strafing) relative to a camera's orientation, which is a fundamental requirement for first-person and third-person movement systems. Rodriguez provides both the mathematical theory for calculating orthogonal vectors and the practical C++ implementation needed to integrate these concepts into a game's velocity calculations. + + + +Games often use a local coordinate frame for the player or camera, so movement can be relative to where the player is facing rather than fixed world axes. The cross product helps derive directions such as forward or right from the other local axes. +In this context `right`, `up`, and `forward` are direction axes, not movement values by themselves. Movement happens when the game moves an object along one of those axes. + +This example shows how a game can derive a `forward` direction from existing axes, which is useful when building camera orientation, movement bases, or surface normals: + +```ts +type Vec3 = { x: number; y: number; z: number }; + +function cross(a: Vec3, b: Vec3): Vec3 { + return { + x: a.y * b.z - a.z * b.y, + y: a.z * b.x - a.x * b.z, + z: a.x * b.y - a.y * b.x, + }; +} -Functions that you can apply to vectors -* Dot Product -* Cross Product -* Distance (e.g between 2 positions) -* Add/Subtract/Scale -* Linearly interpolates between two points (Lerp) -* Normalise (set length to 1 but keep direction) +const right = { x: 1, y: 0, z: 0 }; // the player's right +const up = { x: 0, y: 1, z: 0 }; // the player's up +const forward = cross(right, up); // the direction the player or camera is facing +``` + +In this basis `forward` becomes `{ x: 0, y: 0, z: 1 }`. +If you swap the argument order, the sign flips, which is why operand order matters when working with handedness. +If you want to continue from vectors into transforms and rotations, the Matrix page is the natural next step: +{% include_cached link-to-other-post.html post="/Matrix" description="Matrices build on vector operations for transforms such as translation, rotation, and projection." %} --- -# Nintendo DS -The Nintendo DS Operating System has a basic Matrix library defined by the header file **IrisVEC.h**. This file was leaked as part of the September 2020 "Platinum leak" as it is part of the Nintendo DS Boot ROM. +## Vector Libraries used in Retail Console Game Development +Looking at retail SDK headers is useful because it shows which operations console programmers expected to use frequently. + +### Nintendo DS Official Vector Library +The Nintendo DS boot ROM headers expose a compact vector helper API in `IrisVEC.h`, which is catalogued in this site's Platinum leak coverage [^6]. +Before looking at the declarations, a few details stand out: +* **Multiple storage widths** - `Vec10`, `Vec16`, and `Vec32` show that Nintendo exposed vector math for different precision and packing tradeoffs +* **Bit-packed coordinates** - `Vec10` uses 10-bit fields, which is unusual enough to suggest tight memory or hardware-format constraints +* **Performance-aware helpers** - `VEC_SquareMagnitude`, `VEC_SquareDistance`, and `Fast` variants reflect the same cost-saving patterns seen in modern engines +* **2D and 3D support** - The header mixes full 3D structs with helper macros such as `VEC2D_DotProduct` + +The fixed-point flavour is especially noticeable here. +Both `VEC2D_DotProduct` and `VEC2D_CrossProduct` shift by `V_SFT`, which looks more like a scaling constant than a floating-point API. -
-
-
-Here are the types it provides to the developer: +Combined with `Vec10` and the separate `Fast` variants, the header reads like an interface designed around compact integer storage and predictable performance on DS-era hardware. + +{% capture ds_vector_types_tab %} +Here are the main storage types exposed by the header: ```c #define vl volatile -// 10-bit (slightly unusual to use 10-bits!) +// 10-bit packed coordinates typedef struct { s32 x:10; s32 y:10; @@ -61,12 +254,12 @@ typedef struct { typedef vl Pos10 vPos10; typedef vl Vec10 vVec10; -// 16-bit +// 16-bit typedef struct { s16 x; s16 y; s16 z; - s16 w; + s16 w; } Vec16, Vec, Pos16, TestPos; typedef vl Pos16 vPos16; typedef vl Vec16 vVec16; @@ -83,12 +276,10 @@ typedef vl Pos32 vPos32; typedef vl Pos vPos; ``` -
-
- -
-
-Here are a few of the functions it provides: +{% endcapture %} + +{% capture ds_vector_functions_tab %} +Here are the main vector helpers exposed by the header: ```c void VEC_Copy2Vec10(const Vec *srcp, Vec10 *dstp); @@ -114,15 +305,11 @@ void VEC32_Normalize( Vec32 *srcp, Vec32 *dstp); void VEC32_Normalize2Vec(Vec32 *srcp, Vec *dstp); void VEC_Add(Vec *a, Vec *b, Vec *ab); - void VEC_Sub(Vec *a, Vec *b, Vec *a_b); - void VEC_Scale(Vec *srcp, Vec *dstp, s32 scale); - void VEC_Reverse(const Vec *srcp, Vec *dstp); u32 VEC_Magnitude(const Vec *v); - u32 VEC_SquareMagnitude(const Vec *v); u32 VEC_Distance(const Vec *a, const Vec *b); @@ -133,10 +320,264 @@ void VEC32_Lerp(Vec32 *a, Vec32 *b, Vec32 *d, s32 t); void VEC32_LerpFast(Vec32 *a, Vec32 *b, Vec32 *d, s32 t); ``` -
-
-
+{% endcapture %} + +{% capture ds_vector_tabs %} +{% include rr-tab.html title="Types" default=true content=ds_vector_types_tab %} +{% include rr-tab.html title="Functions" content=ds_vector_functions_tab %} +{% endcapture %} +{% include rr-tabs.html group="group1" tabs=ds_vector_tabs %} + +You can find out more about the Nintendo DS Boot ROM in the Platinum leak: +{% include_cached link-to-other-post.html post="/platinumleak" description="For more information on the Nintendo Platinum leak that exposed these DS headers, check out this post." %} + +--- +### Sony PSP Vector Library +The official PlayStation Portable (PSP) SDK exposes vector types such as `ScePspVector2`, `ScePspVector3` through the `psptypes.h` header and vector functions through the VFPU library header `libvfpu.h`. +What makes the PSP vector API interesting is that it looks much closer to a modern real-time graphics math library than the simpler DS helper headers. + +The use of floating-point vector types, 16-byte-aligned 4D vectors, and operations such as dot product, cross product, normalization, lerp, reflection, refraction, and face-forward suggests an API designed around the PSP's VFPU and 3D rendering workloads rather than just basic gameplay math. + +The repeated `XYZ` variants are especially telling, because they imply that many engine data structures were stored in 4D form while still treating only the first three components as position or direction data. + +{% capture psp_vector_types_tab %} +Here are the main storage types exposed by the header (`psptypes.h`). +The prefix tells you the underlying storage format: `S` means `short`, `I` means `int`, `L64` means 64-bit integer storage, and `F` means `float`. + +The union forms such as `ScePspVector2` and `ScePspVector3` are useful because they let the same bytes be viewed either as named vector structs (`fv`, `iv`) or plain arrays (`f[]`, `i[]`), which makes it easier to switch between component-wise code and bulk math or VFPU-oriented helper code: + ```c + // 2D Vectors + typedef struct ScePspSVector2 { + short x, y; + } ScePspSVector2; + + typedef struct ScePspIVector2 { + int x, y; + } ScePspIVector2; + + typedef struct ScePspL64Vector2 { + SceLong64 x, y; + } ScePspL64Vector2; + + typedef struct ScePspFVector2 { + float x, y; + } ScePspFVector2; + + typedef union ScePspVector2 { + ScePspFVector2 fv; + ScePspIVector2 iv; + float f[2]; + int i[2]; + struct { + float fx, fy; + }; + struct { + int ix, iy; + }; + } ScePspVector2; + + // 3D Vectors + typedef struct ScePspSVector3 { + short x, y, z; + } ScePspSVector3; + + typedef struct ScePspIVector3 { + int x, y, z; + } ScePspIVector3; + + typedef struct ScePspL64Vector3 { + SceLong64 x, y, z; + } ScePspL64Vector3; + + typedef struct ScePspFVector3 { + float x, y, z; + } ScePspFVector3; + + typedef union ScePspVector3 { + ScePspFVector3 fv; + ScePspIVector3 iv; + float f[3]; + int i[3]; + struct { + float fx, fy, fz; + }; + struct { + int ix, iy, iz; + }; + } ScePspVector3; + // 4D Vectors + typedef struct ScePspSVector4 { + short x, y, z, w; + } ScePspSVector4; + typedef struct ScePspIVector4 { + int x, y, z, w; + } ScePspIVector4 __attribute__((aligned(16))); + typedef struct ScePspIVector4Unaligned { + int x, y, z, w; + } ScePspIVector4Unaligned; + + typedef struct ScePspL64Vector4 { + SceLong64 x, y, z, w; + } ScePspL64Vector4; + + typedef struct ScePspFVector4 { + float x, y, z, w; + } ScePspFVector4 __attribute__((aligned(16))); + + typedef struct ScePspFVector4Unaligned { + float x, y, z, w; + } ScePspFVector4Unaligned; + + typedef union ScePspVector4 { + ScePspFVector4 fv; + ScePspIVector4 iv; + SceULong128 qw; + float f[4]; + int i[4]; + struct { + float fx, fy, fz, fw; + }; + struct { + int ix, iy, iz, iw; + }; + } ScePspVector4 __attribute__((aligned(16))); + #define SVector2 ScePspSVector2 + #define IVector2 ScePspIVector2 + #define L64Vector2 ScePspL64Vector2 + #define FVector2 ScePspFVector2 + #define Vector2 ScePspVector2 + + #define SVector3 ScePspSVector3 + #define IVector3 ScePspIVector3 + #define L64Vector3 ScePspL64Vector3 + #define FVector3 ScePspFVector3 + #define Vector3 ScePspVector3 + + #define SVector4 ScePspSVector4 + #define IVector4 ScePspIVector4 + #define L64Vector4 ScePspL64Vector4 + #define FVector4 ScePspFVector4 + #define Vector4 ScePspVector4 + ``` +{% endcapture %} + +{% capture psp_vector_functions_tab %} +Here are the main vector helpers exposed by the header `libvfpu.h` [^7]. They are repeated for each of the vector sizes, so this tab only shows one representative declaration and lists the related variants in a trailing comment. + +Some of the less obvious helpers are worth explaining before reading the declarations [^8]: +* `PositiveZero` and `NegativeZero` show that the PSP SDK cared about exact floating-point bit patterns as well as numeric value. In the source, the negative-zero helpers write the `0x80000000` sign-bit pattern directly into each float lane. +* `Ceil`, `Trunc`, `Round`, `Floor`, and `FromIVector` make the bridge between float vectors and integer vectors explicit. That is useful when an engine moves between VFPU math, grid or tile coordinates, screen-space values, and packed gameplay data. +* `Clamp`, `Min`, `Max`, `Abs`, and `Neg` are all component-wise cleanup helpers. They are the kinds of operations you need when bounding movement, constraining camera input, mirroring directions, or sanitising values before later math. +* `InnerProduct` is the PSP SDK's formal name for the dot product, while `OuterProduct` is used as the implementation name behind the SDK's cross-product helpers. +* `Funnel` is an unusual name, but the implementation shows that it literally sums the components of the vector. `Average` does the same reduction and then divides by the number of components. +* `FaceForward`, `Reflect`, and `Refract` are surface-response helpers that fit naturally with lighting, collision response, and other rendering-style calculations. +* `NormalizePhase` is not vector normalization in the usual magnitude sense. The implementation wraps each component back into the `[-pi, +pi]` range, so it is better understood as angle or phase normalisation. + +```c +// Initialize vectors to positive or negative zero +#define sceVfpuVector2Null(_pv) sceVfpuVector2PositiveZero(_pv) // Variants: sceVfpuVector2Zero, sceVfpuVector3Null, sceVfpuVector3Zero, sceVfpuVector4Null, sceVfpuVector4Zero +ScePspFVector2 *sceVfpuVector2PositiveZero(ScePspFVector2 *pv); // Variants: sceVfpuVector3PositiveZero, sceVfpuVector4PositiveZero +ScePspFVector2 *sceVfpuVector2NegativeZero(ScePspFVector2 *pv); // Variants: sceVfpuVector3NegativeZero, sceVfpuVector4NegativeZero + +// Set and copy vectors +ScePspFVector2 *sceVfpuVector2Set(ScePspFVector2 *pv0, float x, float y); // Variants: sceVfpuVector3Set, sceVfpuVector4Set +ScePspFVector4 *sceVfpuVector4SetXYZ(ScePspFVector4 *pv0, float x, float y, float z); +ScePspFVector2 *sceVfpuVector2Copy(ScePspFVector2 *pv0, const ScePspFVector2 *pv1); // Variants: sceVfpuVector3Copy, sceVfpuVector4Copy + +// Convert floating-point vectors to integer vectors +ScePspIVector2 *sceVfpuVector2Ceil(ScePspIVector2 *pv0, const ScePspFVector2 *pv1); // Variants: sceVfpuVector3Ceil, sceVfpuVector4Ceil +ScePspIVector2 *sceVfpuVector2Trunc(ScePspIVector2 *pv0, const ScePspFVector2 *pv1); // Variants: sceVfpuVector3Trunc, sceVfpuVector4Trunc +ScePspIVector2 *sceVfpuVector2Round(ScePspIVector2 *pv0, const ScePspFVector2 *pv1); // Variants: sceVfpuVector3Round, sceVfpuVector4Round +ScePspIVector2 *sceVfpuVector2Floor(ScePspIVector2 *pv0, const ScePspFVector2 *pv1); // Variants: sceVfpuVector3Floor, sceVfpuVector4Floor + +// Convert integer vectors to floating-point vectors +ScePspFVector2 *sceVfpuVector2FromIVector(ScePspFVector2 *pv0, const ScePspIVector2 *pv1); // Variants: sceVfpuVector3FromIVector, sceVfpuVector4FromIVector + +// Component-wise arithmetic +ScePspFVector2 *sceVfpuVector2Add(ScePspFVector2 *pv0, const ScePspFVector2 *pv1, const ScePspFVector2 *pv2); // Variants: sceVfpuVector3Add, sceVfpuVector4Add +ScePspFVector4 *sceVfpuVector4AddXYZ(ScePspFVector4 *pv0, const ScePspFVector4 *pv1, const ScePspFVector4 *pv2); + +#define sceVfpuVector2Subtract(_pv0, _pv1, _pv2) sceVfpuVector2Sub(_pv0, _pv1, _pv2) // Variants: sceVfpuVector3Subtract, sceVfpuVector4Subtract +#define sceVfpuVector4SubtractXYZ(_pv0, _pv1, _pv2) sceVfpuVector4SubXYZ(_pv0, _pv1, _pv2) +ScePspFVector2 *sceVfpuVector2Sub(ScePspFVector2 *pv0, const ScePspFVector2 *pv1, const ScePspFVector2 *pv2); // Variants: sceVfpuVector3Sub, sceVfpuVector4Sub +ScePspFVector4 *sceVfpuVector4SubXYZ(ScePspFVector4 *pv0, const ScePspFVector4 *pv1, const ScePspFVector4 *pv2); + +#define sceVfpuVector2Multiply(_pv0, _pv1, _pv2) sceVfpuVector2Mul(_pv0, _pv1, _pv2) // Variants: sceVfpuVector3Multiply, sceVfpuVector4Multiply +#define sceVfpuVector4MultiplyXYZ(_pv0, _pv1, _pv2) sceVfpuVector4MulXYZ(_pv0, _pv1, _pv2) +ScePspFVector2 *sceVfpuVector2Mul(ScePspFVector2 *pv0, const ScePspFVector2 *pv1, const ScePspFVector2 *pv2); // Variants: sceVfpuVector3Mul, sceVfpuVector4Mul +ScePspFVector4 *sceVfpuVector4MulXYZ(ScePspFVector4 *pv0, const ScePspFVector4 *pv1, const ScePspFVector4 *pv2); + +#define sceVfpuVector2Divide(_pv0, _pv1, _pv2) sceVfpuVector2Div(_pv0, _pv1, _pv2) // Variants: sceVfpuVector3Divide, sceVfpuVector4Divide +#define sceVfpuVector4DivideXYZ(_pv0, _pv1, _pv2) sceVfpuVector4DivXYZ(_pv0, _pv1, _pv2) +ScePspFVector2 *sceVfpuVector2Div(ScePspFVector2 *pv0, const ScePspFVector2 *pv1, const ScePspFVector2 *pv2); // Variants: sceVfpuVector3Div, sceVfpuVector4Div +ScePspFVector4 *sceVfpuVector4DivXYZ(ScePspFVector4 *pv0, const ScePspFVector4 *pv1, const ScePspFVector4 *pv2); + +// Unary operations and range helpers +ScePspFVector2 *sceVfpuVector2Neg(ScePspFVector2 *pv0, const ScePspFVector2 *pv1); // Variants: sceVfpuVector3Neg, sceVfpuVector4Neg +ScePspFVector2 *sceVfpuVector2Abs(ScePspFVector2 *pv0, const ScePspFVector2 *pv1); // Variants: sceVfpuVector3Abs, sceVfpuVector4Abs +ScePspFVector2 *sceVfpuVector2Clamp(ScePspFVector2 *pv0, const ScePspFVector2 *pv1, float min, float max); // Variants: sceVfpuVector3Clamp, sceVfpuVector4Clamp +ScePspFVector4 *sceVfpuVector4ClampXYZ(ScePspFVector4 *pv0, const ScePspFVector4 *pv1, float min, float max); +ScePspFVector2 *sceVfpuVector2Max(ScePspFVector2 *pv0, const ScePspFVector2 *pv1, const ScePspFVector2 *pv2); // Variants: sceVfpuVector3Max, sceVfpuVector4Max +ScePspFVector2 *sceVfpuVector2Min(ScePspFVector2 *pv0, const ScePspFVector2 *pv1, const ScePspFVector2 *pv2); // Variants: sceVfpuVector3Min, sceVfpuVector4Min + +// Scale and interpolation +ScePspFVector2 *sceVfpuVector2Scale(ScePspFVector2 *pv0, const ScePspFVector2 *pv1, float s); // Variants: sceVfpuVector3Scale, sceVfpuVector4Scale +ScePspFVector4 *sceVfpuVector4ScaleXYZ(ScePspFVector4 *pv0, const ScePspFVector4 *pv1, float s); +#define sceVfpuVector2Inter(_pv0,_pv1,_pv2,_t) sceVfpuVector2Lerp(_pv0,_pv1,_pv2,(1.0f - (_t))) // Variants: sceVfpuVector3Inter, sceVfpuVector4Inter +ScePspFVector2 *sceVfpuVector2Lerp(ScePspFVector2 *pv0, const ScePspFVector2 *pv1, const ScePspFVector2 *pv2, float t); // Variants: sceVfpuVector3Lerp, sceVfpuVector4Lerp +#define sceVfpuVector4InterXYZ(_pv0,_pv1,_pv2,_t) sceVfpuVector4LerpXYZ(_pv0,_pv1,_pv2,(1.0f - (_t))) +ScePspFVector4 *sceVfpuVector4LerpXYZ(ScePspFVector4 *pv0, const ScePspFVector4 *pv1, const ScePspFVector4 *pv2, float t); + +// Dot and cross products +#define sceVfpuVector2Dot(_v1,_v2) sceVfpuVector2InnerProduct(_v1,_v2) // Variants: sceVfpuVector3Dot, sceVfpuVector4Dot +#define sceVfpuVector4DotXYZ(_v1,_v2) sceVfpuVector4InnerProductXYZ(_v1,_v2) +float sceVfpuVector2InnerProduct(const ScePspFVector2 *pv1, const ScePspFVector2 *pv2); // Variants: sceVfpuVector3InnerProduct, sceVfpuVector4InnerProduct +float sceVfpuVector4InnerProductXYZ(const ScePspFVector4 *pv1, const ScePspFVector4 *pv2); +#define sceVfpuVector3Cross(_v0,_v1,_v2) sceVfpuVector3OuterProduct(_v0,_v1,_v2) // Variant: sceVfpuVector4Cross +ScePspFVector3 *sceVfpuVector3OuterProduct(ScePspFVector3 *pv0, const ScePspFVector3 *pv1, const ScePspFVector3 *pv2); // Variant: sceVfpuVector4OuterProductXYZ +ScePspFVector4 *sceVfpuVector4OuterProductXYZ(ScePspFVector4 *pv0, const ScePspFVector4 *pv1, const ScePspFVector4 *pv2); + +// Aggregate queries and comparison helpers +float sceVfpuVector2Funnel(const ScePspFVector2 *pv); // Variants: sceVfpuVector3Funnel, sceVfpuVector4Funnel +float sceVfpuVector2Average(const ScePspFVector2 *pv); // Variants: sceVfpuVector3Average, sceVfpuVector4Average +SceBool sceVfpuVector2IsEqual(const ScePspFVector2 *pv1, const ScePspFVector2 *pv2); // Variants: sceVfpuVector3IsEqual, sceVfpuVector4IsEqual +#define sceVfpuVector2IsNull(_pv) sceVfpuVector2IsZero(_pv) // Variants: sceVfpuVector3IsNull, sceVfpuVector4IsNull +SceBool sceVfpuVector2IsZero(const ScePspFVector2 *pv0); // Variants: sceVfpuVector3IsZero, sceVfpuVector4IsZero +ScePspFVector2 *sceVfpuVector2SignFloat(ScePspFVector2 *pv0, const ScePspFVector2 *pv1); // Variants: sceVfpuVector3SignFloat, sceVfpuVector4SignFloat +ScePspIVector2 *sceVfpuVector2SignInt(ScePspIVector2 *piv, const ScePspFVector2 *pfv); // Variants: sceVfpuVector3SignInt, sceVfpuVector4SignInt + +// Length, normalization, and geometric helpers +ScePspFVector2 *sceVfpuVector2Normalize(ScePspFVector2 *pv0, const ScePspFVector2 *pv1); // Variants: sceVfpuVector3Normalize, sceVfpuVector4Normalize +ScePspFVector4 *sceVfpuVector4NormalizeXYZ(ScePspFVector4 *pv0, const ScePspFVector4 *pv1); +float sceVfpuVector2Length(const ScePspFVector2 *pfv); // Variants: sceVfpuVector3Length, sceVfpuVector4LengthXYZ +float sceVfpuVector2Distance(const ScePspFVector2 *pv1, const ScePspFVector2 *pv2); // Variants: sceVfpuVector3Distance, sceVfpuVector4DistanceXYZ +ScePspFVector4 *sceVfpuVector4NormalizePhase(ScePspFVector4 *pv0, const ScePspFVector4 *pv1); +ScePspFVector2 *sceVfpuVector2FaceForward(ScePspFVector2 *pv0, const ScePspFVector2 *pv1, const ScePspFVector2 *pv2, const ScePspFVector2 *pv3); // Variants: sceVfpuVector3FaceForward, sceVfpuVector2FaceForwardXYZ +ScePspFVector4 *sceVfpuVector2FaceForwardXYZ(ScePspFVector4 *pv0, const ScePspFVector4 *pv1, const ScePspFVector4 *pv2, const ScePspFVector4 *pv3); +ScePspFVector2 *sceVfpuVector2Reflect(ScePspFVector2 *pv0, const ScePspFVector2 *pv1, const ScePspFVector2 *pv2); // Variants: sceVfpuVector3Reflect, sceVfpuVector4ReflectXYZ +ScePspFVector4 *sceVfpuVector4ReflectXYZ(ScePspFVector4 *pv0, const ScePspFVector4 *pv1, const ScePspFVector4 *pv2); +ScePspFVector2 *sceVfpuVector2Refract(ScePspFVector2 *pv0, const ScePspFVector2 *pv1, const ScePspFVector2 *pv2, float eta); // Variants: sceVfpuVector3Refract, sceVfpuVector4RefractXYZ +ScePspFVector4 *sceVfpuVector4RefractXYZ(ScePspFVector4 *pv0, const ScePspFVector4 *pv1, const ScePspFVector4 *pv2, float eta); +``` +{% endcapture %} + +{% capture psp_vector_tabs %} +{% include rr-tab.html title="PSP Vector Types" default=true content=psp_vector_types_tab %} +{% include rr-tab.html title="PSP Vector Functions" content=psp_vector_functions_tab %} +{% endcapture %} +{% include rr-tabs.html group="psp-group1" tabs=psp_vector_tabs %} + +--- +# References +[^1]: [Unity Manual - Moving objects with vectors](https://docs.unity3d.com/Manual/VectorCookbook.html) +[^2]: [Unity Scripting API - Vector3.Normalize](https://docs.unity3d.com/ScriptReference/Vector3.Normalize.html) +[^3]: [Unity Scripting API - Vector3.Distance](https://docs.unity3d.com/ScriptReference/Vector3.Distance.html) +[^4]: [Unity Scripting API - Vector3.Dot](https://docs.unity3d.com/ScriptReference/Vector3.Dot.html) +[^5]: [Unity Scripting API - Vector3.Cross](https://docs.unity3d.com/ScriptReference/Vector3.Cross.html) +[^6]: [RetroReversing - Nintendo Platinum Leak](/platinumleak) +[^7]: Sony PSP SDK headers `psptypes.h` and `libvfpu.h`. +[^8]: Sony PSP SDK implementations `src/vfpu/vector2.c`, `src/vfpu/vector3.c`, and `src/vfpu/vector4.c`. diff --git a/public/css/theme.overrides.css b/public/css/theme.overrides.css index 491c7eee..0b46bb3c 100644 --- a/public/css/theme.overrides.css +++ b/public/css/theme.overrides.css @@ -280,6 +280,11 @@ rr-sandpack { border-bottom-right-radius: 8px; border-bottom-left-radius: 8px; } + +.rr-tabs .rr-tab div p +{ + padding-left: 0px !important; /** Override the padding added to p tags in .row when in a tab **/ +} /* #endregion Tabs */ /* ILBM Viewer */