Jason Deacutis - Portfolio

Reverse Engineering

Reverse Engineering - Battlefield 3

Disclaimer: All reverse engineering I conduct is purely for educational purposes. I do not claim to be responsible for any of the original work.

An alley (from the first campaign level) imported into Blender without textures for analysis.
/content/Reverse Engineer/BF3/bf3_alley_blender_dof.jpg
Fully textured scene coming soon™
Render Analysis
/content/Reverse Engineer/BF3/alley_drawcalls.gif
A wireframe is shown for each drawcall issued. BF3 makes heavy use of instancing, so most of the geometry is drawn in relatively few drawcalls (~300 here). Only unlit surface color is shown, but simultaneously other PBR data is being drawn (deferred rendering).

To understand how the texturing was achieved, I wrote a Python script in Blender to convert shader assembly to a material node network.

ps_5_0
    dcl_globalFlags refactoringAllowed
    dcl_constantbuffer cb0[4], immediateIndexed
    dcl_constantbuffer cb2[21], immediateIndexed
    dcl_constantbuffer cb1[12], immediateIndexed
    dcl_sampler sampler0 (s0), mode_default
    dcl_resource_texture2d (float,float,float,float) texture_Diffuse (t1)
    dcl_resource_texture2d (float,float,float,float) texture_Camo (t2)
    dcl_resource_texture2d (float,float,float,float) texture_Texture (t3)
    dcl_resource_texture2d (float,float,float,float) texture_Specular (t4)
    dcl_resource_texture2d (float,float,float,float) texture_Texture3 (t5)
    dcl_input_ps linear v1.xyz
    dcl_input_ps linear v2.xyz
    dcl_input_ps linear v3.xyz
    dcl_input_ps linear v4.xyz
    dcl_input_ps linear v5.xy
    dcl_output o0.xyzw
    dcl_output o1.xyzw
    dcl_output o2.xyzw
    dcl_output o3.xyzw
    dcl_temps 4
 0: lt r0.x, l(1.0000), v5.y
 1: lt r0.yzw, v5.yyxx, l(0.0000, 0.0000, 0.0000, 1.0000)
 2: and r0.xz, r0.xxzx, l(1.0000, 0.0000, 1.0000, 0.0000)
 3: movc r0.yw, r0.yyyw, l(0, 0, 0, 0), l(0.0000, 1.0000, 0.0000, 1.0000)
 4: mul r1.xy, v5.xyxx, external_ScratchTiling.xyxx
 5: sample_indexable(texture2d)(float,float,float,float) r1.xyz, r1.xyxx, texture_Texture.xyzw, sampler0
 6: add r1.y, -r1.x, r1.y
 7: mad r0.z, r0.z, r1.y, r1.x
 8: add r1.x, -r0.z, r1.z
 9: mad r0.x, r0.x, r1.x, r0.z
10: mul r0.x, r0.x, r0.y
11: sample_indexable(texture2d)(float,float,float,float) r1.xyz, v5.xyxx, texture_Specular.xyzw, sampler0
12: mul r0.y, r1.y, external_WearAmount.x
13: log r0.y, r0.y
14: mul r0.y, r0.y, external_WearPower.x
15: exp r0.y, r0.y
16: min r0.y, r0.y, l(1.0000)
17: add r0.y, -r0.y, l(1.0000)
18: mul r0.x, r0.y, r0.x
19: add r0.y, -external_SmoothnessWear.x, external_SmoothnessRegular.x
20: mad r0.y, r0.x, r0.y, external_SmoothnessWear.x
21: add r0.z, -r0.y, external_SmoothnessMasked.x
22: mad o0.w, r0.w, r0.z, r0.y
23: mul r0.yz, v5.xxyx, external_NormalTiling.xxyx
24: sample_indexable(texture2d)(float,float,float,float) r0.yzw, r0.yzyy, texture_Texture3.zxyw, sampler0
25: mul r0.y, r0.w, r0.y
26: mad r2.xy, r0.yzyy, l(2.0000, 2.0000, 0.0000, 0.0000), l(-1.0000, -1.0000, 0.0000, 0.0000)
27: dp2 r0.y, r2.xyxx, r2.xyxx
28: add r0.y, -r0.y, l(1.0000)
29: max r0.y, r0.y, l(0)
30: sqrt r2.z, r0.y
31: dp3 r3.x, r2.xyzx, v2.xyzx
32: dp3 r3.y, r2.xyzx, v3.xyzx
33: dp3 r3.z, r2.xyzx, v4.xyzx
34: dp3 r0.y, r3.xyzx, r3.xyzx
35: rsq r0.y, r0.y
36: mul r2.xyz, r0.yyyy, r3.xyzx
37: mad o0.xyz, r2.xyzx, l(0.5000, 0.5000, 0.5000, 0.0000), l(0.5000, 0.5000, 0.5000, 0.0000)
38: add r0.y, -r1.x, r1.z
39: mad r0.y, external_ScopeOcc.x, r0.y, r1.x
40: mul r0.zw, v5.xxxy, external_CamoTiling.xxxy
41: sample_indexable(texture2d)(float,float,float,float) r1.xzw, r0.zwzz, texture_Camo.xwyz, sampler0
42: sample_indexable(texture2d)(float,float,float,float) r3.xyz, v5.xyxx, texture_Diffuse.xyzw, sampler0
43: mul r3.xyz, r3.xyzx, external_DiffuseDarkening.xyzx
44: mad r1.xzw, r1.xxzw, external_CamoDarkening.xxyz, -r3.xxyz
45: mad r0.xzw, r0.xxxx, r1.xxzw, r3.xxyz
46: mul r0.xyz, r0.yyyy, r0.xzwx
47: sqrt o1.xyz, r0.xyzx
48: add r0.xyz, -v1.xyzx, cameraPos.xyzx
49: dp3 r0.w, r0.xyzx, r0.xyzx
50: rsq r0.w, r0.w
51: mul r0.xyz, r0.wwww, r0.xyzx
52: dp3 r0.x, r2.xyzx, r0.xyzx
53: add r0.x, -r0.x, l(1.0010)
54: mul r0.y, r0.x, r0.x
55: mul_sat r0.x, r0.y, r0.x
56: mad r0.x, r0.x, l(4.5000), l(0.5000)
57: mad r0.x, r1.y, r0.x, external_PickupSpec.x
58: sqrt o1.w, r0.x
59: mov r2.w, l(1.0000)
60: dp4 o2.x, r2.xyzw, lightProbeShO.xyzw
61: mov o2.yzw, l(0.0000, 0.0000, 0.0118, 0.0000)
62: dp4 r0.x, r2.xyzw, lightProbeShR.xyzw
63: dp4 r0.y, r2.xyzw, lightProbeShG.xyzw
64: dp4 r0.z, r2.xyzw, lightProbeShB.xyzw
65: max r0.xyz, r0.xyzx, l(0, 0, 0, 0)
66: mul r0.xyz, r0.xyzx, l(0.1250, 0.1250, 0.1250, 0.0000)
67: max r0.w, r0.y, r0.x
68: max r1.x, r0.z, l(0.0000)
69: max r0.w, r0.w, r1.x
70: min r0.w, r0.w, l(1.0000)
71: mul r0.w, r0.w, l(255.0000)
72: round_pi r0.w, r0.w
73: mul r0.w, r0.w, l(0.0039)
74: div o3.xyz, r0.xyzx, r0.wwww
75: mov o3.w, r0.w
76: ret
M4A1 rifle DirectX pixel shader assembly
/content/Reverse Engineer/BF3/dxbc_normal.jpg
Example section responsible for reading the normal map
Converted to Blender Material Node Network

/content/Reverse Engineer/BF3/bf3_m4a1_dxbc_nodes.jpg

The result is a near pixel perfect recreation of how the rifle appears ingame.
/content/Reverse Engineer/BF3/bf3_m4a1_desert.jpg
Desert camouflage variant, with every bump & scratch exactly as ingame
/content/Reverse Engineer/BF3/bf3_m4a1_black.jpg
Ordinary black variant uncovered by bypassing the desert camo part of the node network

Ultimately the goal was to fully automate this process to work for all art on screen, but developing the automation proved to be time consuming & I never got around to finishing it.

Reverse Engineering - ArmA Terrain

Disclaimer: All reverse engineering I conduct is purely for educational purposes. I do not claim to be responsible for any of the original work.

/content/Reverse Engineer/ArmA/utes.jpg
Work in progress port of ArmA 2's Utes map into Blender, by reverse engineering & extracting from the game files (Enlarge for 4k).
ArmA 3's Stratis satellite, heightmap, & surface type textures extracted & stitched together (8192x8192, reduced here for legal reasons).
Terrain heightmap as a mesh, followed by bounding boxes of all the objects on the map (to debug before replacing with models).