@@ -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,45 @@ 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+ // Convert to palette index range [0, 16)
267+ byte page = brightness < 0.0 ? Convert . ToByte ( 0 ) : Convert . ToByte ( brightness * 16.0 ) ;
249268
250269 normalIndexToVplPage [ i ] = page ;
251270 }
0 commit comments