Skip to content

Commit 12374bb

Browse files
committed
Reworked Matrix4
1 parent 334bae5 commit 12374bb

File tree

2 files changed

+113
-159
lines changed

2 files changed

+113
-159
lines changed

Engine/Matrix4.cs

Lines changed: 96 additions & 149 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,27 @@
1+
/*
2+
* Matrix4.cs
3+
*
4+
* Represents a 4x4 matrix with float components for 3D transformations.
5+
* Designed for graphics, raytracing, and general linear algebra operations.
6+
*
7+
* Features:
8+
* - Operator overloads for matrix addition (+), subtraction (-), and multiplication (*)
9+
* - Scalar multiplication and matrix-vector multiplication
10+
* - Mutable operations via MultiplyThisBy for in-place updates
11+
* - "Load" methods (LoadIdentity, LoadZero, LoadTranslate, LoadScale, LoadRotate)
12+
* that initialize matrices via out parameters
13+
* - Indexer for convenient element access: matrix[i, j]
14+
*
15+
* Usage:
16+
* Matrix4.LoadIdentity(out var m); // loads identity matrix
17+
* Matrix4.LoadTranslate(1, 2, 3, out var t); // translation matrix
18+
* var combined = m * t; // combine transforms
19+
* var vTransformed = combined.Multiply(vector); // transform a Vector3D
20+
*
21+
* Author: Dan Munteanu
22+
* Date: 28-Sep-2025
23+
*/
24+
125
namespace Unilight
226
{
327
public class Matrix4
@@ -9,220 +33,143 @@ public enum Axis { XAxis, YAxis, ZAxis };
933
public Matrix4()
1034
{
1135
for (int i = 0; i < 4; ++i)
12-
{
1336
for (int j = 0; j < 4; ++j)
14-
{
1537
values[i, j] = 0;
16-
}
17-
}
1838
}
1939

20-
public Matrix4 Add(Matrix4 m)
40+
// Indexer for convenience
41+
public float this[int i, int j]
42+
{
43+
get => values[i, j];
44+
set => values[i, j] = value;
45+
}
46+
47+
// Operator overloads
48+
public static Matrix4 operator +(Matrix4 a, Matrix4 b)
2149
{
2250
Matrix4 result = new Matrix4();
2351
for (int i = 0; i < 4; ++i)
24-
{
2552
for (int j = 0; j < 4; ++j)
26-
{
27-
result.SetAt(i, j, values[i, j] + m.values[i, j]);
28-
}
29-
}
53+
result[i, j] = a[i, j] + b[i, j];
3054
return result;
3155
}
3256

33-
public void IncreaseBy(Matrix4 m)
57+
public static Matrix4 operator -(Matrix4 a, Matrix4 b)
3458
{
59+
Matrix4 result = new Matrix4();
3560
for (int i = 0; i < 4; ++i)
36-
{
3761
for (int j = 0; j < 4; ++j)
38-
{
39-
values[i, j] += m.values[i, j];
40-
}
41-
}
62+
result[i, j] = a[i, j] - b[i, j];
63+
return result;
4264
}
4365

44-
public Matrix4 Subtract(Matrix4 m)
66+
public static Matrix4 operator *(Matrix4 a, float scalar)
4567
{
4668
Matrix4 result = new Matrix4();
4769
for (int i = 0; i < 4; ++i)
48-
{
4970
for (int j = 0; j < 4; ++j)
50-
{
51-
result.SetAt(i, j, values[i, j] - m.values[i, j]);
52-
}
53-
}
71+
result[i, j] = a[i, j] * scalar;
5472
return result;
5573
}
5674

57-
public void DecreaseBy(Matrix4 m)
75+
public static Matrix4 operator *(Matrix4 a, Matrix4 b)
5876
{
77+
Matrix4 result = new Matrix4();
5978
for (int i = 0; i < 4; ++i)
6079
{
6180
for (int j = 0; j < 4; ++j)
6281
{
63-
values[i, j] -= m.values[i, j];
82+
float sum = 0;
83+
for (int k = 0; k < 4; ++k)
84+
sum += a[i, k] * b[k, j];
85+
result[i, j] = sum;
6486
}
6587
}
88+
return result;
6689
}
6790

68-
public void MultiplyThisBy(float scalar)
91+
// Multiply a vector
92+
public Vector3D MultiplyBy(Vector3D v)
6993
{
70-
for (int i = 0; i < 4; ++i)
94+
return new Vector3D
7195
{
72-
for (int j = 0; j < 4; ++j)
73-
{
74-
values[i, j] *= scalar;
75-
}
76-
}
96+
X = values[0, 0] * v.X + values[0, 1] * v.Y + values[0, 2] * v.Z + values[0, 3],
97+
Y = values[1, 0] * v.X + values[1, 1] * v.Y + values[1, 2] * v.Z + values[1, 3],
98+
Z = values[2, 0] * v.X + values[2, 1] * v.Y + values[2, 2] * v.Z + values[2, 3]
99+
};
77100
}
78101

79-
public Matrix4 Multiply(Matrix4 m)
102+
// Mutable operations
103+
public void MultiplyThisBy(Matrix4 m)
80104
{
81-
Matrix4 result = new Matrix4();
105+
var result = this * m;
82106
for (int i = 0; i < 4; ++i)
83-
{
84107
for (int j = 0; j < 4; ++j)
85-
{
86-
float v = 0;
87-
for (int k = 0; k < 4; ++k)
88-
{
89-
v += values[i, k] * m.values[k, j];
90-
}
91-
result.values[i, j] = v;
92-
}
93-
}
94-
return result;
108+
values[i, j] = result[i, j];
95109
}
96110

97-
public void MultiplyThisBy(Matrix4 m)
111+
public void MultiplyThisBy(float scalar)
98112
{
99-
Matrix4 result = this.Multiply(m);
100113
for (int i = 0; i < 4; ++i)
101-
{
102114
for (int j = 0; j < 4; ++j)
103-
{
104-
values[i, j] = result.values[i, j];
105-
}
106-
}
115+
values[i, j] *= scalar;
107116
}
108117

109-
public Vector3D Multiply(Vector3D v)
118+
// ---Convenience "Load" methods (void with out parameter)
119+
public static void LoadIdentity(out Matrix4 result)
110120
{
111-
Vector3D result = new Vector3D();
112-
113-
// compute x
114-
float sum = 0;
115-
for (int j = 0; j < 3; ++j)
116-
{
117-
sum += values[0, j] * v.X;
118-
}
119-
sum += values[0, 3];
120-
result.X = sum;
121-
122-
// compute y
123-
sum = 0;
124-
for (int j = 0; j < 3; ++j)
125-
{
126-
sum += values[1, j] * v.Y;
127-
}
128-
sum += values[1, 3];
129-
result.Y = sum;
130-
131-
// compute z
132-
sum = 0;
133-
for (int j = 0; j < 3; ++j)
134-
{
135-
sum += values[2, j] * v.Z;
136-
}
137-
sum += values[2, 3];
138-
result.Z = sum;
121+
result = new Matrix4();
122+
result[0, 0] = 1; result[1, 1] = 1; result[2, 2] = 1; result[3, 3] = 1;
123+
}
139124

140-
return result;
125+
public static void LoadZero(out Matrix4 result)
126+
{
127+
result = new Matrix4(); // all elements already 0
141128
}
142129

143-
public void SetAt(int i, int j, float value)
130+
public static void LoadTranslate(float tx, float ty, float tz, out Matrix4 result)
144131
{
145-
values[i, j] = value;
132+
LoadIdentity(out result);
133+
result[0, 3] = tx;
134+
result[1, 3] = ty;
135+
result[2, 3] = tz;
146136
}
147137

148-
public float GetAt(int i, int j)
138+
public static void LoadScale(float sx, float sy, float sz, out Matrix4 result)
149139
{
150-
return values[i, j];
140+
LoadIdentity(out result);
141+
result[0, 0] = sx;
142+
result[1, 1] = sy;
143+
result[2, 2] = sz;
151144
}
152145

153-
// x = 0, y = 1, z = 1
154-
public static Matrix4 rotate(float angle, Matrix4.Axis axis)
146+
public static void LoadRotate(float angle, Axis axis, out Matrix4 result)
155147
{
156-
Matrix4 result = new Matrix4();
157-
float cosAngle = (float)Math.Cos(angle);
158-
float sinAngle = (float)Math.Sin(angle); ;
148+
LoadIdentity(out result);
149+
float cos = (float)Math.Cos(angle);
150+
float sin = (float)Math.Sin(angle);
151+
159152
switch (axis)
160153
{
161-
case Matrix4.Axis.XAxis:
162-
result.SetAt(0, 0, 1);
163-
result.SetAt(1, 1, cosAngle);
164-
result.SetAt(1, 2, sinAngle);
165-
result.SetAt(2, 1, -sinAngle);
166-
result.SetAt(2, 2, cosAngle);
154+
case Axis.XAxis:
155+
result[1, 1] = cos; result[1, 2] = sin;
156+
result[2, 1] = -sin; result[2, 2] = cos;
167157
break;
168158

169-
case Matrix4.Axis.YAxis:
170-
result.SetAt(0, 0, cosAngle);
171-
result.SetAt(0, 2, -sinAngle);
172-
result.SetAt(1, 1, 1);
173-
result.SetAt(2, 0, sinAngle);
174-
result.SetAt(2, 2, cosAngle);
159+
case Axis.YAxis:
160+
result[0, 0] = cos; result[0, 2] = -sin;
161+
result[2, 0] = sin; result[2, 2] = cos;
175162
break;
176163

177-
case Matrix4.Axis.ZAxis:
178-
result.SetAt(0, 0, cosAngle);
179-
result.SetAt(0, 1, sinAngle);
180-
result.SetAt(1, 0, -sinAngle);
181-
result.SetAt(1, 1, cosAngle);
182-
result.SetAt(2, 2, 1);
164+
case Axis.ZAxis:
165+
result[0, 0] = cos; result[0, 1] = sin;
166+
result[1, 0] = -sin; result[1, 1] = cos;
183167
break;
184168
}
185-
result.SetAt(3, 3, 1);
186-
return result;
187-
}
188-
189-
public static Matrix4 translate(float tx, float ty, float tz)
190-
{
191-
Matrix4 result = new Matrix4();
192-
result.SetAt(0, 3, tx);
193-
result.SetAt(1, 3, ty);
194-
result.SetAt(2, 3, tz);
195-
result.SetAt(0, 0, 1);
196-
result.SetAt(1, 1, 1);
197-
result.SetAt(2, 2, 1);
198-
result.SetAt(3, 3, 1);
199-
return result;
200-
}
201-
202-
public static Matrix4 scale(float sx, float sy, float sz)
203-
{
204-
Matrix4 result = new Matrix4();
205-
result.SetAt(0, 0, sx);
206-
result.SetAt(1, 1, sy);
207-
result.SetAt(2, 2, sz);
208-
result.SetAt(3, 3, 1);
209-
return result;
210-
}
211-
212-
public static Matrix4 zero()
213-
{
214-
return new Matrix4();
215169
}
216170

217-
public static Matrix4 identity()
218-
{
219-
Matrix4 result = new Matrix4();
220-
result.SetAt(0, 0, 1);
221-
result.SetAt(1, 1, 1);
222-
result.SetAt(2, 2, 1);
223-
result.SetAt(3, 3, 1);
224-
return result;
225-
}
171+
// Indexer for direct access
172+
public float GetAt(int i, int j) => values[i, j];
173+
public void SetAt(int i, int j, float value) => values[i, j] = value;
226174
}
227-
228-
}
175+
}

Engine/Raytracer.cs

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -145,33 +145,40 @@ private void ComputeImageToViewportTransform(int imageWidth, int imageHeight, ou
145145
float width = Camera.ViewportWidth;
146146
float height = Camera.ViewportHeight;
147147

148+
// If any dimension is zero, return identity
148149
if (imageWidth == 0 || imageHeight == 0 || width == 0 || height == 0)
149150
{
150-
result = Matrix4.identity();
151+
Matrix4.LoadIdentity(out result);
151152
return;
152153
}
153154

154-
Matrix4 finalTransform = Matrix4.translate(Camera.LookAt.X, Camera.LookAt.Y, Camera.LookAt.Z);
155+
// Step 1: Translation to camera LookAt
156+
Matrix4 finalTransform;
157+
Matrix4.LoadTranslate(Camera.LookAt.X, Camera.LookAt.Y, Camera.LookAt.Z, out finalTransform);
155158

156-
Vector3D invNormal = Camera.Eye - Camera.LookAt;
157-
invNormal.Normalize();
158-
159-
Matrix4 scale = Matrix4.identity();
159+
// Step 2: Scale viewport to image dimensions
160+
Matrix4 scale;
161+
Matrix4.LoadIdentity(out scale);
160162
scale.SetAt(0, 0, width / imageWidth);
161163
scale.SetAt(1, 1, height / imageHeight);
162164
finalTransform.MultiplyThisBy(scale);
163165

164-
Matrix4 mirror = Matrix4.identity();
166+
// Step 3: Mirror Y axis
167+
Matrix4 mirror;
168+
Matrix4.LoadIdentity(out mirror);
165169
mirror.SetAt(1, 1, -1);
166170
finalTransform.MultiplyThisBy(mirror);
167171

168-
Matrix4 center = Matrix4.translate(-imageWidth / 2, -imageHeight / 2, 0);
172+
// Step 4: Center the image
173+
Matrix4 center;
174+
Matrix4.LoadTranslate(-imageWidth / 2f, -imageHeight / 2f, 0f, out center);
169175
finalTransform.MultiplyThisBy(center);
170176

171-
// assign to out parameter
177+
// Output the final transform
172178
result = finalTransform;
173179
}
174180

181+
175182
public void Render()
176183
{
177184
if (Buffer == null)
@@ -226,7 +233,7 @@ private void RenderChunk(Point start, Point end, byte[] pixels, int stride)
226233

227234
while (!iter.Done())
228235
{
229-
Vector3D mapped = _imageToWorld.Multiply(new Vector3D(iter.Cursor.X, iter.Cursor.Y, 0));
236+
Vector3D mapped = _imageToWorld.MultiplyBy(new Vector3D(iter.Cursor.X, iter.Cursor.Y, 0));
230237
Vector3D dir = mapped - Camera.Eye;
231238
dir.Normalize();
232239
Ray ray = new Ray(Camera.Eye, dir);

0 commit comments

Comments
 (0)