using NativeEngine; namespace Sandbox; public class SceneMapLoader : MapLoader { public SceneMapLoader( SceneWorld world, PhysicsWorld physics, Vector3 origin = default ) : base( world, physics, origin ) { } protected override void CreateObject( ObjectEntry data ) { switch ( data.TypeName ) { case "env_light_probe_volume": CreateLightProbeVolume( data ); break; case "env_combined_light_probe_volume": CreateCombinedLightProbeVolume( data ); break; case "light_environment": case "light_directional": CreateLight( data, LightType.Directional ); break; case "light_rect": CreateLight( data, LightType.Rect ); break; case "light_capsule": CreateLight( data, LightType.Capsule ); break; case "light_spot": CreateLight( data, LightType.Spot ); break; case "light_omni": CreateLight( data, LightType.Omni ); break; case "light_ortho": CreateLight( data, LightType.Ortho ); break; case "point_worldtext": CreatePointWorldText( data ); break; default: CreateModel( data ); break; } } protected virtual void CreateLightProbeVolume( ObjectEntry kv ) { var texture = kv.GetResource( "lightprobetexture" ); var indicesTexture = kv.GetResource( "lightprobetexture_dli" ); var scalarsTexture = kv.GetResource( "lightprobetexture_dls" ); var boundsMin = kv.GetValue( "box_mins", new Vector3( -72.0f, -72.0f, -72.0f ) ); var boundsMax = kv.GetValue( "box_maxs", new Vector3( 72.0f, 72.0f, 72.0f ) ); var handshake = kv.GetValue( "handshake" ); var indoorOutdoorLevel = kv.GetValue( "indoor_outdoor_level" ); var so = new SceneLightProbe( World, texture, indicesTexture, scalarsTexture, new BBox( boundsMin, boundsMax ), kv.Transform, handshake, indoorOutdoorLevel ); // Copy tags from Hammer to this SceneObject. so.Tags.SetFrom( kv.Tags ); SceneObjects.Add( so ); } protected virtual void CreateCombinedLightProbeVolume( ObjectEntry kv ) { CreateLightProbeVolume( kv ); } private enum LightType { Directional, Spot, Omni, Ortho, Rect, Capsule, } private void CreateLight( ObjectEntry kv, LightType lightType ) { if ( !kv.GetValue( "enabled" ) ) return; var color = kv.GetValue( "color" ); var brightness = kv.GetValue( "brightness", 1.0f ); var bounceScale = kv.GetValue( "bouncescale", 1.0f ); var range = kv.GetValue( "range", 1024.0f ); var fallOff = kv.GetValue( "falloff" ); var innerConeAngle = kv.GetValue( "innerconeangle", 45.0f ); var outerConeAngle = kv.GetValue( "outerconeangle", 60.0f ); var attenuation0 = kv.GetValue( "attenuation0", 0.0f ); var attenuation1 = kv.GetValue( "attenuation1", 0.0f ); var attenuation2 = kv.GetValue( "attenuation2", 1.0f ); var castShadows = kv.GetValue( "castshadows" ) == 1; var shadowCascadeCount = kv.GetValue( "numcascades", 1 ); var shadowCascadeDistanceScale = kv.GetValue( "shadowcascadedistancescale" ); var lightCookie = kv.GetResource( "lightcookie" ); var bakeLightIndex = kv.GetValue( "bakelightindex", -1 ); var bakeLightIndexScale = kv.GetValue( "bakelightindexscale", 1.0f ); var bakedLightIndexing = kv.GetValue( "baked_light_indexing", true ); var directLight = kv.GetValue( "directlight", 2 ); var fogLighting = kv.GetValue( "fog_lighting", 2 ); var fogContributionStrength = kv.GetValue( "fogcontributionstrength", 1.0f ); var renderDiffuse = kv.GetValue( "renderdiffuse", true ); var renderSpecular = kv.GetValue( "renderspecular", true ); var shadowTextureWidth = kv.GetValue( "shadowtexturewidth" ); var shadowTextureHeight = kv.GetValue( "shadowtextureheight" ); var lightSourceDim0 = kv.GetValue( "lightsourcedim0" ); var lightSourceDim1 = kv.GetValue( "lightsourcedim1" ); SceneLight sceneLight = null; if ( lightType == LightType.Directional ) { sceneLight = new SceneDirectionalLight( World, kv.Rotation, color * brightness ) { ShadowsEnabled = castShadows, ShadowCascadeCount = shadowCascadeCount, LightCookie = lightCookie, }; sceneLight.Tags.Add( "light_directional" ); } else if ( lightType == LightType.Spot ) { sceneLight = new SceneSpotLight( World, kv.Position, color * brightness ) { Rotation = kv.Rotation, ShadowsEnabled = castShadows, ConeInner = innerConeAngle, ConeOuter = outerConeAngle, Radius = range, FallOff = fallOff, ConstantAttenuation = attenuation0, LinearAttenuation = attenuation1, QuadraticAttenuation = attenuation2 * 10000.0f, LightCookie = lightCookie, }; sceneLight.Tags.Add( "light_spot" ); float scaleFactor = attenuation2 * 10000 + attenuation1 * 100 + attenuation0; if ( scaleFactor > 0 ) { sceneLight.LightColor *= scaleFactor; } } else if ( lightType == LightType.Omni ) { sceneLight = new SceneLight( World, kv.Position, range, color * brightness ) { Rotation = kv.Rotation, ShadowsEnabled = castShadows, Radius = range, ConstantAttenuation = attenuation0, LinearAttenuation = attenuation1, QuadraticAttenuation = attenuation2 * 10000.0f, LightCookie = lightCookie, }; sceneLight.Tags.Add( "light_omni" ); float scaleFactor = attenuation2 * 10000 + attenuation1 * 100 + attenuation0; if ( scaleFactor > 0 ) { sceneLight.LightColor *= scaleFactor; } } else if ( lightType == LightType.Rect ) { sceneLight = new SceneSpotLight( World, kv.Position, color * brightness ) { Rotation = kv.Rotation, ShadowsEnabled = false, // Not yet Radius = range, ConstantAttenuation = attenuation0, LinearAttenuation = attenuation1, QuadraticAttenuation = attenuation2 * 10000.0f, ConeInner = 90, ConeOuter = 90, LightCookie = lightCookie, Shape = SceneLight.LightShape.Rectangle, }; sceneLight.Tags.Add( "light_rect" ); float scaleFactor = attenuation2 * 10000 + attenuation1 * 100 + attenuation0; if ( scaleFactor > 0 ) { sceneLight.LightColor *= scaleFactor; } } else if ( lightType == LightType.Capsule ) { sceneLight = new SceneLight( World, kv.Position, range, color * brightness ) { Rotation = kv.Rotation, ShadowsEnabled = false, // Not yet Radius = range, ConstantAttenuation = attenuation0, LinearAttenuation = attenuation1, QuadraticAttenuation = attenuation2 * 10000.0f, LightCookie = lightCookie, Shape = SceneLight.LightShape.Capsule, }; sceneLight.Tags.Add( "light_capsule" ); float scaleFactor = attenuation2 * 10000 + attenuation1 * 100 + attenuation0; if ( scaleFactor > 0 ) { sceneLight.LightColor *= scaleFactor; } } else if ( lightType == LightType.Ortho ) { sceneLight = new SceneOrthoLight( World ) { Position = kv.Position, Rotation = kv.Rotation, ShadowsEnabled = castShadows, Radius = range, ConstantAttenuation = attenuation0, LinearAttenuation = attenuation1, QuadraticAttenuation = attenuation2 * 10000.0f, LightCookie = lightCookie, LightColor = color * brightness }; sceneLight.Tags.Add( "light_ortho" ); } if ( !sceneLight.IsValid() ) return; // Copy tags from Hammer to this SceneObject. sceneLight.Tags.Add( "light" ); sceneLight.Tags.Add( kv.Tags ); var light = sceneLight.lightNative; light.SetWorldDirection( kv.Rotation ); switch ( directLight ) { case 3: // HAMMER_DIRECT_LIGHT_STATIONARY light.SetLightFlags( light.GetLightFlags() | 512 ); // LIGHTTYPE_FLAGS_RENDER_ALL_GEOMETRY light.SetLightFlags( light.GetLightFlags() | 32 ); // LIGHTTYPE_FLAGS_BAKED break; case 1: // HAMMER_DIRECT_LIGHT_DYNAMIC light.SetLightFlags( light.GetLightFlags() | 32 ); // LIGHTTYPE_FLAGS_BAKED break; } light.SetLightFlags( light.GetLightFlags() | 16 ); // LIGHTTYPE_FLAGS_MIXED_SHADOWS light.GetAttributesPtrForModify().SetFloatValue( "MixedShadowsStrength", 1.0f ); light.SetCascadeDistanceScale( shadowCascadeDistanceScale ); light.SetBounceColor( light.GetColor() * bounceScale ); light.SetBakeLightIndex( bakeLightIndex ); light.SetBakeLightIndexScale( bakeLightIndexScale ); light.SetUsesIndexedBakedLighting( bakedLightIndexing ); light.SetFogContributionStength( fogContributionStrength ); light.SetRenderDiffuse( renderDiffuse ); light.SetRenderSpecular( renderSpecular ); light.SetFogLightingMode( fogLighting ); light.SetShadowTextureWidth( shadowTextureWidth ); light.SetShadowTextureHeight( shadowTextureHeight ); if ( lightType == LightType.Ortho ) { var orthoLightWidth = kv.GetValue( "ortholightwidth", 512 ); var orthoLightHeight = kv.GetValue( "ortholightheight", 512 ); float aspect = orthoLightWidth / orthoLightHeight; var width = shadowTextureWidth; width = (width == 0) ? 2048 : width; var height = (int)(width * aspect); height = height.Clamp( 1, 8196 ); light.SetLightSourceSize0( orthoLightWidth ); light.SetLightSourceSize1( orthoLightHeight ); light.SetShadowTextureWidth( width / 4 ); light.SetShadowTextureHeight( height / 4 ); } if ( lightType == LightType.Rect ) { light.SetLightShape( LightSourceShape_t.Rectangle ); light.SetLightSourceDim0( lightSourceDim0 ); light.SetLightSourceDim1( lightSourceDim1 ); } else if ( lightType == LightType.Capsule ) { light.SetLightShape( LightSourceShape_t.Capsule ); light.SetLightSourceDim0( lightSourceDim0 ); light.SetLightSourceDim1( lightSourceDim1 ); } SceneObjects.Add( sceneLight ); } public class TextSceneObject : SceneCustomObject { public string Text { get; set; } public string FontName { get; set; } = "Roboto"; public float FontSize { get; set; } = 100.0f; public float FontWeight { get; set; } = 800.0f; public TextFlag TextFlags { get; set; } = TextFlag.DontClip; public TextSceneObject( SceneWorld sceneWorld ) : base( sceneWorld ) { RenderLayer = SceneRenderLayer.Default; } public override void RenderSceneObject() { Graphics.Attributes.SetCombo( "D_WORLDPANEL", 1 ); Graphics.DrawText( new Rect( 0 ), Text, ColorTint, FontName, FontSize, FontWeight, TextFlags ); } } protected virtual void CreatePointWorldText( ObjectEntry kv ) { var message = kv.GetString( "message" ); var fontSize = kv.GetValue( "font_size" ); var fontName = kv.GetString( "font_name" ); var worldUnitsPerPixel = kv.GetValue( "world_units_per_pixel" ); var depthRenderOffset = kv.GetValue( "depth_render_offset" ); var color = kv.GetValue( "color" ); var justifyHorizontal = kv.GetValue( "justify_horizontal" ); var justifyVertical = kv.GetValue( "justify_vertical" ); var textObject = new TextSceneObject( World ) { Transform = new Transform( kv.Position + kv.Rotation.Up * depthRenderOffset, kv.Rotation, worldUnitsPerPixel * 0.75f ), LocalBounds = BBox.FromPositionAndSize( 0, 1000 ), ColorTint = color, FontName = fontName, FontSize = fontSize.Clamp( 1, 256 ), Text = message }; // Copy tags from Hammer to this SceneObject. textObject.Tags.SetFrom( kv.Tags ); textObject.Tags.Add( "world_text" ); if ( justifyHorizontal == 0 ) textObject.TextFlags |= TextFlag.Left; else if ( justifyHorizontal == 1 ) textObject.TextFlags |= TextFlag.CenterHorizontally; else if ( justifyHorizontal == 2 ) textObject.TextFlags |= TextFlag.Right; if ( justifyVertical == 0 ) textObject.TextFlags |= TextFlag.Bottom; else if ( justifyVertical == 1 ) textObject.TextFlags |= TextFlag.CenterVertically; else if ( justifyVertical == 2 ) textObject.TextFlags |= TextFlag.Top; SceneObjects.Add( textObject ); } protected virtual void CreateModel( ObjectEntry kv ) { var model = kv.GetResource( "model" ); if ( model == null || model.native.IsNull || model.IsError ) return; if ( model.MeshCount == 0 ) return; if ( !model.native.HasSceneObjects() ) return; var renderColor = kv.GetValue( "rendercolor" ); var sceneObject = new SceneObject( World, model, kv.Transform ); if ( !sceneObject.IsValid() ) return; sceneObject.ColorTint = renderColor; // Copy tags from Hammer to this SceneObject. sceneObject.Tags.SetFrom( kv.Tags ); SceneObjects.Add( sceneObject ); } }