Skip to content

Commit 592ca6a

Browse files
committed
Adjust voxel lighting to match the game better (PR #235))
1 parent 1dccee5 commit 592ca6a

1 file changed

Lines changed: 32 additions & 14 deletions

File tree

src/TSMapEditor/Rendering/ObjectRenderers/VxlRenderer.cs

Lines changed: 32 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ public static (Texture2D texture, Point2D offset) Render(GraphicsDevice graphics
7676
foreach (var section in vxl.Sections)
7777
{
7878
byte[] normalIndexToVplPage =
79-
PreCalculateLighting(section.GetNormals(), section.NormalsMode, rotationFromFacing);
79+
PreCalculateLighting(section.GetNormals(), rotationFromFacing);
8080

8181
var sectionHvaTransform = hva.LoadMatrix(section.Index);
8282
sectionHvaTransform.M41 *= section.HvaMatrixScale;
@@ -194,18 +194,18 @@ private static void RenderVoxel(Vector3 position, Color color, int vertexIndexCo
194194
const float radius = 0.5f;
195195

196196
// Set up the coordinates of the voxel's corners
197-
Span<Vector3> vertexCoordinates = stackalloc Vector3[]
198-
{
197+
Span<Vector3> vertexCoordinates =
198+
[
199199
new(-1, 1, -1), // A1 // 0
200-
new(1, 1, -1), // B1 // 1
200+
new(1, 1, -1), // B1 // 1
201201
new(1, 1, 1), // C1 // 2
202202
new(-1, 1, 1), // D1 // 3
203203

204204
new(-1, -1, -1), // A2 // 4
205205
new(1, -1, -1), // B2 // 5
206206
new(1, -1, 1), // C2 // 6
207207
new(-1, -1, 1) // D2 // 7
208-
};
208+
];
209209

210210
for (int i = 0; i < vertexCoordinates.Length; i++)
211211
{
@@ -226,26 +226,44 @@ private static void RenderVoxel(Vector3 position, Color color, int vertexIndexCo
226226
}
227227
}
228228

229-
private static byte[] PreCalculateLighting(Vector3[] normalsTable, int normalsMode, float rotation)
229+
// This uses the Blinn-Phong reflection model
230+
// Used sometimes in YR, present but not used in TS
231+
private static byte[] PreCalculateLighting(Vector3[] normalsTable, float rotation)
230232
{
231233
Vector3 light = Constants.IsRA2YR ?
232234
Vector3.Transform(YRLight, Matrix.CreateRotationZ(rotation - MathHelper.ToRadians(45))) :
233235
Vector3.Transform(TSLight, Matrix.CreateRotationZ(rotation - MathHelper.ToRadians(45)));
234236

235-
// Center the lighting around this page to make vehicles darker in RA2
236-
const byte centerPage = 7;
237-
// Assume 8 pages per normals mode
238-
int maxPage = normalsMode * 8 - 1;
237+
Vector3 viewer = Vector3.UnitZ;
238+
239+
// Halfway vector between light direction and view direction (Blinn-Phong model)
240+
Vector3 halfway = light + viewer;
241+
halfway.Normalize();
242+
243+
// Constant used in YR
244+
const float specularStrength = 3.0f;
239245

240246
byte[] normalIndexToVplPage = new byte[256];
241247

242248
for (int i = 0; i < normalsTable.Length; i++)
243249
{
244-
float dot = (Vector3.Dot(normalsTable[i], light) + 1) / 2;
250+
// Lambertian diffuse term: cosine of angle between normal and light
251+
float diffuse = Vector3.Dot(normalsTable[i], light);
252+
253+
// Specular term: cosine of angle between normal and halfway vector
254+
float halfwayDot = Vector3.Dot(normalsTable[i], halfway);
255+
256+
// Specular boost (empirical) - the formula adjusts how specular highlights behave
257+
float specular = halfwayDot / (specularStrength - halfwayDot * specularStrength + halfwayDot);
258+
259+
// Clamp both values to [0, inf)
260+
specular = specular >= 0.0f ? specular : 0.0f;
261+
diffuse = diffuse >= 0.0f ? diffuse : 0.0f;
262+
263+
// Final brightness contribution
264+
float brightness = diffuse + specular;
245265

246-
byte page = dot <= 0.5 ?
247-
Convert.ToByte(dot * 2 * centerPage) :
248-
Convert.ToByte(2 * (maxPage - centerPage) * (dot - 0.5f) + centerPage);
266+
byte page = Math.Clamp((byte)(brightness * 16.0), (byte)0, (byte)255);
249267

250268
normalIndexToVplPage[i] = page;
251269
}

0 commit comments

Comments
 (0)