2024-07-21 21:13:31 +00:00
using Godot ;
2024-09-05 17:49:55 +00:00
using KeepersCompound.LGS ;
2024-07-21 21:13:31 +00:00
using KeepersCompound.LGS.Database ;
using KeepersCompound.LGS.Database.Chunks ;
2024-08-03 09:24:45 +00:00
using KeepersCompound.TMV.UI ;
2024-07-21 21:13:31 +00:00
using System ;
using System.Collections.Generic ;
2024-08-24 12:37:21 +00:00
using System.IO ;
2024-07-21 21:13:31 +00:00
2024-08-11 08:18:02 +00:00
namespace KeepersCompound.TMV ;
2024-07-21 21:13:31 +00:00
public partial class Mission : Node3D
{
2024-08-11 11:28:24 +00:00
private readonly struct LightmapRectData
{
public readonly int cellIndex ;
public readonly int lightmapIndex ;
public readonly int textureId ;
public readonly int uvStart ;
public readonly int uvEnd ;
public LightmapRectData ( int cellIndex , int lightmapIndex , int textureId , int uvStart , int uvEnd )
{
this . cellIndex = cellIndex ;
this . lightmapIndex = lightmapIndex ;
this . textureId = textureId ;
this . uvStart = uvStart ;
this . uvEnd = uvEnd ;
}
}
2024-09-07 07:10:14 +00:00
private const string OBJECT_MODELS_GROUP = "ObjectModels" ;
2024-07-21 21:13:31 +00:00
[Export(PropertyHint.GlobalFile, "*.mis")]
public string FileName { get ; set ; }
[Export]
public bool Build = false ;
2024-07-22 18:37:27 +00:00
[Export]
public bool Clear = false ;
2024-08-03 15:09:51 +00:00
[Export]
public bool Dump = false ;
2024-08-28 07:07:01 +00:00
public int LightmapLayers = 33 ;
2024-07-21 21:13:31 +00:00
2024-08-25 09:08:48 +00:00
string _missionName ;
2024-07-21 21:13:31 +00:00
DbFile _file ;
2024-08-11 14:23:56 +00:00
TextureLoader _textureLoader ;
2024-08-28 06:44:07 +00:00
List < ShaderMaterial > _materials ;
Vector2I _lmLayerMask ;
2024-07-21 21:13:31 +00:00
public override void _Ready ( )
{
2024-08-28 06:44:07 +00:00
_materials = new List < ShaderMaterial > ( ) ;
_lmLayerMask = new Vector2I ( ~ 0 , ~ 0 ) ;
2024-08-28 07:07:01 +00:00
var lightmapToggler = GetNode < Control > ( "%LightmapToggler" ) as LightmapLayerToggler ;
lightmapToggler . Setup ( this ) ;
2024-09-05 18:44:19 +00:00
var resourceSelector = GetNode < Control > ( "%ResourceSelector" ) as ResourceSelector ;
resourceSelector . ResourceSelected + = ( string campaign , string mission ) = >
2024-08-03 09:24:45 +00:00
{
2024-08-25 09:08:48 +00:00
_missionName = mission ;
2024-09-19 20:16:07 +00:00
var context = Context . Instance ;
context . SetCampaign ( campaign ) ;
FileName = context . CampaignResources . GetResourcePath ( ResourceType . Mission , mission ) ;
2024-08-03 09:24:45 +00:00
Build = true ;
} ;
2024-07-21 21:13:31 +00:00
}
public override void _Process ( double delta )
{
if ( Build )
{
2024-08-29 18:27:18 +00:00
Timing . Reset ( ) ;
2024-08-26 14:24:07 +00:00
Timing . TimeStage ( "Build" , ( ) = > RebuildMap ( ) ) ;
2024-08-29 18:27:18 +00:00
Timing . LogAll ( ) ;
2024-07-21 21:13:31 +00:00
Build = false ;
}
2024-07-22 18:37:27 +00:00
if ( Clear )
{
ClearMap ( ) ;
Clear = false ;
}
2024-07-21 21:13:31 +00:00
}
2024-09-17 16:46:37 +00:00
public override void _ShortcutInput ( InputEvent @event )
2024-07-21 21:13:31 +00:00
{
if ( @event is InputEventKey keyEvent & & keyEvent . Pressed )
{
if ( keyEvent . Keycode = = Key . R )
{
Build = true ;
}
2024-09-07 07:10:14 +00:00
if ( keyEvent . Keycode = = Key . L )
2024-08-28 06:44:07 +00:00
{
ToggleLightmap ( ) ;
}
2024-09-07 07:10:14 +00:00
if ( keyEvent . Keycode = = Key . O )
{
ToggleObjectRendering ( ) ;
}
2024-07-21 21:13:31 +00:00
}
}
2024-07-22 18:37:27 +00:00
public void ClearMap ( )
2024-07-21 21:13:31 +00:00
{
foreach ( var node in GetChildren ( ) )
{
node . QueueFree ( ) ;
}
2024-08-28 06:44:07 +00:00
_materials . Clear ( ) ;
2024-07-22 18:37:27 +00:00
}
public void RebuildMap ( )
{
ClearMap ( ) ;
2024-07-21 21:13:31 +00:00
2024-09-19 20:16:07 +00:00
_textureLoader = new TextureLoader ( ) ;
2024-08-26 12:25:06 +00:00
Timing . TimeStage ( "DbFile Parse" , ( ) = > _file = new ( FileName ) ) ;
2024-08-29 18:09:53 +00:00
Timing . TimeStage ( "Register Textures" , ( ) = > UseChunk < TxList > ( "TXLIST" , RegisterTextures ) ) ;
2024-08-26 12:25:06 +00:00
Timing . TimeStage ( "Build WR" , ( ) = > UseChunk < WorldRep > ( "WREXT" , BuildWrMeshes ) ) ;
2024-08-25 14:49:40 +00:00
2024-08-26 14:10:05 +00:00
if ( _file . Chunks . TryGetValue ( "BRLIST" , out var brList ) )
{
2024-08-26 14:24:07 +00:00
var objHierarchy = Timing . TimeStage ( "Hierarchy" , BuildHierarchy ) ;
2024-08-26 14:10:05 +00:00
Timing . TimeStage ( "Object Placement" , ( ) = > PlaceObjects ( ( BrList ) brList , objHierarchy ) ) ;
}
}
2024-09-07 07:10:14 +00:00
public void ToggleObjectRendering ( )
{
// It might be "better" to use a parent node and just toggle that, but
// the performance of this is completely fine so we'll stick with it
foreach ( var node in GetTree ( ) . GetNodesInGroup ( OBJECT_MODELS_GROUP ) )
{
var node3d = node as Node3D ;
node3d . Visible = ! node3d . Visible ;
}
}
2024-08-28 06:44:07 +00:00
public void ToggleLightmap ( )
{
if ( _lmLayerMask = = Vector2I . Zero )
{
_lmLayerMask = new Vector2I ( ~ 0 , ~ 0 ) ;
}
else
{
_lmLayerMask = Vector2I . Zero ;
}
foreach ( var mat in _materials )
{
mat . SetShaderParameter ( "lightmap_bitmask" , _lmLayerMask ) ;
}
}
public void ToggleLmLayer ( uint layer )
{
if ( layer > = 64 )
{
throw new ArgumentOutOfRangeException ( nameof ( layer ) ) ;
}
static int ToggleBit ( int bitIdx , int val )
{
var mask = 1 < < bitIdx ;
var isEnabled = ( val & mask ) ! = 0 ;
if ( isEnabled )
{
val & = ~ mask ;
}
else
{
val | = mask ;
}
return val ;
}
if ( layer < 32 )
{
_lmLayerMask . X = ToggleBit ( ( int ) layer , _lmLayerMask . X ) ;
}
else
{
_lmLayerMask . Y = ToggleBit ( ( int ) layer - 32 , _lmLayerMask . Y ) ;
}
foreach ( var mat in _materials )
{
mat . SetShaderParameter ( "lightmap_bitmask" , _lmLayerMask ) ;
}
}
2024-08-26 14:10:05 +00:00
// TODO: Make this less of a mess
2024-08-26 14:24:07 +00:00
private ObjectHierarchy BuildHierarchy ( )
2024-08-26 14:10:05 +00:00
{
2024-08-25 14:49:40 +00:00
ObjectHierarchy objHierarchy ;
if ( _file . Chunks . TryGetValue ( "GAM_FILE" , out var gamFileChunk ) )
2024-08-24 10:33:55 +00:00
{
2024-08-24 12:37:21 +00:00
2024-08-25 14:49:40 +00:00
var options = new EnumerationOptions { MatchCasing = MatchCasing . CaseInsensitive } ;
var name = ( ( GamFile ) gamFileChunk ) . fileName ;
var paths = Directory . GetFiles ( FileName . GetBaseDir ( ) , name , options ) ;
if ( ! paths . IsEmpty ( ) )
2024-08-24 12:37:21 +00:00
{
2024-08-25 14:49:40 +00:00
objHierarchy = new ObjectHierarchy ( _file , new DbFile ( paths [ 0 ] ) ) ;
}
else
{
objHierarchy = new ObjectHierarchy ( _file ) ;
2024-08-24 12:37:21 +00:00
}
2024-08-25 14:49:40 +00:00
}
else
{
objHierarchy = new ObjectHierarchy ( _file ) ;
}
2024-08-26 14:10:05 +00:00
return objHierarchy ;
2024-08-14 17:39:13 +00:00
}
2024-08-03 15:09:51 +00:00
2024-08-14 17:39:13 +00:00
private void UseChunk < T > ( string name , Action < T > action )
{
if ( _file . Chunks . TryGetValue ( name , out var value ) )
{
action ( ( T ) value ) ;
}
else
{
GD . Print ( $"No chunk found/loaded: {name}" ) ;
}
2024-07-21 21:13:31 +00:00
}
2024-08-25 14:49:40 +00:00
private void PlaceObjects ( BrList brList , ObjectHierarchy objHierarchy )
2024-08-23 17:55:39 +00:00
{
foreach ( var brush in brList . Brushes )
{
if ( brush . media ! = BrList . Brush . Media . Object )
{
continue ;
}
2024-08-24 10:33:55 +00:00
var id = ( int ) brush . brushInfo ;
2024-09-07 09:48:30 +00:00
var modelNameProp = objHierarchy . GetProperty < PropLabel > ( id , "P$ModelName" ) ;
2024-09-07 10:18:40 +00:00
var scaleProp = objHierarchy . GetProperty < PropVector > ( id , "P$Scale" ) ;
2024-08-25 14:49:40 +00:00
var renderTypeProp = objHierarchy . GetProperty < PropRenderType > ( id , "P$RenderTyp" ) ;
2024-12-07 13:20:03 +00:00
var jointPosProp = objHierarchy . GetProperty < PropJointPos > ( id , "P$JointPos" ) ;
2024-08-31 11:00:07 +00:00
var txtRepl0 = objHierarchy . GetProperty < PropString > ( id , "P$OTxtRepr0" ) ;
var txtRepl1 = objHierarchy . GetProperty < PropString > ( id , "P$OTxtRepr1" ) ;
var txtRepl2 = objHierarchy . GetProperty < PropString > ( id , "P$OTxtRepr2" ) ;
var txtRepl3 = objHierarchy . GetProperty < PropString > ( id , "P$OTxtRepr3" ) ;
2024-09-07 07:30:26 +00:00
var renderAlpha = objHierarchy . GetProperty < PropFloat > ( id , "P$RenderAlp" ) ;
2024-08-25 14:49:40 +00:00
var renderMode = renderTypeProp = = null ? PropRenderType . Mode . Normal : renderTypeProp . mode ;
2024-08-25 10:54:33 +00:00
2024-08-25 14:49:40 +00:00
if ( modelNameProp = = null | | renderMode = = PropRenderType . Mode . NotRendered )
2024-08-24 10:33:55 +00:00
{
continue ;
}
2024-08-24 16:36:32 +00:00
// Let's try and place an object :)
2024-09-07 09:48:30 +00:00
var modelName = modelNameProp . value + ".bin" ;
2024-08-23 17:55:39 +00:00
var pos = brush . position . ToGodotVec3 ( ) ;
2024-09-07 11:20:13 +00:00
var rot = brush . angle . ToGodotVec3 ( false ) ;
2024-09-07 10:18:40 +00:00
var scale = scaleProp = = null ? Vector3 . One : scaleProp . value . ToGodotVec3 ( false ) ;
2024-12-07 12:39:25 +00:00
var meshDetails = Timing . TimeStage ( "Get Models" , ( ) = > Context . Instance . ModelLoader . Load ( modelName ) ) ;
if ( meshDetails . Length ! = 0 )
2024-08-23 17:55:39 +00:00
{
2024-12-07 12:39:25 +00:00
var model = new Node3D ( ) ;
2024-08-25 15:47:05 +00:00
model . Position = pos ;
model . RotationDegrees = rot ;
model . Scale = scale ;
2024-12-07 12:39:25 +00:00
2024-12-07 13:20:03 +00:00
var joints = jointPosProp ! = null ? jointPosProp . Positions : [ 0 , 0 , 0 , 0 , 0 , 0 ] ;
var meshes = ModelLoader . TransformMeshes ( joints , meshDetails ) ;
2024-12-07 12:39:25 +00:00
2024-08-31 11:00:07 +00:00
bool GetTextReplPath ( PropString prop , out string path )
{
path = "" ;
if ( prop = = null )
{
return false ;
}
path = prop . value ;
2024-09-05 17:49:55 +00:00
2024-09-19 20:16:07 +00:00
var campaignResources = Context . Instance . CampaignResources ;
2024-08-31 11:00:07 +00:00
if ( path . StartsWith ( "fam" , StringComparison . OrdinalIgnoreCase ) )
{
2024-09-05 17:49:55 +00:00
var resType = ResourceType . Texture ;
2024-09-19 20:16:07 +00:00
path = campaignResources . GetResourcePath ( resType , prop . value ) ;
2024-09-05 17:49:55 +00:00
}
else
{
var resType = ResourceType . ObjectTexture ;
2024-09-07 07:01:15 +00:00
var convertedValue = ResourcePathManager . ConvertSeparator ( prop . value ) ;
var resName = Path . GetFileNameWithoutExtension ( convertedValue ) ;
2024-09-19 20:16:07 +00:00
path = campaignResources . GetResourcePath ( resType , resName ) ;
2024-08-31 11:00:07 +00:00
}
return path ! = null ;
}
var repls = new PropString [ ] { txtRepl0 , txtRepl1 , txtRepl2 , txtRepl3 } ;
2024-12-07 12:39:25 +00:00
foreach ( var meshInstance in meshes )
2024-08-31 11:00:07 +00:00
{
2024-12-07 12:39:25 +00:00
for ( var i = 0 ; i < 4 ; i + + )
2024-08-31 11:00:07 +00:00
{
2024-12-07 12:39:25 +00:00
if ( GetTextReplPath ( repls [ i ] , out var path ) )
2024-08-31 11:00:07 +00:00
{
2024-12-07 12:39:25 +00:00
var overrideMat = new StandardMaterial3D
{
AlbedoTexture = TextureLoader . LoadTexture ( path ) ,
Transparency = BaseMaterial3D . TransparencyEnum . AlphaDepthPrePass ,
} ;
2024-08-31 11:00:07 +00:00
2024-12-07 12:39:25 +00:00
var surfaceCount = meshInstance . Mesh . GetSurfaceCount ( ) ;
for ( var idx = 0 ; idx < surfaceCount ; idx + + )
2024-08-31 11:00:07 +00:00
{
2024-12-07 12:39:25 +00:00
var surfaceMat = meshInstance . Mesh . SurfaceGetMaterial ( idx ) ;
if ( surfaceMat . HasMeta ( $"TxtRepl{i}" ) )
{
meshInstance . SetSurfaceOverrideMaterial ( idx , overrideMat ) ;
}
2024-08-31 11:00:07 +00:00
}
}
}
2024-12-07 12:39:25 +00:00
if ( renderAlpha ! = null )
{
meshInstance . Transparency = 1.0f - renderAlpha . value ;
}
model . AddChild ( meshInstance ) ;
2024-09-07 07:30:26 +00:00
}
2024-12-07 12:39:25 +00:00
2024-09-07 07:10:14 +00:00
model . AddToGroup ( OBJECT_MODELS_GROUP ) ;
2024-12-07 12:39:25 +00:00
2024-08-25 15:47:05 +00:00
AddChild ( model ) ;
2024-08-24 16:36:32 +00:00
}
2024-08-23 17:55:39 +00:00
}
}
2024-08-14 17:39:13 +00:00
private void BuildWrMeshes ( WorldRep worldRep )
2024-07-21 21:13:31 +00:00
{
2024-08-14 17:39:13 +00:00
var cells = worldRep . Cells ;
var lmHdr = worldRep . DataHeader . LightmapFormat = = 2 ;
GD . Print ( $"HDR Lightmap: {lmHdr}" ) ;
2024-08-13 20:38:00 +00:00
2024-08-28 20:39:30 +00:00
var packingRects = new List < RectPacker . Rect > ( ) ;
2024-08-11 11:28:24 +00:00
var surfaceDataMap = new Dictionary < int , MeshSurfaceData > ( ) ;
var rectDataMap = new Dictionary < int , LightmapRectData > ( ) ;
2024-07-21 21:13:31 +00:00
2024-08-11 11:28:24 +00:00
for ( var cellIdx = 0 ; cellIdx < cells . Length ; cellIdx + + )
2024-07-22 18:37:27 +00:00
{
2024-08-11 11:28:24 +00:00
var cell = cells [ cellIdx ] ;
var numPolys = cell . PolyCount ;
var numRenderPolys = cell . RenderPolyCount ;
var numPortalPolys = cell . PortalPolyCount ;
// There's nothing to render
if ( numRenderPolys = = 0 | | numPortalPolys > = numPolys )
{
continue ;
}
// You'd think these would be the same number, but apparently not
// I think it's because water counts as a render poly and a portal poly
var maxPolyIdx = Math . Min ( numRenderPolys , numPolys - numPortalPolys ) ;
var cellIdxOffset = 0 ;
for ( int polyIdx = 0 ; polyIdx < maxPolyIdx ; polyIdx + + )
{
2024-08-26 15:53:01 +00:00
var lightmapRectData = ProcessCellSurfaceData ( surfaceDataMap , worldRep , cellIdx , polyIdx , cellIdxOffset ) ;
2024-08-11 11:28:24 +00:00
rectDataMap . Add ( packingRects . Count , lightmapRectData ) ;
2024-07-22 18:37:27 +00:00
2024-08-11 11:28:24 +00:00
var light = cell . LightList [ polyIdx ] ;
2024-08-28 20:39:30 +00:00
var rect = new RectPacker . Rect
{
Width = light . Width ,
Height = light . Height ,
Id = packingRects . Count ,
} ;
2024-08-11 11:28:24 +00:00
packingRects . Add ( rect ) ;
2024-08-03 16:22:38 +00:00
2024-08-11 11:28:24 +00:00
cellIdxOffset + = cell . Polys [ polyIdx ] . VertexCount ;
}
}
2024-08-05 18:25:44 +00:00
2024-08-26 13:03:50 +00:00
var lightmapTexture = Timing . TimeStage ( "Build Lightmap" , ( ) = >
{
return BuildLightmapTexture ( cells , packingRects . ToArray ( ) , rectDataMap , surfaceDataMap ) ;
} ) ;
2024-08-11 14:43:44 +00:00
foreach ( var ( textureId , surface ) in surfaceDataMap )
2024-08-03 16:24:04 +00:00
{
2024-08-11 11:28:24 +00:00
if ( surface . Empty )
2024-08-05 18:25:44 +00:00
{
2024-08-11 11:28:24 +00:00
continue ;
2024-08-05 18:25:44 +00:00
}
2024-08-11 14:43:44 +00:00
var albedoTexture = _textureLoader . Get ( textureId ) ;
2024-08-28 06:44:07 +00:00
var mat = BuildMaterial ( albedoTexture , lightmapTexture , lmHdr , _lmLayerMask ) ;
_materials . Add ( mat ) ;
2024-08-11 14:43:44 +00:00
2024-08-11 11:28:24 +00:00
var mesh = new ArrayMesh ( ) ;
2024-08-11 14:43:44 +00:00
mesh . AddSurfaceFromArrays ( Mesh . PrimitiveType . Triangles , surface . BuildSurfaceArray ( ) ) ;
2024-08-28 06:44:07 +00:00
mesh . SurfaceSetMaterial ( 0 , mat ) ;
2024-08-11 11:28:24 +00:00
var meshInstance = new MeshInstance3D { Mesh = mesh } ;
AddChild ( meshInstance ) ;
}
}
2024-08-26 15:53:01 +00:00
private LightmapRectData ProcessCellSurfaceData ( Dictionary < int , MeshSurfaceData > surfaceDataMap , WorldRep worldRep , int cellIdx , int polyIdx , int indicesOffset )
2024-08-11 11:28:24 +00:00
{
2024-08-26 15:53:01 +00:00
var cell = worldRep . Cells [ cellIdx ] ;
2024-08-11 11:28:24 +00:00
var poly = cell . Polys [ polyIdx ] ;
var normal = cell . Planes [ poly . PlaneId ] . Normal . ToGodotVec3 ( ) ;
var vertices = new List < Vector3 > ( ) ;
var textureUvs = new List < Vector2 > ( ) ;
var lightmapUvs = new List < Vector2 > ( ) ;
var numPolyVertices = poly . VertexCount ;
for ( var j = 0 ; j < numPolyVertices ; j + + )
{
var vertex = cell . Vertices [ cell . Indices [ indicesOffset + j ] ] ;
vertices . Add ( vertex . ToGodotVec3 ( ) ) ;
2024-08-05 18:25:44 +00:00
}
2024-08-03 16:24:04 +00:00
2024-08-11 11:28:24 +00:00
var renderPoly = cell . RenderPolys [ polyIdx ] ;
var light = cell . LightList [ polyIdx ] ;
2024-08-27 07:02:10 +00:00
var lightmapScale = worldRep . DataHeader . LightmapScaleMultiplier ( ) ;
2024-08-26 15:53:01 +00:00
var textureId = CalcBaseUV ( cell , poly , renderPoly , light , textureUvs , lightmapUvs , lightmapScale , indicesOffset ) ;
2024-08-11 11:28:24 +00:00
if ( ! surfaceDataMap . ContainsKey ( textureId ) )
{
2024-08-11 14:43:44 +00:00
surfaceDataMap . Add ( textureId , new MeshSurfaceData ( ) ) ;
2024-08-11 11:28:24 +00:00
}
var surfaceData = surfaceDataMap [ textureId ] ;
var ( start , end ) = surfaceData . AddPolygon ( vertices , normal , textureUvs , lightmapUvs ) ;
2024-08-03 16:24:04 +00:00
2024-08-11 11:28:24 +00:00
if ( polyIdx > = cell . Lightmaps . Length ) GD . Print ( "HUH" ) ;
2024-08-03 16:24:04 +00:00
2024-08-11 11:28:24 +00:00
return new LightmapRectData ( cellIdx , polyIdx , textureId , start , end ) ;
2024-08-03 16:24:04 +00:00
}
2024-08-27 20:18:31 +00:00
private static Texture2DArray BuildLightmapTexture (
WorldRep . Cell [ ] cells ,
2024-08-28 20:39:30 +00:00
RectPacker . Rect [ ] packingRects ,
2024-08-27 20:18:31 +00:00
Dictionary < int , LightmapRectData > rectDataMap ,
Dictionary < int , MeshSurfaceData > surfaceDataMap )
2024-08-03 16:24:04 +00:00
{
2024-08-26 13:26:45 +00:00
var bounds = Timing . TimeStage ( "RectPack" , ( ) = >
{
2024-08-28 21:03:48 +00:00
return RectPacker . Pack ( packingRects ) ;
2024-08-26 13:26:45 +00:00
} ) ;
2024-08-28 20:39:30 +00:00
GD . Print ( $"Packed {packingRects.Length} rects in ({bounds.Width}, {bounds.Height})" ) ;
2024-08-27 20:18:31 +00:00
var lightmapFormat = Image . Format . Rgba8 ;
2024-08-28 07:07:01 +00:00
var lmLayerCount = 33 ; // TODO: Use LightmapLayers
2024-08-27 20:18:31 +00:00
var lmImages = new Godot . Collections . Array < Image > ( ) ;
lmImages . Resize ( lmLayerCount ) ;
for ( var i = 0 ; i < lmLayerCount ; i + + )
{
2024-08-29 15:59:41 +00:00
lmImages [ i ] = Image . CreateEmpty ( bounds . Width , bounds . Height , false , lightmapFormat ) ;
2024-08-27 20:18:31 +00:00
}
2024-07-30 20:50:34 +00:00
foreach ( var rect in packingRects )
{
2024-08-11 11:28:24 +00:00
if ( ! rectDataMap . ContainsKey ( rect . Id ) ) GD . Print ( "Invalid rectDataMap key" ) ;
var info = rectDataMap [ rect . Id ] ;
if ( info . cellIndex > = cells . Length ) GD . Print ( $"CellIndex too big: {info.cellIndex}/{cells.Length}" ) ;
if ( info . lightmapIndex > = cells [ info . cellIndex ] . Lightmaps . Length ) GD . Print ( $"LightmapIndex too big: {info.lightmapIndex}/{cells[info.cellIndex].Lightmaps.Length}" ) ;
2024-08-27 20:18:31 +00:00
2024-08-11 11:28:24 +00:00
var lightmap = cells [ info . cellIndex ] . Lightmaps [ info . lightmapIndex ] ;
2024-08-27 20:18:31 +00:00
var width = lightmap . Width ;
var height = lightmap . Height ;
var layerCount = lightmap . Layers ;
var srcRect = new Rect2I ( 0 , 0 , width , height ) ;
2024-08-29 15:59:41 +00:00
var dst = new Vector2I ( rect . X , rect . Y ) ;
2024-08-29 18:59:01 +00:00
Timing . TimeStage ( "Blit LM Data" , ( ) = >
2024-08-27 20:18:31 +00:00
{
2024-08-29 18:59:01 +00:00
for ( var i = 0 ; i < layerCount ; i + + )
{
var bytes = Timing . TimeStage ( "Get LM Data" , ( ) = > { return lightmap . AsBytesRgba ( i ) ; } ) ;
var cellLm = Image . CreateFromData ( width , height , false , lightmapFormat , bytes ) ;
lmImages [ i ] . BlitRect ( cellLm , srcRect , dst ) ;
}
} ) ;
2024-07-30 20:50:34 +00:00
2024-08-29 18:59:01 +00:00
Timing . TimeStage ( "Transform LM UVs" , ( ) = >
2024-07-30 20:50:34 +00:00
{
2024-08-29 18:59:01 +00:00
if ( ! surfaceDataMap . ContainsKey ( info . textureId ) ) GD . Print ( "Invalid SurfaceDataMap key" ) ;
surfaceDataMap [ info . textureId ] . TransformUv2s ( info . uvStart , info . uvEnd , ( uv ) = >
{
var u = uv . X ;
var v = uv . Y ;
// Clamp uv range to [0..1]
u % = 1 ;
v % = 1 ;
if ( u < 0 ) u = Math . Abs ( u ) ;
if ( v < 0 ) v = Math . Abs ( v ) ;
// Transform!
u = ( rect . X + rect . Width * u ) / bounds . Width ;
v = ( rect . Y + rect . Height * v ) / bounds . Height ;
return new Vector2 ( u , v ) ;
} ) ;
2024-08-11 11:28:24 +00:00
} ) ;
2024-08-05 17:46:57 +00:00
}
2024-07-22 19:34:12 +00:00
2024-09-17 16:15:28 +00:00
for ( var i = 0 ; i < lmLayerCount ; i + + )
{
lmImages [ i ] . GenerateMipmaps ( ) ;
}
2024-08-27 20:18:31 +00:00
var lightmapTexture = new Texture2DArray ( ) ;
lightmapTexture . CreateFromImages ( lmImages ) ;
return lightmapTexture ;
2024-07-21 21:13:31 +00:00
}
2024-07-30 20:50:34 +00:00
2024-08-11 11:28:24 +00:00
private int CalcBaseUV (
2024-07-30 20:50:34 +00:00
WorldRep . Cell cell ,
WorldRep . Cell . Poly poly ,
WorldRep . Cell . RenderPoly renderPoly ,
WorldRep . Cell . LightmapInfo light ,
2024-08-05 18:49:36 +00:00
List < Vector2 > textureUvs ,
2024-07-30 20:50:34 +00:00
List < Vector2 > lightmapUvs ,
2024-08-26 15:53:01 +00:00
float lightmapScale ,
2024-07-30 20:50:34 +00:00
int cellIdxOffset )
{
// TODO: This is slightly hardcoded for ND. Check other stuff at some point. Should be handled in LG side imo
// TODO: This is a mess lol
2024-08-05 18:49:36 +00:00
var textureId = renderPoly . TextureId ;
2024-08-29 18:59:01 +00:00
var texture = Timing . TimeStage ( "Load Textures" , ( ) = > { return _textureLoader . Get ( textureId ) ; } ) ;
2024-08-01 17:29:10 +00:00
var texU = renderPoly . TextureVectors . Item1 . ToGodotVec3 ( ) ;
var texV = renderPoly . TextureVectors . Item2 . ToGodotVec3 ( ) ;
2024-08-05 18:49:36 +00:00
var baseU = renderPoly . TextureBases . Item1 ;
var baseV = renderPoly . TextureBases . Item2 ;
2024-07-30 20:50:34 +00:00
2024-08-26 15:53:01 +00:00
var txUScale = 64.0f / lightmapScale / texture . GetWidth ( ) ;
var txVScale = 64.0f / lightmapScale / texture . GetHeight ( ) ;
2024-07-30 20:50:34 +00:00
var lmUScale = 4.0f / light . Width ;
var lmVScale = 4.0f / light . Height ;
2024-08-05 18:49:36 +00:00
var txUBase = baseU * txUScale ;
var txVBase = baseV * txVScale ;
2024-07-30 20:50:34 +00:00
var lmUBase = lmUScale * ( baseU + ( 0.5f - light . Bases . Item1 ) / 4.0f ) ;
var lmVBase = lmVScale * ( baseV + ( 0.5f - light . Bases . Item2 ) / 4.0f ) ;
2024-08-05 18:49:36 +00:00
var uu = texU . Dot ( texU ) ;
var vv = texV . Dot ( texV ) ;
var uv = texU . Dot ( texV ) ;
var anchor = cell . Vertices [ cell . Indices [ cellIdxOffset + 0 ] ] . ToGodotVec3 ( ) ; // TODO: This probably shouldn't be hardcoded idx 0
2024-07-30 20:50:34 +00:00
if ( uv = = 0.0 )
{
2024-08-05 18:49:36 +00:00
var txUVec = texU * txUScale / uu ;
var txVVec = texV * txVScale / vv ;
2024-07-30 20:50:34 +00:00
var lmUVec = texU * lmUScale / uu ;
var lmVVec = texV * lmVScale / vv ;
for ( var i = 0 ; i < poly . VertexCount ; i + + )
{
2024-08-01 17:29:10 +00:00
var v = cell . Vertices [ cell . Indices [ cellIdxOffset + i ] ] . ToGodotVec3 ( ) ;
2024-07-30 20:50:34 +00:00
var delta = new Vector3 ( v . X - anchor . X , v . Y - anchor . Y , v . Z - anchor . Z ) ;
2024-08-05 18:49:36 +00:00
var txUV = new Vector2 ( delta . Dot ( txUVec ) + txUBase , delta . Dot ( txVVec ) + txVBase ) ;
2024-07-30 20:50:34 +00:00
var lmUV = new Vector2 ( delta . Dot ( lmUVec ) + lmUBase , delta . Dot ( lmVVec ) + lmVBase ) ;
2024-08-05 18:49:36 +00:00
textureUvs . Add ( txUV ) ;
2024-07-30 20:50:34 +00:00
lightmapUvs . Add ( lmUV ) ;
}
}
else
{
var denom = 1.0f / ( uu * vv - uv * uv ) ;
2024-08-05 18:49:36 +00:00
var txUu = uu * txVScale * denom ;
var txVv = vv * txUScale * denom ;
var txUvu = txUScale * denom * uv ;
var txUvv = txVScale * denom * uv ;
2024-07-30 20:50:34 +00:00
var lmUu = uu * lmVScale * denom ;
var lmVv = vv * lmUScale * denom ;
var lmUvu = lmUScale * denom * uv ;
var lmUvv = lmVScale * denom * uv ;
for ( var i = 0 ; i < poly . VertexCount ; i + + )
{
2024-08-01 17:29:10 +00:00
var v = cell . Vertices [ cell . Indices [ cellIdxOffset + i ] ] . ToGodotVec3 ( ) ;
2024-07-30 20:50:34 +00:00
var delta = new Vector3 ( v . X - anchor . X , v . Y - anchor . Y , v . Z - anchor . Z ) ;
var du = delta . Dot ( texU ) ;
var dv = delta . Dot ( texV ) ;
2024-08-05 18:49:36 +00:00
var txUV = new Vector2 ( txUBase + txVv * du - txUvu * dv , txVBase + txUu * dv - txUvv * du ) ;
2024-07-30 20:50:34 +00:00
var lmUV = new Vector2 ( lmUBase + lmVv * du - lmUvu * dv , lmVBase + lmUu * dv - lmUvv * du ) ;
2024-08-05 18:49:36 +00:00
textureUvs . Add ( txUV ) ;
2024-07-30 20:50:34 +00:00
lightmapUvs . Add ( lmUV ) ;
}
}
2024-08-11 11:28:24 +00:00
return textureId ;
2024-07-30 20:50:34 +00:00
}
2024-08-03 15:09:51 +00:00
2024-08-29 18:09:53 +00:00
private void RegisterTextures ( TxList textureList )
2024-08-03 15:09:51 +00:00
{
2024-08-07 17:40:46 +00:00
// TODO: Use PathJoin
2024-08-03 15:09:51 +00:00
var count = textureList . ItemCount ;
for ( var i = 0 ; i < count ; i + + )
{
var item = textureList . Items [ i ] ;
2024-08-24 16:10:08 +00:00
var path = "" ;
2024-08-03 15:09:51 +00:00
for ( var j = 0 ; j < item . Tokens . Length ; j + + )
{
var token = item . Tokens [ j ] ;
if ( token = = 0 )
{
break ;
}
path + = $"{textureList.Tokens[token - 1]}/" ;
}
2024-08-07 19:10:45 +00:00
path + = item . Name ;
2024-08-03 15:09:51 +00:00
2024-08-29 18:09:53 +00:00
if ( ! _textureLoader . Register ( i , path ) )
2024-08-03 15:09:51 +00:00
{
2024-08-29 18:09:53 +00:00
GD . Print ( $"Failed to register texture: {path}" ) ;
2024-08-03 15:09:51 +00:00
}
}
2024-08-14 17:39:13 +00:00
if ( Dump ) DumpTextureList ( textureList ) ;
2024-08-03 15:09:51 +00:00
}
private static void DumpTextureList ( TxList textureList )
{
GD . Print ( $"TXLIST:\n BlockSize: {textureList.BlockSize}\n ItemCount: {textureList.ItemCount}\n TokenCount: {textureList.TokenCount}\n Tokens:" ) ;
for ( var i = 0 ; i < textureList . TokenCount ; i + + )
{
GD . Print ( $" {i}: {textureList.Tokens[i]}" ) ;
}
GD . Print ( $" Items:" ) ;
for ( var i = 0 ; i < textureList . ItemCount ; i + + )
{
var item = textureList . Items [ i ] ;
GD . Print ( $" {i}:\n Tokens: [{item.Tokens[0]}, {item.Tokens[1]}, {item.Tokens[2]}, {item.Tokens[3]}]\n Name: {item.Name}" ) ;
}
}
2024-08-11 14:43:44 +00:00
const string MATERIAL_PATH = "res://project/materials/base.tres" ;
2024-08-28 06:44:07 +00:00
private static ShaderMaterial BuildMaterial (
Texture2D albedoTexture ,
Texture2DArray lightmapTexture ,
bool lmHdr ,
Vector2I layerMask )
2024-08-11 14:43:44 +00:00
{
var material = ResourceLoader . Load < ShaderMaterial > ( MATERIAL_PATH ) . Duplicate ( ) as ShaderMaterial ;
material . SetShaderParameter ( "texture_albedo" , albedoTexture ) ;
material . SetShaderParameter ( "lightmap_albedo" , lightmapTexture ) ;
2024-08-13 20:38:00 +00:00
material . SetShaderParameter ( "lightmap_2x" , lmHdr ) ;
2024-08-28 06:44:07 +00:00
material . SetShaderParameter ( "lightmap_bitmask" , layerMask ) ;
2024-08-11 14:43:44 +00:00
return material ;
}
2024-07-21 21:13:31 +00:00
}