From 592c326b5322321742a7e136a16d69d144eedf8f Mon Sep 17 00:00:00 2001 From: Nico de Poel Date: Fri, 23 Jul 2021 16:03:11 +0200 Subject: [PATCH 01/11] Added a scene to experiment with split-screen running multiple instances of the game. Added the ability to override the default engine library name, and added additional layers for camera masking. --- Assets/Scenes/SplitScreenTest.unity | 1221 ++++++++++++++++++++++ Assets/Scenes/SplitScreenTest.unity.meta | 7 + Assets/Scripts/Layers.cs | 9 + Assets/Scripts/SplitScreenTest.cs | 63 ++ Assets/Scripts/SplitScreenTest.cs.meta | 11 + Assets/Scripts/UniQuake.Interop.cs | 11 +- Assets/Scripts/UniQuake.cs | 2 + ProjectSettings/TagManager.asset | 12 +- 8 files changed, 1325 insertions(+), 11 deletions(-) create mode 100644 Assets/Scenes/SplitScreenTest.unity create mode 100644 Assets/Scenes/SplitScreenTest.unity.meta create mode 100644 Assets/Scripts/SplitScreenTest.cs create mode 100644 Assets/Scripts/SplitScreenTest.cs.meta diff --git a/Assets/Scenes/SplitScreenTest.unity b/Assets/Scenes/SplitScreenTest.unity new file mode 100644 index 0000000..5907baa --- /dev/null +++ b/Assets/Scenes/SplitScreenTest.unity @@ -0,0 +1,1221 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!29 &1 +OcclusionCullingSettings: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_OcclusionBakeSettings: + smallestOccluder: 5 + smallestHole: 0.25 + backfaceThreshold: 100 + m_SceneGUID: 00000000000000000000000000000000 + m_OcclusionCullingData: {fileID: 0} +--- !u!104 &2 +RenderSettings: + m_ObjectHideFlags: 0 + serializedVersion: 9 + m_Fog: 0 + m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1} + m_FogMode: 3 + m_FogDensity: 0.01 + m_LinearFogStart: 0 + m_LinearFogEnd: 300 + m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1} + m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1} + m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1} + m_AmbientIntensity: 1 + m_AmbientMode: 0 + m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1} + m_SkyboxMaterial: {fileID: 10304, guid: 0000000000000000f000000000000000, type: 0} + m_HaloStrength: 0.5 + m_FlareStrength: 1 + m_FlareFadeSpeed: 3 + m_HaloTexture: {fileID: 0} + m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0} + m_DefaultReflectionMode: 0 + m_DefaultReflectionResolution: 128 + m_ReflectionBounces: 1 + m_ReflectionIntensity: 1 + m_CustomReflection: {fileID: 0} + m_Sun: {fileID: 705507994} + m_IndirectSpecularColor: {r: 0.18028438, g: 0.22571537, b: 0.30692333, a: 1} + m_UseRadianceAmbientProbe: 0 +--- !u!157 &3 +LightmapSettings: + m_ObjectHideFlags: 0 + serializedVersion: 12 + m_GIWorkflowMode: 1 + m_GISettings: + serializedVersion: 2 + m_BounceScale: 1 + m_IndirectOutputScale: 1 + m_AlbedoBoost: 1 + m_EnvironmentLightingMode: 0 + m_EnableBakedLightmaps: 1 + m_EnableRealtimeLightmaps: 0 + m_LightmapEditorSettings: + serializedVersion: 12 + m_Resolution: 2 + m_BakeResolution: 40 + m_AtlasSize: 1024 + m_AO: 0 + m_AOMaxDistance: 1 + m_CompAOExponent: 1 + m_CompAOExponentDirect: 0 + m_ExtractAmbientOcclusion: 0 + m_Padding: 2 + m_LightmapParameters: {fileID: 0} + m_LightmapsBakeMode: 1 + m_TextureCompression: 1 + m_FinalGather: 0 + m_FinalGatherFiltering: 1 + m_FinalGatherRayCount: 256 + m_ReflectionCompression: 2 + m_MixedBakeMode: 2 + m_BakeBackend: 1 + m_PVRSampling: 1 + m_PVRDirectSampleCount: 32 + m_PVRSampleCount: 500 + m_PVRBounces: 2 + m_PVREnvironmentSampleCount: 500 + m_PVREnvironmentReferencePointCount: 2048 + m_PVRFilteringMode: 2 + m_PVRDenoiserTypeDirect: 0 + m_PVRDenoiserTypeIndirect: 0 + m_PVRDenoiserTypeAO: 0 + m_PVRFilterTypeDirect: 0 + m_PVRFilterTypeIndirect: 0 + m_PVRFilterTypeAO: 0 + m_PVREnvironmentMIS: 0 + m_PVRCulling: 1 + m_PVRFilteringGaussRadiusDirect: 1 + m_PVRFilteringGaussRadiusIndirect: 5 + m_PVRFilteringGaussRadiusAO: 2 + m_PVRFilteringAtrousPositionSigmaDirect: 0.5 + m_PVRFilteringAtrousPositionSigmaIndirect: 2 + m_PVRFilteringAtrousPositionSigmaAO: 1 + m_ExportTrainingData: 0 + m_TrainingDataDestination: TrainingData + m_LightProbeSampleCountMultiplier: 4 + m_LightingDataAsset: {fileID: 0} + m_LightingSettings: {fileID: 4890085278179872738, guid: f0ed978cba596434485fe01a320af46a, + type: 2} +--- !u!196 &4 +NavMeshSettings: + serializedVersion: 2 + m_ObjectHideFlags: 0 + m_BuildSettings: + serializedVersion: 2 + agentTypeID: 0 + agentRadius: 0.5 + agentHeight: 2 + agentSlope: 45 + agentClimb: 0.4 + ledgeDropHeight: 0 + maxJumpAcrossDistance: 0 + minRegionArea: 2 + manualCellSize: 0 + cellSize: 0.16666667 + manualTileSize: 0 + tileSize: 256 + accuratePlacement: 0 + maxJobWorkers: 0 + preserveTilesOutsideBounds: 0 + debug: + m_Flags: 0 + m_NavMeshData: {fileID: 0} +--- !u!1 &157597863 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 157597865} + - component: {fileID: 157597864} + m_Layer: 0 + m_Name: UniQuake Test + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &157597864 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 157597863} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 47d8bb9d10da63c4d9b96cb96a94aca3, type: 3} + m_Name: + m_EditorClassIdentifier: + gameInstances: + - mainCamera: {fileID: 963194227} + baseGame: 0 + modPath: + arguments: + libraryName: + - mainCamera: {fileID: 821010101} + baseGame: 1 + modPath: + arguments: + libraryName: uniquake2 + - mainCamera: {fileID: 1512894441} + baseGame: 2 + modPath: + arguments: + libraryName: uniquake3 + - mainCamera: {fileID: 506815091} + baseGame: 0 + modPath: dopa + arguments: + libraryName: uniquake4 + visualStyles: + - {fileID: 11400000, guid: d187fe54fb9a3e047bf4cec083877e72, type: 2} +--- !u!4 &157597865 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 157597863} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &274888287 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 274888291} + - component: {fileID: 274888288} + - component: {fileID: 274888290} + - component: {fileID: 274888289} + m_Layer: 0 + m_Name: View Camera + m_TagString: MainCamera + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!20 &274888288 +Camera: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 274888287} + m_Enabled: 1 + serializedVersion: 2 + m_ClearFlags: 1 + m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0} + m_projectionMatrixMode: 1 + m_GateFitMode: 2 + m_FOVAxisMode: 0 + m_SensorSize: {x: 36, y: 24} + m_LensShift: {x: 0, y: 0} + m_FocalLength: 50 + m_NormalizedViewPortRect: + serializedVersion: 2 + x: 0 + y: 0 + width: 1 + height: 1 + near clip plane: 4 + far clip plane: 16384 + field of view: 70 + orthographic: 0 + orthographic size: 5 + m_Depth: -1 + m_CullingMask: + serializedVersion: 2 + m_Bits: 1024 + m_RenderingPath: -1 + m_TargetTexture: {fileID: 0} + m_TargetDisplay: 0 + m_TargetEye: 3 + m_HDR: 1 + m_AllowMSAA: 1 + m_AllowDynamicResolution: 0 + m_ForceIntoRT: 0 + m_OcclusionCulling: 0 + m_StereoConvergence: 10 + m_StereoSeparation: 0.022 +--- !u!114 &274888289 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 274888287} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: a79441f348de89743a2939f4d699eac1, type: 3} + m_Name: + m_EditorClassIdentifier: + m_RenderShadows: 1 + m_RequiresDepthTextureOption: 2 + m_RequiresOpaqueTextureOption: 2 + m_CameraType: 1 + m_Cameras: [] + m_RendererIndex: -1 + m_VolumeLayerMask: + serializedVersion: 2 + m_Bits: 1 + m_VolumeTrigger: {fileID: 0} + m_RenderPostProcessing: 0 + m_Antialiasing: 0 + m_AntialiasingQuality: 2 + m_StopNaN: 0 + m_Dithering: 0 + m_ClearDepth: 1 + m_AllowXRRendering: 1 + m_RequiresDepthTexture: 0 + m_RequiresColorTexture: 0 + m_Version: 2 +--- !u!81 &274888290 +AudioListener: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 274888287} + m_Enabled: 1 +--- !u!4 &274888291 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 274888287} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 821010102} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &463222245 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 463222249} + - component: {fileID: 463222246} + - component: {fileID: 463222248} + - component: {fileID: 463222247} + m_Layer: 0 + m_Name: View Camera + m_TagString: MainCamera + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!20 &463222246 +Camera: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 463222245} + m_Enabled: 1 + serializedVersion: 2 + m_ClearFlags: 1 + m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0} + m_projectionMatrixMode: 1 + m_GateFitMode: 2 + m_FOVAxisMode: 0 + m_SensorSize: {x: 36, y: 24} + m_LensShift: {x: 0, y: 0} + m_FocalLength: 50 + m_NormalizedViewPortRect: + serializedVersion: 2 + x: 0 + y: 0 + width: 1 + height: 1 + near clip plane: 4 + far clip plane: 16384 + field of view: 70 + orthographic: 0 + orthographic size: 5 + m_Depth: -1 + m_CullingMask: + serializedVersion: 2 + m_Bits: 8192 + m_RenderingPath: -1 + m_TargetTexture: {fileID: 0} + m_TargetDisplay: 0 + m_TargetEye: 3 + m_HDR: 1 + m_AllowMSAA: 1 + m_AllowDynamicResolution: 0 + m_ForceIntoRT: 0 + m_OcclusionCulling: 0 + m_StereoConvergence: 10 + m_StereoSeparation: 0.022 +--- !u!114 &463222247 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 463222245} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: a79441f348de89743a2939f4d699eac1, type: 3} + m_Name: + m_EditorClassIdentifier: + m_RenderShadows: 1 + m_RequiresDepthTextureOption: 2 + m_RequiresOpaqueTextureOption: 2 + m_CameraType: 1 + m_Cameras: [] + m_RendererIndex: -1 + m_VolumeLayerMask: + serializedVersion: 2 + m_Bits: 1 + m_VolumeTrigger: {fileID: 0} + m_RenderPostProcessing: 0 + m_Antialiasing: 0 + m_AntialiasingQuality: 2 + m_StopNaN: 0 + m_Dithering: 0 + m_ClearDepth: 1 + m_AllowXRRendering: 1 + m_RequiresDepthTexture: 0 + m_RequiresColorTexture: 0 + m_Version: 2 +--- !u!81 &463222248 +AudioListener: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 463222245} + m_Enabled: 1 +--- !u!4 &463222249 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 463222245} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 1512894442} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &506815088 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 506815092} + - component: {fileID: 506815091} + - component: {fileID: 506815090} + - component: {fileID: 506815089} + m_Layer: 0 + m_Name: Main Camera 4 + m_TagString: MainCamera + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &506815089 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 506815088} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: a79441f348de89743a2939f4d699eac1, type: 3} + m_Name: + m_EditorClassIdentifier: + m_RenderShadows: 1 + m_RequiresDepthTextureOption: 2 + m_RequiresOpaqueTextureOption: 2 + m_CameraType: 0 + m_Cameras: + - {fileID: 1489339775} + m_RendererIndex: -1 + m_VolumeLayerMask: + serializedVersion: 2 + m_Bits: 1 + m_VolumeTrigger: {fileID: 0} + m_RenderPostProcessing: 0 + m_Antialiasing: 0 + m_AntialiasingQuality: 2 + m_StopNaN: 0 + m_Dithering: 0 + m_ClearDepth: 1 + m_AllowXRRendering: 1 + m_RequiresDepthTexture: 0 + m_RequiresColorTexture: 0 + m_Version: 2 +--- !u!81 &506815090 +AudioListener: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 506815088} + m_Enabled: 1 +--- !u!20 &506815091 +Camera: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 506815088} + m_Enabled: 1 + serializedVersion: 2 + m_ClearFlags: 1 + m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0} + m_projectionMatrixMode: 1 + m_GateFitMode: 2 + m_FOVAxisMode: 0 + m_SensorSize: {x: 36, y: 24} + m_LensShift: {x: 0, y: 0} + m_FocalLength: 50 + m_NormalizedViewPortRect: + serializedVersion: 2 + x: 0.5 + y: 0 + width: 0.5 + height: 0.5 + near clip plane: 4 + far clip plane: 16384 + field of view: 70 + orthographic: 0 + orthographic size: 5 + m_Depth: -1 + m_CullingMask: + serializedVersion: 2 + m_Bits: 32768 + m_RenderingPath: -1 + m_TargetTexture: {fileID: 0} + m_TargetDisplay: 0 + m_TargetEye: 3 + m_HDR: 1 + m_AllowMSAA: 1 + m_AllowDynamicResolution: 0 + m_ForceIntoRT: 0 + m_OcclusionCulling: 1 + m_StereoConvergence: 10 + m_StereoSeparation: 0.022 +--- !u!4 &506815092 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 506815088} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 250, z: -2500} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 1489339778} + m_Father: {fileID: 0} + m_RootOrder: 5 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &567286279 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 567286283} + - component: {fileID: 567286282} + - component: {fileID: 567286281} + - component: {fileID: 567286280} + m_Layer: 0 + m_Name: View Camera + m_TagString: MainCamera + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &567286280 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 567286279} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: a79441f348de89743a2939f4d699eac1, type: 3} + m_Name: + m_EditorClassIdentifier: + m_RenderShadows: 1 + m_RequiresDepthTextureOption: 2 + m_RequiresOpaqueTextureOption: 2 + m_CameraType: 1 + m_Cameras: [] + m_RendererIndex: -1 + m_VolumeLayerMask: + serializedVersion: 2 + m_Bits: 1 + m_VolumeTrigger: {fileID: 0} + m_RenderPostProcessing: 0 + m_Antialiasing: 0 + m_AntialiasingQuality: 2 + m_StopNaN: 0 + m_Dithering: 0 + m_ClearDepth: 1 + m_AllowXRRendering: 1 + m_RequiresDepthTexture: 0 + m_RequiresColorTexture: 0 + m_Version: 2 +--- !u!81 &567286281 +AudioListener: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 567286279} + m_Enabled: 1 +--- !u!20 &567286282 +Camera: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 567286279} + m_Enabled: 1 + serializedVersion: 2 + m_ClearFlags: 1 + m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0} + m_projectionMatrixMode: 1 + m_GateFitMode: 2 + m_FOVAxisMode: 0 + m_SensorSize: {x: 36, y: 24} + m_LensShift: {x: 0, y: 0} + m_FocalLength: 50 + m_NormalizedViewPortRect: + serializedVersion: 2 + x: 0 + y: 0 + width: 1 + height: 1 + near clip plane: 4 + far clip plane: 16384 + field of view: 70 + orthographic: 0 + orthographic size: 5 + m_Depth: -1 + m_CullingMask: + serializedVersion: 2 + m_Bits: 128 + m_RenderingPath: -1 + m_TargetTexture: {fileID: 0} + m_TargetDisplay: 0 + m_TargetEye: 3 + m_HDR: 1 + m_AllowMSAA: 1 + m_AllowDynamicResolution: 0 + m_ForceIntoRT: 0 + m_OcclusionCulling: 0 + m_StereoConvergence: 10 + m_StereoSeparation: 0.022 +--- !u!4 &567286283 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 567286279} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 963194228} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &705507993 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 705507995} + - component: {fileID: 705507994} + m_Layer: 0 + m_Name: Directional Light + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!108 &705507994 +Light: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 705507993} + m_Enabled: 1 + serializedVersion: 10 + m_Type: 1 + m_Shape: 0 + m_Color: {r: 1, g: 0.95686275, b: 0.8392157, a: 1} + m_Intensity: 1 + m_Range: 10 + m_SpotAngle: 30 + m_InnerSpotAngle: 21.80208 + m_CookieSize: 10 + m_Shadows: + m_Type: 2 + m_Resolution: -1 + m_CustomResolution: -1 + m_Strength: 1 + m_Bias: 0.05 + m_NormalBias: 0.4 + m_NearPlane: 0.2 + m_CullingMatrixOverride: + e00: 1 + e01: 0 + e02: 0 + e03: 0 + e10: 0 + e11: 1 + e12: 0 + e13: 0 + e20: 0 + e21: 0 + e22: 1 + e23: 0 + e30: 0 + e31: 0 + e32: 0 + e33: 1 + m_UseCullingMatrixOverride: 0 + m_Cookie: {fileID: 0} + m_DrawHalo: 0 + m_Flare: {fileID: 0} + m_RenderMode: 0 + m_CullingMask: + serializedVersion: 2 + m_Bits: 4294967295 + m_RenderingLayerMask: 1 + m_Lightmapping: 1 + m_LightShadowCasterMode: 0 + m_AreaSize: {x: 1, y: 1} + m_BounceIntensity: 1 + m_ColorTemperature: 6570 + m_UseColorTemperature: 0 + m_BoundingSphereOverride: {x: 0, y: 0, z: 0, w: 0} + m_UseBoundingSphereOverride: 0 + m_UseViewFrustumForShadowCasterCull: 1 + m_ShadowRadius: 0 + m_ShadowAngle: 0 +--- !u!4 &705507995 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 705507993} + m_LocalRotation: {x: 0.39713123, y: -0.30997548, z: 0.14454393, w: 0.8516508} + m_LocalPosition: {x: 0, y: 3, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 50, y: -40, z: 0} +--- !u!1 &821010098 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 821010102} + - component: {fileID: 821010101} + - component: {fileID: 821010100} + - component: {fileID: 821010099} + m_Layer: 0 + m_Name: Main Camera 2 + m_TagString: MainCamera + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &821010099 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 821010098} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: a79441f348de89743a2939f4d699eac1, type: 3} + m_Name: + m_EditorClassIdentifier: + m_RenderShadows: 1 + m_RequiresDepthTextureOption: 2 + m_RequiresOpaqueTextureOption: 2 + m_CameraType: 0 + m_Cameras: + - {fileID: 274888288} + m_RendererIndex: -1 + m_VolumeLayerMask: + serializedVersion: 2 + m_Bits: 1 + m_VolumeTrigger: {fileID: 0} + m_RenderPostProcessing: 0 + m_Antialiasing: 0 + m_AntialiasingQuality: 2 + m_StopNaN: 0 + m_Dithering: 0 + m_ClearDepth: 1 + m_AllowXRRendering: 1 + m_RequiresDepthTexture: 0 + m_RequiresColorTexture: 0 + m_Version: 2 +--- !u!81 &821010100 +AudioListener: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 821010098} + m_Enabled: 1 +--- !u!20 &821010101 +Camera: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 821010098} + m_Enabled: 1 + serializedVersion: 2 + m_ClearFlags: 1 + m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0} + m_projectionMatrixMode: 1 + m_GateFitMode: 2 + m_FOVAxisMode: 0 + m_SensorSize: {x: 36, y: 24} + m_LensShift: {x: 0, y: 0} + m_FocalLength: 50 + m_NormalizedViewPortRect: + serializedVersion: 2 + x: 0.5 + y: 0.5 + width: 0.5 + height: 0.5 + near clip plane: 4 + far clip plane: 16384 + field of view: 70 + orthographic: 0 + orthographic size: 5 + m_Depth: -1 + m_CullingMask: + serializedVersion: 2 + m_Bits: 512 + m_RenderingPath: -1 + m_TargetTexture: {fileID: 0} + m_TargetDisplay: 0 + m_TargetEye: 3 + m_HDR: 1 + m_AllowMSAA: 1 + m_AllowDynamicResolution: 0 + m_ForceIntoRT: 0 + m_OcclusionCulling: 1 + m_StereoConvergence: 10 + m_StereoSeparation: 0.022 +--- !u!4 &821010102 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 821010098} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 250, z: -2500} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 274888291} + m_Father: {fileID: 0} + m_RootOrder: 3 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &963194225 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 963194228} + - component: {fileID: 963194227} + - component: {fileID: 963194226} + - component: {fileID: 963194229} + m_Layer: 0 + m_Name: Main Camera 1 + m_TagString: MainCamera + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!81 &963194226 +AudioListener: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 963194225} + m_Enabled: 1 +--- !u!20 &963194227 +Camera: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 963194225} + m_Enabled: 1 + serializedVersion: 2 + m_ClearFlags: 1 + m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0} + m_projectionMatrixMode: 1 + m_GateFitMode: 2 + m_FOVAxisMode: 0 + m_SensorSize: {x: 36, y: 24} + m_LensShift: {x: 0, y: 0} + m_FocalLength: 50 + m_NormalizedViewPortRect: + serializedVersion: 2 + x: 0 + y: 0.5 + width: 0.5 + height: 0.5 + near clip plane: 4 + far clip plane: 16384 + field of view: 70 + orthographic: 0 + orthographic size: 5 + m_Depth: -1 + m_CullingMask: + serializedVersion: 2 + m_Bits: 64 + m_RenderingPath: -1 + m_TargetTexture: {fileID: 0} + m_TargetDisplay: 0 + m_TargetEye: 3 + m_HDR: 1 + m_AllowMSAA: 1 + m_AllowDynamicResolution: 0 + m_ForceIntoRT: 0 + m_OcclusionCulling: 1 + m_StereoConvergence: 10 + m_StereoSeparation: 0.022 +--- !u!4 &963194228 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 963194225} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 250, z: -2500} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 567286283} + m_Father: {fileID: 0} + m_RootOrder: 2 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &963194229 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 963194225} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: a79441f348de89743a2939f4d699eac1, type: 3} + m_Name: + m_EditorClassIdentifier: + m_RenderShadows: 1 + m_RequiresDepthTextureOption: 2 + m_RequiresOpaqueTextureOption: 2 + m_CameraType: 0 + m_Cameras: + - {fileID: 567286282} + m_RendererIndex: -1 + m_VolumeLayerMask: + serializedVersion: 2 + m_Bits: 1 + m_VolumeTrigger: {fileID: 0} + m_RenderPostProcessing: 0 + m_Antialiasing: 0 + m_AntialiasingQuality: 2 + m_StopNaN: 0 + m_Dithering: 0 + m_ClearDepth: 1 + m_AllowXRRendering: 1 + m_RequiresDepthTexture: 0 + m_RequiresColorTexture: 0 + m_Version: 2 +--- !u!1 &1489339774 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1489339778} + - component: {fileID: 1489339775} + - component: {fileID: 1489339777} + - component: {fileID: 1489339776} + m_Layer: 0 + m_Name: View Camera + m_TagString: MainCamera + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!20 &1489339775 +Camera: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1489339774} + m_Enabled: 1 + serializedVersion: 2 + m_ClearFlags: 1 + m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0} + m_projectionMatrixMode: 1 + m_GateFitMode: 2 + m_FOVAxisMode: 0 + m_SensorSize: {x: 36, y: 24} + m_LensShift: {x: 0, y: 0} + m_FocalLength: 50 + m_NormalizedViewPortRect: + serializedVersion: 2 + x: 0 + y: 0 + width: 1 + height: 1 + near clip plane: 4 + far clip plane: 16384 + field of view: 70 + orthographic: 0 + orthographic size: 5 + m_Depth: -1 + m_CullingMask: + serializedVersion: 2 + m_Bits: 65536 + m_RenderingPath: -1 + m_TargetTexture: {fileID: 0} + m_TargetDisplay: 0 + m_TargetEye: 3 + m_HDR: 1 + m_AllowMSAA: 1 + m_AllowDynamicResolution: 0 + m_ForceIntoRT: 0 + m_OcclusionCulling: 0 + m_StereoConvergence: 10 + m_StereoSeparation: 0.022 +--- !u!114 &1489339776 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1489339774} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: a79441f348de89743a2939f4d699eac1, type: 3} + m_Name: + m_EditorClassIdentifier: + m_RenderShadows: 1 + m_RequiresDepthTextureOption: 2 + m_RequiresOpaqueTextureOption: 2 + m_CameraType: 1 + m_Cameras: [] + m_RendererIndex: -1 + m_VolumeLayerMask: + serializedVersion: 2 + m_Bits: 1 + m_VolumeTrigger: {fileID: 0} + m_RenderPostProcessing: 0 + m_Antialiasing: 0 + m_AntialiasingQuality: 2 + m_StopNaN: 0 + m_Dithering: 0 + m_ClearDepth: 1 + m_AllowXRRendering: 1 + m_RequiresDepthTexture: 0 + m_RequiresColorTexture: 0 + m_Version: 2 +--- !u!81 &1489339777 +AudioListener: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1489339774} + m_Enabled: 1 +--- !u!4 &1489339778 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1489339774} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 506815092} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &1512894438 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1512894442} + - component: {fileID: 1512894441} + - component: {fileID: 1512894440} + - component: {fileID: 1512894439} + m_Layer: 0 + m_Name: Main Camera 3 + m_TagString: MainCamera + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &1512894439 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1512894438} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: a79441f348de89743a2939f4d699eac1, type: 3} + m_Name: + m_EditorClassIdentifier: + m_RenderShadows: 1 + m_RequiresDepthTextureOption: 2 + m_RequiresOpaqueTextureOption: 2 + m_CameraType: 0 + m_Cameras: + - {fileID: 463222246} + m_RendererIndex: -1 + m_VolumeLayerMask: + serializedVersion: 2 + m_Bits: 1 + m_VolumeTrigger: {fileID: 0} + m_RenderPostProcessing: 0 + m_Antialiasing: 0 + m_AntialiasingQuality: 2 + m_StopNaN: 0 + m_Dithering: 0 + m_ClearDepth: 1 + m_AllowXRRendering: 1 + m_RequiresDepthTexture: 0 + m_RequiresColorTexture: 0 + m_Version: 2 +--- !u!81 &1512894440 +AudioListener: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1512894438} + m_Enabled: 1 +--- !u!20 &1512894441 +Camera: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1512894438} + m_Enabled: 1 + serializedVersion: 2 + m_ClearFlags: 1 + m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0} + m_projectionMatrixMode: 1 + m_GateFitMode: 2 + m_FOVAxisMode: 0 + m_SensorSize: {x: 36, y: 24} + m_LensShift: {x: 0, y: 0} + m_FocalLength: 50 + m_NormalizedViewPortRect: + serializedVersion: 2 + x: 0 + y: 0 + width: 0.5 + height: 0.5 + near clip plane: 4 + far clip plane: 16384 + field of view: 70 + orthographic: 0 + orthographic size: 5 + m_Depth: -1 + m_CullingMask: + serializedVersion: 2 + m_Bits: 4096 + m_RenderingPath: -1 + m_TargetTexture: {fileID: 0} + m_TargetDisplay: 0 + m_TargetEye: 3 + m_HDR: 1 + m_AllowMSAA: 1 + m_AllowDynamicResolution: 0 + m_ForceIntoRT: 0 + m_OcclusionCulling: 1 + m_StereoConvergence: 10 + m_StereoSeparation: 0.022 +--- !u!4 &1512894442 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1512894438} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 250, z: -2500} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 463222249} + m_Father: {fileID: 0} + m_RootOrder: 4 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} diff --git a/Assets/Scenes/SplitScreenTest.unity.meta b/Assets/Scenes/SplitScreenTest.unity.meta new file mode 100644 index 0000000..eaf44f3 --- /dev/null +++ b/Assets/Scenes/SplitScreenTest.unity.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 4ca6e7235b69a0d4dbe7cadc2b23fb6e +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Layers.cs b/Assets/Scripts/Layers.cs index 4a66570..21a2beb 100644 --- a/Assets/Scripts/Layers.cs +++ b/Assets/Scripts/Layers.cs @@ -2,6 +2,15 @@ { Game1 = 6, ViewModel1 = 7, + + Game2 = 9, + ViewModel2 = 10, + + Game3 = 12, + ViewModel3 = 13, + + Game4 = 15, + ViewModel4 = 16, } public static class LayerExtensions diff --git a/Assets/Scripts/SplitScreenTest.cs b/Assets/Scripts/SplitScreenTest.cs new file mode 100644 index 0000000..90a2fae --- /dev/null +++ b/Assets/Scripts/SplitScreenTest.cs @@ -0,0 +1,63 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +public class SplitScreenTest : MonoBehaviour +{ + [SerializeField] + private GameInstance[] gameInstances = new GameInstance[4]; + + [SerializeField] + private List visualStyles = new List(); + + private void Start() + { + Debug.Log($"Running in {IntPtr.Size * 8}-bit mode"); + + Layers gameLayer = Layers.Game1; + Layers viewLayer = Layers.ViewModel1; + int layerStride = Layers.Game2 - Layers.Game1; + + foreach (var gameInstance in gameInstances) + { + if (gameInstance == null || gameInstance.mainCamera == null) + continue; + + var uq = gameObject.AddComponent(); + uq.BaseGame = gameInstance.baseGame; + uq.ModDirectory = gameInstance.modPath; + uq.AdditionalArguments = ParseArgs(gameInstance.arguments); + uq.Camera = gameInstance.mainCamera; + uq.GameLayer = gameLayer; + uq.ViewModelLayer = viewLayer; + uq.LibraryOverride = gameInstance.libraryName; + uq.SetVisualStyle(visualStyles[0]); + + gameLayer += layerStride; + viewLayer += layerStride; + } + } + + private string[] ParseArgs(string args) + { + if (args == null) + return new string[0]; + + return args.Split(new[] {' ', '\t', '\r', '\n'}, StringSplitOptions.RemoveEmptyEntries); + } + + [Serializable] + public class GameInstance + { + public Camera mainCamera; + + public MissionPack baseGame; + + public string modPath; + + public string arguments; + + public string libraryName; + } +} diff --git a/Assets/Scripts/SplitScreenTest.cs.meta b/Assets/Scripts/SplitScreenTest.cs.meta new file mode 100644 index 0000000..097113b --- /dev/null +++ b/Assets/Scripts/SplitScreenTest.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 47d8bb9d10da63c4d9b96cb96a94aca3 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/UniQuake.Interop.cs b/Assets/Scripts/UniQuake.Interop.cs index a504867..fe3ec1f 100644 --- a/Assets/Scripts/UniQuake.Interop.cs +++ b/Assets/Scripts/UniQuake.Interop.cs @@ -38,11 +38,12 @@ public partial class UniQuake string dllFile = Path.Combine(Application.dataPath, DllPath); // Experimental code to allow running multiple instances of Quake next to each other - // string dllName = Path.GetFileNameWithoutExtension(dllFile); - // string dllExt = Path.GetExtension(dllFile); - // string dllCopy = Path.Combine(Application.persistentDataPath, $"{dllName}{GetInstanceID()}{dllExt}"); - // File.Copy(dllFile, dllCopy, true); - // dllFile = dllCopy; + if (!string.IsNullOrEmpty(LibraryOverride)) + { + string directory = Path.GetDirectoryName(dllFile); + string extension = Path.GetExtension(dllFile); + dllFile = Path.Combine(directory, LibraryOverride + extension); + } libraryHandle = SystemLibrary.LoadLibrary(dllFile); if (libraryHandle == IntPtr.Zero) diff --git a/Assets/Scripts/UniQuake.cs b/Assets/Scripts/UniQuake.cs index 68e6eb1..36f8c8b 100644 --- a/Assets/Scripts/UniQuake.cs +++ b/Assets/Scripts/UniQuake.cs @@ -28,6 +28,8 @@ public partial class UniQuake: MonoBehaviour public string ModDirectory { get; set; } public string[] AdditionalArguments { get; set; } + public string LibraryOverride { get; set; } + /// /// Time since startup for this particular instance of Quake /// diff --git a/ProjectSettings/TagManager.asset b/ProjectSettings/TagManager.asset index 44c535b..5149af1 100644 --- a/ProjectSettings/TagManager.asset +++ b/ProjectSettings/TagManager.asset @@ -14,14 +14,14 @@ TagManager: - Game1 - ViewModel1 - + - Game2 + - ViewModel2 - + - Game3 + - ViewModel3 - - - - - - - - - - - - - + - Game4 + - ViewModel4 - - - From f668c97b5208eede1385807723901e0043d2d472 Mon Sep 17 00:00:00 2001 From: Nico de Poel Date: Mon, 26 Jul 2021 21:05:06 +0200 Subject: [PATCH 02/11] Reworked the entity animation system, so that blending between poses is pulled fully to the Unity side. With this we can detect the situation where we are trying to blend from one animation sequence to another. Since this is not possible in UniQuake due to each animation sequence being converted to a separate mesh, we instead hold the poses for a frame as if we're not using animation lerping at all. This fixes the occasional animation glitch where the previous sequence would be animated a bit too far, which can be very noticeable on non-looping animation sequences (e.g. the Chthon's rise animation). --- Assets/Scripts/Game/Entity.cs | 6 +- Assets/Scripts/Modules/GameModule.Interop.cs | 6 +- Assets/Scripts/Modules/GameModule.cs | 4 +- Assets/Scripts/Support/AliasModel.cs | 62 ++++++++++++++++---- engine/Quake/client.h | 2 +- engine/Quake/r_alias.c | 5 +- engine/UniQuake/game_uniquake.c | 6 +- 7 files changed, 65 insertions(+), 26 deletions(-) diff --git a/Assets/Scripts/Game/Entity.cs b/Assets/Scripts/Game/Entity.cs index 71599b6..3b3d06e 100644 --- a/Assets/Scripts/Game/Entity.cs +++ b/Assets/Scripts/Game/Entity.cs @@ -79,7 +79,7 @@ public class Entity AssignMeshRenderer(); // Set a default pose based on the first animation frame - UpdateAnimation(0); + UpdateAnimation(0, 0, 0f); if (model.AutoAnimate) { @@ -103,11 +103,11 @@ public class Entity worldModel.transform.SetParent(gameObject.transform); } - public void UpdateAnimation(float frameNum) + public void UpdateAnimation(int pose1, int pose2, float blend) { if (aliasModel != null) { - aliasModel.Animate(frameNum, out Mesh mesh, out float blendWeight); + aliasModel.Animate(pose1, pose2, blend, out Mesh mesh, out float blendWeight); if (skinnedMeshRenderer.enabled) { diff --git a/Assets/Scripts/Modules/GameModule.Interop.cs b/Assets/Scripts/Modules/GameModule.Interop.cs index 3e72b1b..022aafc 100644 --- a/Assets/Scripts/Modules/GameModule.Interop.cs +++ b/Assets/Scripts/Modules/GameModule.Interop.cs @@ -66,12 +66,12 @@ public partial class GameModule : CallbackHandler } [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - private delegate void GameUpdateEntityAnimationCallback(IntPtr target, int entityNum, float frameNum); + private delegate void GameUpdateEntityAnimationCallback(IntPtr target, int entityNum, int pose1, int pose2, float blend); [MonoPInvokeCallback(typeof(GameUpdateEntityAnimationCallback))] - private static void Callback_GameUpdateEntityAnimation(IntPtr target, int entityNum, float frameNum) + private static void Callback_GameUpdateEntityAnimation(IntPtr target, int entityNum, int pose1, int pose2, float blend) { - GetSelf(target).UpdateEntityAnimation(entityNum, frameNum); + GetSelf(target).UpdateEntityAnimation(entityNum, pose1, pose2, blend); } [UnmanagedFunctionPointer(CallingConvention.Cdecl)] diff --git a/Assets/Scripts/Modules/GameModule.cs b/Assets/Scripts/Modules/GameModule.cs index e4b5e7a..5f7e3a8 100644 --- a/Assets/Scripts/Modules/GameModule.cs +++ b/Assets/Scripts/Modules/GameModule.cs @@ -58,9 +58,9 @@ public partial class GameModule uq.GameState.RemoveEntity(entityNum); } - private void UpdateEntityAnimation(int entityNum, float frameNum) + private void UpdateEntityAnimation(int entityNum, int pose1, int pose2, float blend) { - uq.GameState.GetEntity(entityNum)?.UpdateAnimation(frameNum); + uq.GameState.GetEntity(entityNum)?.UpdateAnimation(pose1, pose2, blend); } private void SetEntitySkin(int entityNum, int skinNum) diff --git a/Assets/Scripts/Support/AliasModel.cs b/Assets/Scripts/Support/AliasModel.cs index 781ae9c..c35edac 100644 --- a/Assets/Scripts/Support/AliasModel.cs +++ b/Assets/Scripts/Support/AliasModel.cs @@ -26,7 +26,9 @@ public class AliasModel public int GetAnimationFrameCount(float frameNum) { - if (!FindAnimation((int)frameNum, out Mesh mesh, out int startFrame)) + GetAnimationMesh((int)frameNum, out Mesh mesh, out int startFrame); + + if (mesh == null) return 0; if (mesh.blendShapeCount == 0) @@ -37,34 +39,74 @@ public class AliasModel public void Animate(float frameNum, out Mesh mesh, out float blendWeight) { + GetAnimationMesh((int)frameNum, out mesh, out int startFrame); + + if (mesh == null || mesh.blendShapeCount == 0) + { + blendWeight = 0; + return; + } + + int numFrames = mesh.GetBlendShapeFrameCount(0); + blendWeight = ((frameNum - startFrame) / numFrames) % 1; + } + + public void Animate(int pose1, int pose2, float blend, out Mesh mesh, out float blendWeight) + { + int startFrame, numFrames; blendWeight = 0; - int frameIndex = (int)frameNum; - if (!FindAnimation(frameIndex, out mesh, out int startFrame)) + // Don't interpolate if we're on a fixed pose + if (pose1 == pose2) + { + GetAnimationMesh(pose1, out mesh, out startFrame); + if (mesh == null || mesh.blendShapeCount == 0) + return; + + numFrames = mesh.GetBlendShapeFrameCount(0); + blendWeight = ((float)pose1 - startFrame) / numFrames % 1; return; + } + + GetAnimationMesh(pose1, out var mesh1, out int startFrame1); + GetAnimationMesh(pose2, out var mesh2, out int startFrame2); + float pose; + if (mesh1 != mesh2) + { + // We cannot blend from one animation sequence to another, since each sequence is a separate Unity mesh. + // Therefore when transitioning between sequences, we hold the previous and next pose for half a frame each. + mesh = blend > 0.5f ? mesh2 : mesh1; + startFrame = blend > 0.5f ? startFrame2 : startFrame1; + pose = blend > 0.5f ? pose2 : pose1; + } + else + { + mesh = mesh1; + startFrame = startFrame1; + pose = pose1 + blend; + } + if (mesh == null || mesh.blendShapeCount == 0) return; - int numFrames = mesh.GetBlendShapeFrameCount(0); - blendWeight = ((frameNum - startFrame) / numFrames) % 1; + numFrames = mesh.GetBlendShapeFrameCount(0); + blendWeight = (pose - startFrame) / numFrames % 1; } - private bool FindAnimation(int frameIndex, out Mesh mesh, out int startFrame) + private void GetAnimationMesh(int pose, out Mesh mesh, out int startFrame) { mesh = null; startFrame = 0; for (int i = 0; i < animationMeshes.Count; ++i) { - if (animationMeshes[i].Item1 > frameIndex) - return true; + if (animationMeshes[i].Item1 > pose) + return; startFrame = animationMeshes[i].Item1; mesh = animationMeshes[i].Item2; } - - return true; } public void Dispose() diff --git a/engine/Quake/client.h b/engine/Quake/client.h index 843ee6e..529d1aa 100644 --- a/engine/Quake/client.h +++ b/engine/Quake/client.h @@ -375,7 +375,7 @@ void Chase_UpdateForDrawing (void); //johnfitz void UQ_Game_SetEntityModel(int entityNum, const char *modelName); void UQ_Game_SetEntityTransform(int entityNum, vec3_t origin, vec3_t angles); void UQ_Game_RemoveEntity(int entityNum); -void UQ_Game_UpdateEntityAnimation(int entityNum, float frameNum); +void UQ_Game_UpdateEntityAnimation(int entityNum, int pose1, int pose2, float blend); void UQ_Game_SetEntitySkin(int entityNum, int skinNum); #endif /* _CLIENT_H_ */ diff --git a/engine/Quake/r_alias.c b/engine/Quake/r_alias.c index f787047..e4bfe6e 100644 --- a/engine/Quake/r_alias.c +++ b/engine/Quake/r_alias.c @@ -642,10 +642,7 @@ void R_DrawAliasModel (entity_t *e) R_SetupEntityTransform (e, &lerpdata); UQ_Game_SetEntityTransform(e->num, lerpdata.origin, lerpdata.angles); - if (lerpdata.pose1 == lerpdata.pose2) - UQ_Game_UpdateEntityAnimation(e->num, (float)lerpdata.pose1); // Fixed pose, don't lerp - else - UQ_Game_UpdateEntityAnimation(e->num, (float)lerpdata.pose1 + lerpdata.blend); + UQ_Game_UpdateEntityAnimation(e->num, lerpdata.pose1, lerpdata.pose2, lerpdata.blend); // // cull it diff --git a/engine/UniQuake/game_uniquake.c b/engine/UniQuake/game_uniquake.c index 2bbdf86..0f057f9 100644 --- a/engine/UniQuake/game_uniquake.c +++ b/engine/UniQuake/game_uniquake.c @@ -9,7 +9,7 @@ typedef struct unity_gamecalls_s void(*SetEntityModel)(void *target, int entityNum, const char *modelName); void(*SetEntityTransform)(void *target, int entityNum, vec3_t origin, vec3_t angles); void(*RemoveEntity)(void *target, int entityNum); - void(*UpdateEntityAnimation)(void *target, int entityNum, float frameNum); + void(*UpdateEntityAnimation)(void *target, int entityNum, int pose1, int pose2, float blend); void(*SetEntitySkin)(void *target, int entityNum, int skinNum); } unity_gamecalls_t; @@ -30,9 +30,9 @@ void UQ_Game_RemoveEntity(int entityNum) unity_gamecalls->RemoveEntity(unity_gamecalls->target, entityNum); } -void UQ_Game_UpdateEntityAnimation(int entityNum, float frameNum) +void UQ_Game_UpdateEntityAnimation(int entityNum, int pose1, int pose2, float blend) { - unity_gamecalls->UpdateEntityAnimation(unity_gamecalls->target, entityNum, frameNum); + unity_gamecalls->UpdateEntityAnimation(unity_gamecalls->target, entityNum, pose1, pose2, blend); } void UQ_Game_SetEntitySkin(int entityNum, int skinNum) From c9a3d0430cd8ee167cc1919a3f27c72126bc746a Mon Sep 17 00:00:00 2001 From: Nico de Poel Date: Tue, 27 Jul 2021 14:03:22 +0200 Subject: [PATCH 03/11] First implementation of dynamically updated lightmap textures. These are uploaded from the Quake engine to Unity on map load, and whenever a lightmap texture changes. It has been tested and found (mostly) working by manipulating the UVs on surface meshes, applying the lightmap as main texture, and using an unlit shader. This is not part of the commit. --- Assets/Scripts/Game/GameAssets.cs | 41 +++++++++++++++++++ Assets/Scripts/Game/GameState.cs | 3 +- .../Scripts/Modules/RenderModule.Interop.cs | 14 +++++++ Assets/Scripts/Modules/RenderModule.cs | 9 ++++ engine/Quake/gl_texmgr.h | 1 + engine/Quake/r_brush.c | 4 ++ engine/UniQuake/gl_uniquake.c | 6 +++ 7 files changed, 77 insertions(+), 1 deletion(-) diff --git a/Assets/Scripts/Game/GameAssets.cs b/Assets/Scripts/Game/GameAssets.cs index d43523e..c82e983 100644 --- a/Assets/Scripts/Game/GameAssets.cs +++ b/Assets/Scripts/Game/GameAssets.cs @@ -7,6 +7,7 @@ public class GameAssets private readonly UniQuake uq; private readonly Dictionary textures = new Dictionary(); + private readonly Dictionary lightmaps = new Dictionary(); private BrushModel worldModel; private readonly List brushModels = new List(); @@ -46,6 +47,39 @@ public class GameAssets return texNum > 0 && textures.TryGetValue(texNum, out texture); } + public bool TryGetTexture(string texName, out Texture2D texture) + { + foreach (var tex in textures.Values) + { + if (tex.name == texName) + { + texture = tex; + return true; + } + } + + texture = null; + return false; + } + + public void UploadLightmap(int lightmapNum, int width, int height, byte[] data) + { + if (!lightmaps.TryGetValue(lightmapNum, out var lightmap)) + { + lightmap = new Texture2D(width, height, TextureFormat.RGBA32, true) { name = $"Lightmap_{lightmapNum}" }; + lightmaps.Add(lightmapNum, lightmap); + } + + lightmap.SetPixelData(data, 0); + lightmap.Apply(); + } + + public bool TryGetLightmap(int lightmapNum, out Texture2D lightmap) + { + lightmap = null; + return lightmapNum >= 0 && lightmaps.TryGetValue(lightmapNum, out lightmap); + } + public void AddAliasModel(AliasModel aliasModel) { aliasModels.Add(aliasModel); @@ -126,6 +160,13 @@ public class GameAssets aliasModels.Clear(); + foreach (var lightmap in lightmaps.Values) + { + Object.Destroy(lightmap); + } + + lightmaps.Clear(); + foreach (var texture in textures.Values) { Object.Destroy(texture); diff --git a/Assets/Scripts/Game/GameState.cs b/Assets/Scripts/Game/GameState.cs index 1795981..64225e4 100644 --- a/Assets/Scripts/Game/GameState.cs +++ b/Assets/Scripts/Game/GameState.cs @@ -57,8 +57,9 @@ public class GameState { uint fbNum = surfaceMesh.FullBrightNum; uq.GameAssets.TryGetTexture(fbNum, out var fullBright); + uq.GameAssets.TryGetLightmap(surfaceMesh.Lightmap, out var lightmap); - uq.CurrentStyle.SetWorldTextures(mr.material, texture, fullBright, null); + uq.CurrentStyle.SetWorldTextures(mr.material, texture, fullBright, lightmap); } } diff --git a/Assets/Scripts/Modules/RenderModule.Interop.cs b/Assets/Scripts/Modules/RenderModule.Interop.cs index 2af570a..cc5b9ca 100644 --- a/Assets/Scripts/Modules/RenderModule.Interop.cs +++ b/Assets/Scripts/Modules/RenderModule.Interop.cs @@ -18,6 +18,7 @@ public partial class RenderModule: CallbackHandler UploadWorldModel = CreateCallback(Callback_UploadWorldModel), UploadTexture = CreateCallback(Callback_UploadTexture), + UploadLightmap = CreateCallback(Callback_UploadLightmap), SetupView = CreateCallback(Callback_SetupView), }; @@ -36,6 +37,7 @@ public partial class RenderModule: CallbackHandler public IntPtr UploadBrushModel; public IntPtr UploadWorldModel; public IntPtr UploadTexture; + public IntPtr UploadLightmap; public IntPtr SetupView; } @@ -123,6 +125,18 @@ public partial class RenderModule: CallbackHandler return GetSelf(target).UploadTexture(texture, dataBytes, ref texNum); } + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate bool UploadLightmapCallback(IntPtr target, int lmap, int width, int height, IntPtr data); + + [MonoPInvokeCallback(typeof(UploadLightmapCallback))] + private static bool Callback_UploadLightmap(IntPtr target, int lmap, int width, int height, IntPtr data) + { + // TODO: this is a fairly pointless additional data copy step; we could probably make this faster by wrapping the IntPtr directly into a NativeArray + byte[] dataBytes = new byte[width * height * 4]; // 32 bits per pixel (RGBA) + Marshal.Copy(data, dataBytes, 0, dataBytes.Length); + return GetSelf(target).UploadLightmap(lmap, width, height, dataBytes); + } + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] private delegate void SetupViewCallback(IntPtr target, ref QVec3 origin, ref QVec3 angles, ref QLeaf viewLeaf); diff --git a/Assets/Scripts/Modules/RenderModule.cs b/Assets/Scripts/Modules/RenderModule.cs index da350b9..bf75e35 100644 --- a/Assets/Scripts/Modules/RenderModule.cs +++ b/Assets/Scripts/Modules/RenderModule.cs @@ -67,6 +67,15 @@ public partial class RenderModule texNum = uq.GameAssets.SetTexture(texNum, tex); return true; } + + private bool UploadLightmap(int lightmapNum, int width, int height, byte[] data) + { + if (width == 0 || height == 0) + return false; + + uq.GameAssets.UploadLightmap(lightmapNum, width, height, data); + return true; + } private void SetupView(QVec3 origin, QVec3 angles, QLeaf viewLeaf) { diff --git a/engine/Quake/gl_texmgr.h b/engine/Quake/gl_texmgr.h index b39b09c..d982b3e 100644 --- a/engine/Quake/gl_texmgr.h +++ b/engine/Quake/gl_texmgr.h @@ -107,6 +107,7 @@ void GL_Bind (gltexture_t *texture); void GL_ClearBindings (void); extern qboolean UQ_GL_UploadTexture(gltexture_t *texture, unsigned *data); +extern qboolean UQ_GL_UploadLightmap(int lmap, int width, int height, byte *data); #endif /* _GL_TEXMAN_H */ diff --git a/engine/Quake/r_brush.c b/engine/Quake/r_brush.c index 796ae4c..2ca839d 100644 --- a/engine/Quake/r_brush.c +++ b/engine/Quake/r_brush.c @@ -940,6 +940,8 @@ void GL_BuildLightmaps (void) lm->texture = TexMgr_LoadImage (cl.worldmodel, name, LMBLOCK_WIDTH, LMBLOCK_HEIGHT, SRC_LIGHTMAP, lm->data, "", (src_offset_t)lm->data, TEXPREF_LINEAR | TEXPREF_NOPICMIP); //johnfitz + + UQ_GL_UploadLightmap(i, LMBLOCK_WIDTH, LMBLOCK_HEIGHT, lm->data); } //johnfitz -- warn about exceeding old limits @@ -1270,6 +1272,8 @@ static void R_UploadLightmap(int lmap) lm->rectchange.h = 0; lm->rectchange.w = 0; + UQ_GL_UploadLightmap(lmap, LMBLOCK_WIDTH, LMBLOCK_HEIGHT, lm->data); + rs_dynamiclightmaps++; } diff --git a/engine/UniQuake/gl_uniquake.c b/engine/UniQuake/gl_uniquake.c index 41e8215..3ac445c 100644 --- a/engine/UniQuake/gl_uniquake.c +++ b/engine/UniQuake/gl_uniquake.c @@ -11,6 +11,7 @@ typedef struct unity_glcalls_s int(*UploadBrushModel)(void *target, qmodel_t *model); int(*UploadWorldModel)(void *target, qmodel_t *model); qboolean(*UploadTexture)(void *target, gltexture_t *texture, unsigned *data, GLuint *texnum); + qboolean(*UploadLightmap)(void *target, int lmap, int width, int height, byte *data); void(*SetupView)(void *target, vec3_t origin, vec3_t angles, mleaf_t *viewLeaf); } unity_glcalls_t; @@ -39,6 +40,11 @@ qboolean UQ_GL_UploadTexture(gltexture_t *texture, unsigned *data) return unity_glcalls->UploadTexture(unity_glcalls->target, texture, data, &texture->texnum); } +qboolean UQ_GL_UploadLightmap(int lmap, int width, int height, byte *data) +{ + return unity_glcalls->UploadLightmap(unity_glcalls->target, lmap, width, height, data); +} + void UQ_GL_SetupView(vec3_t origin, vec3_t angles, mleaf_t *viewLeaf) { unity_glcalls->SetupView(unity_glcalls->target, origin, angles, viewLeaf); From 2460110525cd2cb9b570f66465649237ca49483c Mon Sep 17 00:00:00 2001 From: Nico de Poel Date: Tue, 27 Jul 2021 21:35:05 +0200 Subject: [PATCH 04/11] First experimental shader using lightmaps, based on URP's Unlit shader. Needed to change the project's Color Space to Gamma for the lightmapped output to look correct. --- Assets/Scripts/Game/GameAssets.cs | 2 +- Assets/Scripts/VisualStyle.cs | 8 +- Assets/Shaders.meta | 8 + Assets/Shaders/Lightmapped.shader | 303 ++++++++++++++++++ Assets/Shaders/Lightmapped.shader.meta | 10 + Assets/Shaders/LightmappedInput.hlsl | 28 ++ Assets/Shaders/LightmappedInput.hlsl.meta | 10 + .../GLQuake/Materials/GLQuake_World.mat | 11 +- ProjectSettings/GraphicsSettings.asset | 2 +- ProjectSettings/ProjectSettings.asset | 2 +- 10 files changed, 375 insertions(+), 9 deletions(-) create mode 100644 Assets/Shaders.meta create mode 100644 Assets/Shaders/Lightmapped.shader create mode 100644 Assets/Shaders/Lightmapped.shader.meta create mode 100644 Assets/Shaders/LightmappedInput.hlsl create mode 100644 Assets/Shaders/LightmappedInput.hlsl.meta diff --git a/Assets/Scripts/Game/GameAssets.cs b/Assets/Scripts/Game/GameAssets.cs index c82e983..88eb99d 100644 --- a/Assets/Scripts/Game/GameAssets.cs +++ b/Assets/Scripts/Game/GameAssets.cs @@ -66,7 +66,7 @@ public class GameAssets { if (!lightmaps.TryGetValue(lightmapNum, out var lightmap)) { - lightmap = new Texture2D(width, height, TextureFormat.RGBA32, true) { name = $"Lightmap_{lightmapNum}" }; + lightmap = new Texture2D(width, height, TextureFormat.RGBA32, false) { name = $"Lightmap_{lightmapNum}" }; lightmaps.Add(lightmapNum, lightmap); } diff --git a/Assets/Scripts/VisualStyle.cs b/Assets/Scripts/VisualStyle.cs index 95692d3..d5a46f2 100644 --- a/Assets/Scripts/VisualStyle.cs +++ b/Assets/Scripts/VisualStyle.cs @@ -97,8 +97,12 @@ public class VisualStyle : ScriptableObject material.EnableKeyword("_EMISSION"); else material.DisableKeyword("_EMISSION"); - - // TODO: lightmap texture + + if (lightmap != null) + { + material.SetTexture("_LightMap", lightmap); + // TODO enable keyword (shader is still very specific) + } } } diff --git a/Assets/Shaders.meta b/Assets/Shaders.meta new file mode 100644 index 0000000..5feb358 --- /dev/null +++ b/Assets/Shaders.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 96c36a1f940f3094689bb6a4848af311 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Shaders/Lightmapped.shader b/Assets/Shaders/Lightmapped.shader new file mode 100644 index 0000000..eb64fd2 --- /dev/null +++ b/Assets/Shaders/Lightmapped.shader @@ -0,0 +1,303 @@ +Shader "UniQuake/Lightmapped" +{ + Properties + { + [MainTexture] _BaseMap("Texture", 2D) = "white" {} + [MainColor] _BaseColor("Color", Color) = (1, 1, 1, 1) + _Cutoff("AlphaCutout", Range(0.0, 1.0)) = 0.5 + + [NoScaleOffset] _LightMap("Lightmap", 2D) = "white" {} + + // BlendMode + [HideInInspector] _Surface("__surface", Float) = 0.0 + [HideInInspector] _Blend("__blend", Float) = 0.0 + [HideInInspector] _AlphaClip("__clip", Float) = 0.0 + [HideInInspector] _SrcBlend("Src", Float) = 1.0 + [HideInInspector] _DstBlend("Dst", Float) = 0.0 + [HideInInspector] _ZWrite("ZWrite", Float) = 1.0 + [HideInInspector] _Cull("__cull", Float) = 2.0 + + // Editmode props + [HideInInspector] _QueueOffset("Queue offset", Float) = 0.0 + + // ObsoleteProperties + [HideInInspector] _MainTex("BaseMap", 2D) = "white" {} + [HideInInspector] _Color("Base Color", Color) = (0.5, 0.5, 0.5, 1) + [HideInInspector] _SampleGI("SampleGI", float) = 0.0 // needed from bakedlit + } + SubShader + { + Tags {"RenderType" = "Opaque" "IgnoreProjector" = "True" "RenderPipeline" = "UniversalPipeline" "ShaderModel"="4.5"} + LOD 100 + + Blend [_SrcBlend][_DstBlend] + ZWrite [_ZWrite] + Cull [_Cull] + + Pass + { + Name "Lightmapped" + + HLSLPROGRAM + #pragma exclude_renderers gles gles3 glcore + #pragma target 4.5 + + #pragma vertex vert + #pragma fragment frag + #pragma shader_feature_local_fragment _ALPHATEST_ON + #pragma shader_feature_local_fragment _ALPHAPREMULTIPLY_ON + + // ------------------------------------- + // Unity defined keywords + #pragma multi_compile_fog + #pragma multi_compile_instancing + #pragma multi_compile _ DOTS_INSTANCING_ON + + #include "LightmappedInput.hlsl" + + struct Attributes + { + float4 positionOS : POSITION; + float2 uv : TEXCOORD0; + float2 uv2 : TEXCOORD1; + UNITY_VERTEX_INPUT_INSTANCE_ID + }; + + struct Varyings + { + float2 uv : TEXCOORD0; + float2 uv2 : TEXCOORD1; + float4 vertex : SV_POSITION; + + UNITY_VERTEX_INPUT_INSTANCE_ID + UNITY_VERTEX_OUTPUT_STEREO + }; + + Varyings vert(Attributes input) + { + Varyings output = (Varyings)0; + + UNITY_SETUP_INSTANCE_ID(input); + UNITY_TRANSFER_INSTANCE_ID(input, output); + UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(output); + + VertexPositionInputs vertexInput = GetVertexPositionInputs(input.positionOS.xyz); + output.vertex = vertexInput.positionCS; + output.uv = TRANSFORM_TEX(input.uv, _BaseMap); + output.uv2 = TRANSFORM_TEX(input.uv2, _LightMap); + //output.fogCoord = ComputeFogFactor(vertexInput.positionCS.z); + + return output; + } + + half4 frag(Varyings input) : SV_Target + { + UNITY_SETUP_INSTANCE_ID(input); + UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(input); + + half2 uv = input.uv; + half4 texColor = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, uv); + half3 color = texColor.rgb * _BaseColor.rgb; + half alpha = texColor.a * _BaseColor.a; + AlphaDiscard(alpha, _Cutoff); + + half4 lightmapColor = SAMPLE_TEXTURE2D(_LightMap, sampler_LightMap, input.uv2); + color.rgb = color.rgb * lightmapColor.rgb * 2.0f; + //color.rgb = lightmapColor.rgb; + +#ifdef _ALPHAPREMULTIPLY_ON + color *= alpha; +#endif + + //color = MixFog(color, input.fogCoord); + + return half4(color, alpha); + } + ENDHLSL + } + Pass + { + Name "DepthOnly" + Tags{"LightMode" = "DepthOnly"} + + ZWrite On + ColorMask 0 + + HLSLPROGRAM + #pragma exclude_renderers gles gles3 glcore + #pragma target 4.5 + + #pragma vertex DepthOnlyVertex + #pragma fragment DepthOnlyFragment + + // ------------------------------------- + // Material Keywords + #pragma shader_feature_local_fragment _ALPHATEST_ON + + //-------------------------------------- + // GPU Instancing + #pragma multi_compile_instancing + #pragma multi_compile _ DOTS_INSTANCING_ON + + #include "LightmappedInput.hlsl" + #include "Packages/com.unity.render-pipelines.universal/Shaders/DepthOnlyPass.hlsl" + ENDHLSL + } + + // This pass it not used during regular rendering, only for lightmap baking. + Pass + { + Name "Meta" + Tags{"LightMode" = "Meta"} + + Cull Off + + HLSLPROGRAM + #pragma exclude_renderers gles gles3 glcore + #pragma target 4.5 + + #pragma vertex UniversalVertexMeta + #pragma fragment UniversalFragmentMetaUnlit + + #include "LightmappedInput.hlsl" + #include "Packages/com.unity.render-pipelines.universal/Shaders/UnlitMetaPass.hlsl" + + ENDHLSL + } + } + + SubShader + { + Tags {"RenderType" = "Opaque" "IgnoreProjector" = "True" "RenderPipeline" = "UniversalPipeline" "ShaderModel"="2.0"} + LOD 100 + + Blend [_SrcBlend][_DstBlend] + ZWrite [_ZWrite] + Cull [_Cull] + + Pass + { + Name "Lightmapped" + HLSLPROGRAM + #pragma only_renderers gles gles3 glcore d3d11 + #pragma target 2.0 + + #pragma vertex vert + #pragma fragment frag + #pragma shader_feature_local_fragment _ALPHATEST_ON + #pragma shader_feature_local_fragment _ALPHAPREMULTIPLY_ON + + // ------------------------------------- + // Unity defined keywords + #pragma multi_compile_fog + #pragma multi_compile_instancing + + #include "LightmappedInput.hlsl" + + struct Attributes + { + float4 positionOS : POSITION; + float2 uv : TEXCOORD0; + UNITY_VERTEX_INPUT_INSTANCE_ID + }; + + struct Varyings + { + float2 uv : TEXCOORD0; + float fogCoord : TEXCOORD1; + float4 vertex : SV_POSITION; + + UNITY_VERTEX_INPUT_INSTANCE_ID + UNITY_VERTEX_OUTPUT_STEREO + }; + + Varyings vert(Attributes input) + { + Varyings output = (Varyings)0; + + UNITY_SETUP_INSTANCE_ID(input); + UNITY_TRANSFER_INSTANCE_ID(input, output); + UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(output); + + VertexPositionInputs vertexInput = GetVertexPositionInputs(input.positionOS.xyz); + output.vertex = vertexInput.positionCS; + output.uv = TRANSFORM_TEX(input.uv, _BaseMap); + output.fogCoord = ComputeFogFactor(vertexInput.positionCS.z); + + return output; + } + + half4 frag(Varyings input) : SV_Target + { + UNITY_SETUP_INSTANCE_ID(input); + UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(input); + + half2 uv = input.uv; + half4 texColor = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, uv); + half3 color = texColor.rgb * _BaseColor.rgb; + half alpha = texColor.a * _BaseColor.a; + AlphaDiscard(alpha, _Cutoff); + +#ifdef _ALPHAPREMULTIPLY_ON + color *= alpha; +#endif + + color = MixFog(color, input.fogCoord); + alpha = OutputAlpha(alpha, _Surface); + + return half4(color, alpha); + } + ENDHLSL + } + Pass + { + Name "DepthOnly" + Tags{"LightMode" = "DepthOnly"} + + ZWrite On + ColorMask 0 + + HLSLPROGRAM + #pragma only_renderers gles gles3 glcore d3d11 + #pragma target 2.0 + + #pragma vertex DepthOnlyVertex + #pragma fragment DepthOnlyFragment + + // ------------------------------------- + // Material Keywords + #pragma shader_feature_local_fragment _ALPHATEST_ON + + //-------------------------------------- + // GPU Instancing + #pragma multi_compile_instancing + + #include "LightmappedInput.hlsl" + #include "Packages/com.unity.render-pipelines.universal/Shaders/DepthOnlyPass.hlsl" + ENDHLSL + } + + // This pass it not used during regular rendering, only for lightmap baking. + Pass + { + Name "Meta" + Tags{"LightMode" = "Meta"} + + Cull Off + + HLSLPROGRAM + #pragma only_renderers gles gles3 glcore d3d11 + #pragma target 2.0 + + #pragma vertex UniversalVertexMeta + #pragma fragment UniversalFragmentMetaUnlit + + #include "LightmappedInput.hlsl" + #include "Packages/com.unity.render-pipelines.universal/Shaders/UnlitMetaPass.hlsl" + + ENDHLSL + } + } + FallBack "Hidden/Universal Render Pipeline/FallbackError" + //CustomEditor "UnityEditor.Rendering.Universal.ShaderGUI.UnlitShader" +} diff --git a/Assets/Shaders/Lightmapped.shader.meta b/Assets/Shaders/Lightmapped.shader.meta new file mode 100644 index 0000000..9471dee --- /dev/null +++ b/Assets/Shaders/Lightmapped.shader.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: 91d80b5043d41db478595fab96d6e2c2 +ShaderImporter: + externalObjects: {} + defaultTextures: [] + nonModifiableTextures: [] + preprocessorOverride: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Shaders/LightmappedInput.hlsl b/Assets/Shaders/LightmappedInput.hlsl new file mode 100644 index 0000000..b4cddf4 --- /dev/null +++ b/Assets/Shaders/LightmappedInput.hlsl @@ -0,0 +1,28 @@ +#ifndef UNIVERSAL_UNLIT_INPUT_INCLUDED +#define UNIVERSAL_UNLIT_INPUT_INCLUDED + +#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/SurfaceInput.hlsl" + +TEXTURE2D(_LightMap); SAMPLER(sampler_LightMap); + +CBUFFER_START(UnityPerMaterial) + float4 _BaseMap_ST; + float4 _LightMap_ST; + half4 _BaseColor; + half _Cutoff; + half _Surface; +CBUFFER_END + +#ifdef UNITY_DOTS_INSTANCING_ENABLED +UNITY_DOTS_INSTANCING_START(MaterialPropertyMetadata) + UNITY_DOTS_INSTANCED_PROP(float4, _BaseColor) + UNITY_DOTS_INSTANCED_PROP(float , _Cutoff) + UNITY_DOTS_INSTANCED_PROP(float , _Surface) +UNITY_DOTS_INSTANCING_END(MaterialPropertyMetadata) + +#define _BaseColor UNITY_ACCESS_DOTS_INSTANCED_PROP_FROM_MACRO(float4 , Metadata__BaseColor) +#define _Cutoff UNITY_ACCESS_DOTS_INSTANCED_PROP_FROM_MACRO(float , Metadata__Cutoff) +#define _Surface UNITY_ACCESS_DOTS_INSTANCED_PROP_FROM_MACRO(float , Metadata__Surface) +#endif + +#endif diff --git a/Assets/Shaders/LightmappedInput.hlsl.meta b/Assets/Shaders/LightmappedInput.hlsl.meta new file mode 100644 index 0000000..de4f011 --- /dev/null +++ b/Assets/Shaders/LightmappedInput.hlsl.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: fe2f7d54113588c42925192dff3809e9 +ShaderImporter: + externalObjects: {} + defaultTextures: [] + nonModifiableTextures: [] + preprocessorOverride: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Styles/GLQuake/Materials/GLQuake_World.mat b/Assets/Styles/GLQuake/Materials/GLQuake_World.mat index f8f23a8..d2ba49e 100644 --- a/Assets/Styles/GLQuake/Materials/GLQuake_World.mat +++ b/Assets/Styles/GLQuake/Materials/GLQuake_World.mat @@ -21,14 +21,13 @@ Material: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_Name: GLQuake_World - m_Shader: {fileID: 4800000, guid: 8d2bb70cbf9db8d4da26e15b26e74248, type: 3} + m_Shader: {fileID: 4800000, guid: 91d80b5043d41db478595fab96d6e2c2, type: 3} m_ShaderKeywords: _EMISSION _RECEIVE_SHADOWS_OFF m_LightmapFlags: 2 m_EnableInstancingVariants: 0 m_DoubleSidedGI: 0 - m_CustomRenderQueue: 2000 - stringTagMap: - RenderType: Opaque + m_CustomRenderQueue: -1 + stringTagMap: {} disabledShaderPasses: [] m_SavedProperties: serializedVersion: 3 @@ -61,6 +60,10 @@ Material: m_Texture: {fileID: 0} m_Scale: {x: 1, y: 1} m_Offset: {x: 0, y: 0} + - _LightMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} - _MainTex: m_Texture: {fileID: 0} m_Scale: {x: 1, y: 1} diff --git a/ProjectSettings/GraphicsSettings.asset b/ProjectSettings/GraphicsSettings.asset index 1663460..c5d384e 100644 --- a/ProjectSettings/GraphicsSettings.asset +++ b/ProjectSettings/GraphicsSettings.asset @@ -60,7 +60,7 @@ GraphicsSettings: m_FogKeepExp: 1 m_FogKeepExp2: 1 m_AlbedoSwatchInfos: [] - m_LightsUseLinearIntensity: 1 + m_LightsUseLinearIntensity: 0 m_LightsUseColorTemperature: 0 m_DefaultRenderingLayerMask: 1 m_LogWhenShaderIsCompiled: 0 diff --git a/ProjectSettings/ProjectSettings.asset b/ProjectSettings/ProjectSettings.asset index e45e94b..64e25c8 100644 --- a/ProjectSettings/ProjectSettings.asset +++ b/ProjectSettings/ProjectSettings.asset @@ -47,7 +47,7 @@ PlayerSettings: defaultScreenWidthWeb: 960 defaultScreenHeightWeb: 600 m_StereoRenderingPath: 0 - m_ActiveColorSpace: 1 + m_ActiveColorSpace: 0 m_MTRendering: 1 mipStripping: 0 numberOfMipsStripped: 0 From cce1ed6d47ac6f4d5caa75b0150d3510ee4ef515 Mon Sep 17 00:00:00 2001 From: Nico de Poel Date: Wed, 28 Jul 2021 10:33:33 +0200 Subject: [PATCH 05/11] Reuse the same lightmap byte buffer, since the lightmap dimensions are static anyway. Reduces the amount of garbage produced by dynamic lightmap uploading. --- Assets/Scripts/Data/QConstants.cs | 3 +++ Assets/Scripts/Modules/RenderModule.Interop.cs | 12 +++++++----- engine/Quake/gl_texmgr.h | 2 +- engine/Quake/r_brush.c | 4 ++-- engine/UniQuake/gl_uniquake.c | 6 +++--- 5 files changed, 16 insertions(+), 11 deletions(-) diff --git a/Assets/Scripts/Data/QConstants.cs b/Assets/Scripts/Data/QConstants.cs index 942cc5c..90a47d4 100644 --- a/Assets/Scripts/Data/QConstants.cs +++ b/Assets/Scripts/Data/QConstants.cs @@ -5,4 +5,7 @@ public const int MaxSkins = 32; // Should correspond to MAX_SKINS public const int MaxDLights = 64; // Should correspond to MAX_DLIGHTS public const int MaxLightmaps = 4; // Should correspond to MAXLIGHTMAPS + + public const int LightmapBlockWidth = 256; // Should correspond to LMBLOCK_WIDTH + public const int LightmapBlockHeight = 256; // Should correspond to LMBLOCK_HEIGHT } diff --git a/Assets/Scripts/Modules/RenderModule.Interop.cs b/Assets/Scripts/Modules/RenderModule.Interop.cs index cc5b9ca..a14af10 100644 --- a/Assets/Scripts/Modules/RenderModule.Interop.cs +++ b/Assets/Scripts/Modules/RenderModule.Interop.cs @@ -125,16 +125,18 @@ public partial class RenderModule: CallbackHandler return GetSelf(target).UploadTexture(texture, dataBytes, ref texNum); } + private readonly byte[] lightmapBytes = new byte[QConstants.LightmapBlockWidth * QConstants.LightmapBlockHeight * 4]; // 32 bits per pixel + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - private delegate bool UploadLightmapCallback(IntPtr target, int lmap, int width, int height, IntPtr data); + private delegate bool UploadLightmapCallback(IntPtr target, int lmap, IntPtr data); [MonoPInvokeCallback(typeof(UploadLightmapCallback))] - private static bool Callback_UploadLightmap(IntPtr target, int lmap, int width, int height, IntPtr data) + private static bool Callback_UploadLightmap(IntPtr target, int lmap, IntPtr data) { // TODO: this is a fairly pointless additional data copy step; we could probably make this faster by wrapping the IntPtr directly into a NativeArray - byte[] dataBytes = new byte[width * height * 4]; // 32 bits per pixel (RGBA) - Marshal.Copy(data, dataBytes, 0, dataBytes.Length); - return GetSelf(target).UploadLightmap(lmap, width, height, dataBytes); + var self = GetSelf(target); + Marshal.Copy(data, self.lightmapBytes, 0, self.lightmapBytes.Length); + return self.UploadLightmap(lmap, QConstants.LightmapBlockWidth, QConstants.LightmapBlockHeight, self.lightmapBytes); } [UnmanagedFunctionPointer(CallingConvention.Cdecl)] diff --git a/engine/Quake/gl_texmgr.h b/engine/Quake/gl_texmgr.h index d982b3e..5702612 100644 --- a/engine/Quake/gl_texmgr.h +++ b/engine/Quake/gl_texmgr.h @@ -107,7 +107,7 @@ void GL_Bind (gltexture_t *texture); void GL_ClearBindings (void); extern qboolean UQ_GL_UploadTexture(gltexture_t *texture, unsigned *data); -extern qboolean UQ_GL_UploadLightmap(int lmap, int width, int height, byte *data); +extern qboolean UQ_GL_UploadLightmap(int lmap, byte *data); #endif /* _GL_TEXMAN_H */ diff --git a/engine/Quake/r_brush.c b/engine/Quake/r_brush.c index 2ca839d..0929ba5 100644 --- a/engine/Quake/r_brush.c +++ b/engine/Quake/r_brush.c @@ -941,7 +941,7 @@ void GL_BuildLightmaps (void) SRC_LIGHTMAP, lm->data, "", (src_offset_t)lm->data, TEXPREF_LINEAR | TEXPREF_NOPICMIP); //johnfitz - UQ_GL_UploadLightmap(i, LMBLOCK_WIDTH, LMBLOCK_HEIGHT, lm->data); + UQ_GL_UploadLightmap(i, lm->data); } //johnfitz -- warn about exceeding old limits @@ -1272,7 +1272,7 @@ static void R_UploadLightmap(int lmap) lm->rectchange.h = 0; lm->rectchange.w = 0; - UQ_GL_UploadLightmap(lmap, LMBLOCK_WIDTH, LMBLOCK_HEIGHT, lm->data); + UQ_GL_UploadLightmap(lmap, lm->data); rs_dynamiclightmaps++; } diff --git a/engine/UniQuake/gl_uniquake.c b/engine/UniQuake/gl_uniquake.c index 3ac445c..ddea92e 100644 --- a/engine/UniQuake/gl_uniquake.c +++ b/engine/UniQuake/gl_uniquake.c @@ -11,7 +11,7 @@ typedef struct unity_glcalls_s int(*UploadBrushModel)(void *target, qmodel_t *model); int(*UploadWorldModel)(void *target, qmodel_t *model); qboolean(*UploadTexture)(void *target, gltexture_t *texture, unsigned *data, GLuint *texnum); - qboolean(*UploadLightmap)(void *target, int lmap, int width, int height, byte *data); + qboolean(*UploadLightmap)(void *target, int lmap, byte *data); void(*SetupView)(void *target, vec3_t origin, vec3_t angles, mleaf_t *viewLeaf); } unity_glcalls_t; @@ -40,9 +40,9 @@ qboolean UQ_GL_UploadTexture(gltexture_t *texture, unsigned *data) return unity_glcalls->UploadTexture(unity_glcalls->target, texture, data, &texture->texnum); } -qboolean UQ_GL_UploadLightmap(int lmap, int width, int height, byte *data) +qboolean UQ_GL_UploadLightmap(int lmap, byte *data) { - return unity_glcalls->UploadLightmap(unity_glcalls->target, lmap, width, height, data); + return unity_glcalls->UploadLightmap(unity_glcalls->target, lmap, data); } void UQ_GL_SetupView(vec3_t origin, vec3_t angles, mleaf_t *viewLeaf) From 54c49bad341dbfc99b0eb2eebee2d9a554d2e54b Mon Sep 17 00:00:00 2001 From: Nico de Poel Date: Wed, 28 Jul 2021 10:52:07 +0200 Subject: [PATCH 06/11] Added some missing initialization that was messing up the view frustum calculations. This fixes dynamic lightmaps not getting updated due to surfaces being culled incorrectly. --- engine/UniQuake/vid_uniquake.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/engine/UniQuake/vid_uniquake.c b/engine/UniQuake/vid_uniquake.c index ac61e0b..2e5301b 100644 --- a/engine/UniQuake/vid_uniquake.c +++ b/engine/UniQuake/vid_uniquake.c @@ -31,6 +31,8 @@ void VID_Init(void) vid.colormap = host_colormap; vid.fullbright = 256 - LittleLong(*((int *)vid.colormap + 2048)); + + vid.recalc_refdef = 1; } void VID_Shutdown(void) @@ -47,6 +49,9 @@ void VID_Lock(void) void GL_BeginRendering(int *x, int *y, int *width, int *height) { + *x = *y = 0; + *width = vid.width; + *height = vid.height; } void GL_EndRendering(void) From fcc41b0154a2e9f1df61b12b9de7a2f83aad3904 Mon Sep 17 00:00:00 2001 From: Nico de Poel Date: Wed, 28 Jul 2021 15:06:51 +0200 Subject: [PATCH 07/11] Replaced previous Lightmapped shader with a generic Quake uber-shader, which is based on the Simple Unlit shader and allows choosing between lightmapped or blinn/phong lit, as well as emissive properties. Applied this to both the Entity and World materials, which is another step towards creating a unified and authentic look. --- Assets/Scripts/VisualStyle.cs | 9 +- Assets/Shaders/Lightmapped.shader | 303 ------------------ Assets/Shaders/LightmappedInput.hlsl | 28 -- Assets/Shaders/Quake.shader | 121 +++++++ ...htmapped.shader.meta => Quake.shader.meta} | 2 +- Assets/Shaders/QuakeForwardPass.hlsl | 140 ++++++++ ...t.hlsl.meta => QuakeForwardPass.hlsl.meta} | 2 +- Assets/Shaders/QuakeInput.hlsl | 50 +++ Assets/Shaders/QuakeInput.hlsl.meta | 10 + .../GLQuake/Materials/GLQuake_Entity.mat | 13 +- .../GLQuake/Materials/GLQuake_World.mat | 4 +- 11 files changed, 339 insertions(+), 343 deletions(-) delete mode 100644 Assets/Shaders/Lightmapped.shader delete mode 100644 Assets/Shaders/LightmappedInput.hlsl create mode 100644 Assets/Shaders/Quake.shader rename Assets/Shaders/{Lightmapped.shader.meta => Quake.shader.meta} (83%) create mode 100644 Assets/Shaders/QuakeForwardPass.hlsl rename Assets/Shaders/{LightmappedInput.hlsl.meta => QuakeForwardPass.hlsl.meta} (83%) create mode 100644 Assets/Shaders/QuakeInput.hlsl create mode 100644 Assets/Shaders/QuakeInput.hlsl.meta diff --git a/Assets/Scripts/VisualStyle.cs b/Assets/Scripts/VisualStyle.cs index d5a46f2..8cfc0b0 100644 --- a/Assets/Scripts/VisualStyle.cs +++ b/Assets/Scripts/VisualStyle.cs @@ -39,13 +39,15 @@ public class VisualStyle : ScriptableObject else if (surfaceFlags.HasFlag(QSurfaceFlags.DrawLava)) alpha = liquidProperties.lavaAlpha; else if (surfaceFlags.HasFlag(QSurfaceFlags.DrawTeleporter)) - alpha = liquidProperties.teleporterAlpha; + alpha = liquidProperties.teleAlpha; var material = new Material(liquidMaterial); material.SetColor("_BaseColor", new Color(1, 1, 1, alpha)); return material; } + // TODO: enable alpha test for DrawFence flag + return new Material(worldMaterial); } @@ -101,11 +103,12 @@ public class VisualStyle : ScriptableObject if (lightmap != null) { material.SetTexture("_LightMap", lightmap); - // TODO enable keyword (shader is still very specific) + material.EnableKeyword("_QLIGHTMAP_ON"); } } } +// TODO: should probably just use the cvars for this (r_wateralpha and such) [System.Serializable] public class LiquidProperties { @@ -114,7 +117,7 @@ public class LiquidProperties waterAlpha = 0.85f, slimeAlpha = 0.9f, lavaAlpha = 0.95f, - teleporterAlpha = 1.0f; + teleAlpha = 1.0f; } [System.Serializable] diff --git a/Assets/Shaders/Lightmapped.shader b/Assets/Shaders/Lightmapped.shader deleted file mode 100644 index eb64fd2..0000000 --- a/Assets/Shaders/Lightmapped.shader +++ /dev/null @@ -1,303 +0,0 @@ -Shader "UniQuake/Lightmapped" -{ - Properties - { - [MainTexture] _BaseMap("Texture", 2D) = "white" {} - [MainColor] _BaseColor("Color", Color) = (1, 1, 1, 1) - _Cutoff("AlphaCutout", Range(0.0, 1.0)) = 0.5 - - [NoScaleOffset] _LightMap("Lightmap", 2D) = "white" {} - - // BlendMode - [HideInInspector] _Surface("__surface", Float) = 0.0 - [HideInInspector] _Blend("__blend", Float) = 0.0 - [HideInInspector] _AlphaClip("__clip", Float) = 0.0 - [HideInInspector] _SrcBlend("Src", Float) = 1.0 - [HideInInspector] _DstBlend("Dst", Float) = 0.0 - [HideInInspector] _ZWrite("ZWrite", Float) = 1.0 - [HideInInspector] _Cull("__cull", Float) = 2.0 - - // Editmode props - [HideInInspector] _QueueOffset("Queue offset", Float) = 0.0 - - // ObsoleteProperties - [HideInInspector] _MainTex("BaseMap", 2D) = "white" {} - [HideInInspector] _Color("Base Color", Color) = (0.5, 0.5, 0.5, 1) - [HideInInspector] _SampleGI("SampleGI", float) = 0.0 // needed from bakedlit - } - SubShader - { - Tags {"RenderType" = "Opaque" "IgnoreProjector" = "True" "RenderPipeline" = "UniversalPipeline" "ShaderModel"="4.5"} - LOD 100 - - Blend [_SrcBlend][_DstBlend] - ZWrite [_ZWrite] - Cull [_Cull] - - Pass - { - Name "Lightmapped" - - HLSLPROGRAM - #pragma exclude_renderers gles gles3 glcore - #pragma target 4.5 - - #pragma vertex vert - #pragma fragment frag - #pragma shader_feature_local_fragment _ALPHATEST_ON - #pragma shader_feature_local_fragment _ALPHAPREMULTIPLY_ON - - // ------------------------------------- - // Unity defined keywords - #pragma multi_compile_fog - #pragma multi_compile_instancing - #pragma multi_compile _ DOTS_INSTANCING_ON - - #include "LightmappedInput.hlsl" - - struct Attributes - { - float4 positionOS : POSITION; - float2 uv : TEXCOORD0; - float2 uv2 : TEXCOORD1; - UNITY_VERTEX_INPUT_INSTANCE_ID - }; - - struct Varyings - { - float2 uv : TEXCOORD0; - float2 uv2 : TEXCOORD1; - float4 vertex : SV_POSITION; - - UNITY_VERTEX_INPUT_INSTANCE_ID - UNITY_VERTEX_OUTPUT_STEREO - }; - - Varyings vert(Attributes input) - { - Varyings output = (Varyings)0; - - UNITY_SETUP_INSTANCE_ID(input); - UNITY_TRANSFER_INSTANCE_ID(input, output); - UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(output); - - VertexPositionInputs vertexInput = GetVertexPositionInputs(input.positionOS.xyz); - output.vertex = vertexInput.positionCS; - output.uv = TRANSFORM_TEX(input.uv, _BaseMap); - output.uv2 = TRANSFORM_TEX(input.uv2, _LightMap); - //output.fogCoord = ComputeFogFactor(vertexInput.positionCS.z); - - return output; - } - - half4 frag(Varyings input) : SV_Target - { - UNITY_SETUP_INSTANCE_ID(input); - UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(input); - - half2 uv = input.uv; - half4 texColor = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, uv); - half3 color = texColor.rgb * _BaseColor.rgb; - half alpha = texColor.a * _BaseColor.a; - AlphaDiscard(alpha, _Cutoff); - - half4 lightmapColor = SAMPLE_TEXTURE2D(_LightMap, sampler_LightMap, input.uv2); - color.rgb = color.rgb * lightmapColor.rgb * 2.0f; - //color.rgb = lightmapColor.rgb; - -#ifdef _ALPHAPREMULTIPLY_ON - color *= alpha; -#endif - - //color = MixFog(color, input.fogCoord); - - return half4(color, alpha); - } - ENDHLSL - } - Pass - { - Name "DepthOnly" - Tags{"LightMode" = "DepthOnly"} - - ZWrite On - ColorMask 0 - - HLSLPROGRAM - #pragma exclude_renderers gles gles3 glcore - #pragma target 4.5 - - #pragma vertex DepthOnlyVertex - #pragma fragment DepthOnlyFragment - - // ------------------------------------- - // Material Keywords - #pragma shader_feature_local_fragment _ALPHATEST_ON - - //-------------------------------------- - // GPU Instancing - #pragma multi_compile_instancing - #pragma multi_compile _ DOTS_INSTANCING_ON - - #include "LightmappedInput.hlsl" - #include "Packages/com.unity.render-pipelines.universal/Shaders/DepthOnlyPass.hlsl" - ENDHLSL - } - - // This pass it not used during regular rendering, only for lightmap baking. - Pass - { - Name "Meta" - Tags{"LightMode" = "Meta"} - - Cull Off - - HLSLPROGRAM - #pragma exclude_renderers gles gles3 glcore - #pragma target 4.5 - - #pragma vertex UniversalVertexMeta - #pragma fragment UniversalFragmentMetaUnlit - - #include "LightmappedInput.hlsl" - #include "Packages/com.unity.render-pipelines.universal/Shaders/UnlitMetaPass.hlsl" - - ENDHLSL - } - } - - SubShader - { - Tags {"RenderType" = "Opaque" "IgnoreProjector" = "True" "RenderPipeline" = "UniversalPipeline" "ShaderModel"="2.0"} - LOD 100 - - Blend [_SrcBlend][_DstBlend] - ZWrite [_ZWrite] - Cull [_Cull] - - Pass - { - Name "Lightmapped" - HLSLPROGRAM - #pragma only_renderers gles gles3 glcore d3d11 - #pragma target 2.0 - - #pragma vertex vert - #pragma fragment frag - #pragma shader_feature_local_fragment _ALPHATEST_ON - #pragma shader_feature_local_fragment _ALPHAPREMULTIPLY_ON - - // ------------------------------------- - // Unity defined keywords - #pragma multi_compile_fog - #pragma multi_compile_instancing - - #include "LightmappedInput.hlsl" - - struct Attributes - { - float4 positionOS : POSITION; - float2 uv : TEXCOORD0; - UNITY_VERTEX_INPUT_INSTANCE_ID - }; - - struct Varyings - { - float2 uv : TEXCOORD0; - float fogCoord : TEXCOORD1; - float4 vertex : SV_POSITION; - - UNITY_VERTEX_INPUT_INSTANCE_ID - UNITY_VERTEX_OUTPUT_STEREO - }; - - Varyings vert(Attributes input) - { - Varyings output = (Varyings)0; - - UNITY_SETUP_INSTANCE_ID(input); - UNITY_TRANSFER_INSTANCE_ID(input, output); - UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(output); - - VertexPositionInputs vertexInput = GetVertexPositionInputs(input.positionOS.xyz); - output.vertex = vertexInput.positionCS; - output.uv = TRANSFORM_TEX(input.uv, _BaseMap); - output.fogCoord = ComputeFogFactor(vertexInput.positionCS.z); - - return output; - } - - half4 frag(Varyings input) : SV_Target - { - UNITY_SETUP_INSTANCE_ID(input); - UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(input); - - half2 uv = input.uv; - half4 texColor = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, uv); - half3 color = texColor.rgb * _BaseColor.rgb; - half alpha = texColor.a * _BaseColor.a; - AlphaDiscard(alpha, _Cutoff); - -#ifdef _ALPHAPREMULTIPLY_ON - color *= alpha; -#endif - - color = MixFog(color, input.fogCoord); - alpha = OutputAlpha(alpha, _Surface); - - return half4(color, alpha); - } - ENDHLSL - } - Pass - { - Name "DepthOnly" - Tags{"LightMode" = "DepthOnly"} - - ZWrite On - ColorMask 0 - - HLSLPROGRAM - #pragma only_renderers gles gles3 glcore d3d11 - #pragma target 2.0 - - #pragma vertex DepthOnlyVertex - #pragma fragment DepthOnlyFragment - - // ------------------------------------- - // Material Keywords - #pragma shader_feature_local_fragment _ALPHATEST_ON - - //-------------------------------------- - // GPU Instancing - #pragma multi_compile_instancing - - #include "LightmappedInput.hlsl" - #include "Packages/com.unity.render-pipelines.universal/Shaders/DepthOnlyPass.hlsl" - ENDHLSL - } - - // This pass it not used during regular rendering, only for lightmap baking. - Pass - { - Name "Meta" - Tags{"LightMode" = "Meta"} - - Cull Off - - HLSLPROGRAM - #pragma only_renderers gles gles3 glcore d3d11 - #pragma target 2.0 - - #pragma vertex UniversalVertexMeta - #pragma fragment UniversalFragmentMetaUnlit - - #include "LightmappedInput.hlsl" - #include "Packages/com.unity.render-pipelines.universal/Shaders/UnlitMetaPass.hlsl" - - ENDHLSL - } - } - FallBack "Hidden/Universal Render Pipeline/FallbackError" - //CustomEditor "UnityEditor.Rendering.Universal.ShaderGUI.UnlitShader" -} diff --git a/Assets/Shaders/LightmappedInput.hlsl b/Assets/Shaders/LightmappedInput.hlsl deleted file mode 100644 index b4cddf4..0000000 --- a/Assets/Shaders/LightmappedInput.hlsl +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef UNIVERSAL_UNLIT_INPUT_INCLUDED -#define UNIVERSAL_UNLIT_INPUT_INCLUDED - -#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/SurfaceInput.hlsl" - -TEXTURE2D(_LightMap); SAMPLER(sampler_LightMap); - -CBUFFER_START(UnityPerMaterial) - float4 _BaseMap_ST; - float4 _LightMap_ST; - half4 _BaseColor; - half _Cutoff; - half _Surface; -CBUFFER_END - -#ifdef UNITY_DOTS_INSTANCING_ENABLED -UNITY_DOTS_INSTANCING_START(MaterialPropertyMetadata) - UNITY_DOTS_INSTANCED_PROP(float4, _BaseColor) - UNITY_DOTS_INSTANCED_PROP(float , _Cutoff) - UNITY_DOTS_INSTANCED_PROP(float , _Surface) -UNITY_DOTS_INSTANCING_END(MaterialPropertyMetadata) - -#define _BaseColor UNITY_ACCESS_DOTS_INSTANCED_PROP_FROM_MACRO(float4 , Metadata__BaseColor) -#define _Cutoff UNITY_ACCESS_DOTS_INSTANCED_PROP_FROM_MACRO(float , Metadata__Cutoff) -#define _Surface UNITY_ACCESS_DOTS_INSTANCED_PROP_FROM_MACRO(float , Metadata__Surface) -#endif - -#endif diff --git a/Assets/Shaders/Quake.shader b/Assets/Shaders/Quake.shader new file mode 100644 index 0000000..dee7ccc --- /dev/null +++ b/Assets/Shaders/Quake.shader @@ -0,0 +1,121 @@ +// Shader targeted for low end devices. Single Pass Forward Rendering. +Shader "UniQuake/Quake" +{ + Properties + { + [MainTexture] _BaseMap("Base Map (RGB) Alpha (A)", 2D) = "white" {} + [MainColor] _BaseColor("Base Color", Color) = (1, 1, 1, 1) + + [NoScaleOffset] _LightMap("Lightmap (RGBA)", 2D) = "white" {} + + _Cutoff("Alpha Clipping", Range(0.0, 1.0)) = 0.666 + + [HDR] _EmissionColor("Emission Color", Color) = (0,0,0) + [NoScaleOffset]_EmissionMap("Emission Map", 2D) = "white" {} + + // Blending state + [HideInInspector] _Surface("__surface", Float) = 0.0 + [HideInInspector] _Blend("__blend", Float) = 0.0 + [HideInInspector] _AlphaClip("__clip", Float) = 0.0 + [HideInInspector] _SrcBlend("__src", Float) = 1.0 + [HideInInspector] _DstBlend("__dst", Float) = 0.0 + [HideInInspector] _ZWrite("__zw", Float) = 1.0 + [HideInInspector] _Cull("__cull", Float) = 2.0 + } + + SubShader + { + Tags { "RenderType" = "Opaque" "RenderPipeline" = "UniversalPipeline" "UniversalMaterialType" = "SimpleLit" "IgnoreProjector" = "True" "ShaderModel"="4.5"} + LOD 300 + + Pass + { + Name "ForwardLit" + Tags { "LightMode" = "UniversalForward" } + + // Use same blending / depth states as Standard shader + Blend[_SrcBlend][_DstBlend] + ZWrite[_ZWrite] + Cull[_Cull] + + HLSLPROGRAM + #pragma exclude_renderers gles gles3 glcore + #pragma target 4.5 + + // ------------------------------------- + // Material Keywords + #pragma shader_feature_local_fragment _ALPHATEST_ON + #pragma shader_feature_local_fragment _EMISSION + #pragma shader_feature_local _QLIGHTMAP_ON + + // ------------------------------------- + // Universal Pipeline keywords + #pragma multi_compile _ _ADDITIONAL_LIGHTS_VERTEX _ADDITIONAL_LIGHTS + + // ------------------------------------- + // Unity defined keywords + #pragma multi_compile_fog + + #pragma vertex LitPassVertexSimple + #pragma fragment LitPassFragmentSimple + #define BUMP_SCALE_NOT_SUPPORTED 1 + + #include "QuakeInput.hlsl" + #include "QuakeForwardPass.hlsl" + ENDHLSL + } + + Pass + { + Name "DepthOnly" + Tags{"LightMode" = "DepthOnly"} + + ZWrite On + ColorMask 0 + Cull[_Cull] + + HLSLPROGRAM + #pragma exclude_renderers gles gles3 glcore + #pragma target 4.5 + + #pragma vertex DepthOnlyVertex + #pragma fragment DepthOnlyFragment + + // ------------------------------------- + // Material Keywords + #pragma shader_feature_local_fragment _ALPHATEST_ON + + #include "QuakeInput.hlsl" + #include "Packages/com.unity.render-pipelines.universal/Shaders/DepthOnlyPass.hlsl" + ENDHLSL + } + + // This pass is used when drawing to a _CameraNormalsTexture texture + Pass + { + Name "DepthNormals" + Tags{"LightMode" = "DepthNormals"} + + ZWrite On + Cull[_Cull] + + HLSLPROGRAM + #pragma exclude_renderers gles gles3 glcore + #pragma target 4.5 + + #pragma vertex DepthNormalsVertex + #pragma fragment DepthNormalsFragment + + // ------------------------------------- + // Material Keywords + #pragma shader_feature_local_fragment _ALPHATEST_ON + + #include "QuakeInput.hlsl" + #include "Packages/com.unity.render-pipelines.universal/Shaders/DepthNormalsPass.hlsl" + ENDHLSL + } + } + + Fallback "Hidden/Universal Render Pipeline/FallbackError" + //CustomEditor "UnityEditor.Rendering.Universal.ShaderGUI.SimpleLitShader" +} diff --git a/Assets/Shaders/Lightmapped.shader.meta b/Assets/Shaders/Quake.shader.meta similarity index 83% rename from Assets/Shaders/Lightmapped.shader.meta rename to Assets/Shaders/Quake.shader.meta index 9471dee..8e8a082 100644 --- a/Assets/Shaders/Lightmapped.shader.meta +++ b/Assets/Shaders/Quake.shader.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 91d80b5043d41db478595fab96d6e2c2 +guid: eb7abeca30cd4fb4cb8be05eca69f850 ShaderImporter: externalObjects: {} defaultTextures: [] diff --git a/Assets/Shaders/QuakeForwardPass.hlsl b/Assets/Shaders/QuakeForwardPass.hlsl new file mode 100644 index 0000000..f92b030 --- /dev/null +++ b/Assets/Shaders/QuakeForwardPass.hlsl @@ -0,0 +1,140 @@ +#ifndef UNIVERSAL_SIMPLE_LIT_PASS_INCLUDED +#define UNIVERSAL_SIMPLE_LIT_PASS_INCLUDED + +#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl" + +struct Attributes +{ + float4 positionOS : POSITION; + float3 normalOS : NORMAL; + float4 tangentOS : TANGENT; + float2 texcoord : TEXCOORD0; +#ifdef _QLIGHTMAP_ON + float2 lightmapUV : TEXCOORD1; +#endif + UNITY_VERTEX_INPUT_INSTANCE_ID +}; + +struct Varyings +{ + float2 uv : TEXCOORD0; +#ifdef _QLIGHTMAP_ON + float2 lightmapUV : TEXCOORD1; +#else + half3 vertexSH : TEXCOORD1; +#endif + + float3 posWS : TEXCOORD2; // xyz: posWS + + float3 normal : TEXCOORD3; + float3 viewDir : TEXCOORD4; + + half4 fogFactorAndVertexLight : TEXCOORD6; // x: fogFactor, yzw: vertex light + + float4 positionCS : SV_POSITION; + UNITY_VERTEX_INPUT_INSTANCE_ID + UNITY_VERTEX_OUTPUT_STEREO +}; + +void InitializeInputData(Varyings input, out InputData inputData) +{ + inputData.positionWS = input.posWS; + + half3 viewDirWS = input.viewDir; + inputData.normalWS = input.normal; + + inputData.normalWS = NormalizeNormalPerPixel(inputData.normalWS); + viewDirWS = SafeNormalize(viewDirWS); + + inputData.viewDirectionWS = viewDirWS; + + inputData.shadowCoord = float4(0, 0, 0, 0); + + inputData.fogCoord = input.fogFactorAndVertexLight.x; + inputData.vertexLighting = input.fogFactorAndVertexLight.yzw; +#ifdef _QLIGHTMAP_ON + inputData.bakedGI = 0.0; +#else + inputData.bakedGI = SampleSHPixel(input.vertexSH, inputData.normalWS); +#endif + inputData.normalizedScreenSpaceUV = GetNormalizedScreenSpaceUV(input.positionCS); + inputData.shadowMask = 0.0; +} + +/////////////////////////////////////////////////////////////////////////////// +// Vertex and Fragment functions // +/////////////////////////////////////////////////////////////////////////////// + +// Used in Standard (Simple Lighting) shader +Varyings LitPassVertexSimple(Attributes input) +{ + Varyings output = (Varyings)0; + + UNITY_SETUP_INSTANCE_ID(input); + UNITY_TRANSFER_INSTANCE_ID(input, output); + UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(output); + + VertexPositionInputs vertexInput = GetVertexPositionInputs(input.positionOS.xyz); + VertexNormalInputs normalInput = GetVertexNormalInputs(input.normalOS, input.tangentOS); + half3 viewDirWS = GetWorldSpaceViewDir(vertexInput.positionWS); + half3 vertexLight = VertexLighting(vertexInput.positionWS, normalInput.normalWS); + half fogFactor = ComputeFogFactor(vertexInput.positionCS.z); + + output.uv = TRANSFORM_TEX(input.texcoord, _BaseMap); + output.posWS.xyz = vertexInput.positionWS; + output.positionCS = vertexInput.positionCS; + + output.normal = NormalizeNormalPerVertex(normalInput.normalWS); + output.viewDir = viewDirWS; + +#ifdef _QLIGHTMAP_ON + output.lightmapUV.xy = input.lightmapUV.xy; +#else + output.vertexSH.xyz = SampleSHVertex(output.normal.xyz); +#endif + + output.fogFactorAndVertexLight = half4(fogFactor, vertexLight); + + return output; +} + +// Used for StandardSimpleLighting shader +half4 LitPassFragmentSimple(Varyings input) : SV_Target +{ + UNITY_SETUP_INSTANCE_ID(input); + UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(input); + + float2 uv = input.uv; + half4 diffuseAlpha = SampleAlbedoAlpha(uv, TEXTURE2D_ARGS(_BaseMap, sampler_BaseMap)); + half3 diffuse = diffuseAlpha.rgb * _BaseColor.rgb; + + half alpha = diffuseAlpha.a * _BaseColor.a; + AlphaDiscard(alpha, _Cutoff); + + InputData inputData; + InitializeInputData(input, inputData); + +#ifdef _QLIGHTMAP_ON + half4 lightmapColor = SAMPLE_TEXTURE2D(_LightMap, sampler_LightMap, input.lightmapUV); + half3 finalColor = diffuse * lightmapColor.rgb * 2.0f; +#else + // Specular and smoothness are some bogus values just to make models not appear completely black on one side + half3 finalColor = UniversalFragmentBlinnPhong(inputData, diffuse, half4(0.2, 0.2, 0.2, 1), 0.5, 0.0, alpha).rgb; + // Light light = GetMainLight(); + // half3 diffuseColor = LightingLambert(light.color, light.direction, inputData.normalWS); + // half3 finalColor = diffuse * diffuseColor; +#endif + +#ifdef _EMISSION + finalColor += SampleEmission(uv, _EmissionColor.rgb, TEXTURE2D_ARGS(_EmissionMap, sampler_EmissionMap)); +#endif + + half4 color = half4(finalColor, alpha); + + color.rgb = MixFog(color.rgb, inputData.fogCoord); + color.a = OutputAlpha(color.a, _Surface); + + return color; +} + +#endif diff --git a/Assets/Shaders/LightmappedInput.hlsl.meta b/Assets/Shaders/QuakeForwardPass.hlsl.meta similarity index 83% rename from Assets/Shaders/LightmappedInput.hlsl.meta rename to Assets/Shaders/QuakeForwardPass.hlsl.meta index de4f011..536b92b 100644 --- a/Assets/Shaders/LightmappedInput.hlsl.meta +++ b/Assets/Shaders/QuakeForwardPass.hlsl.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: fe2f7d54113588c42925192dff3809e9 +guid: 6aa574d369884d24e8f28b300a86fcb3 ShaderImporter: externalObjects: {} defaultTextures: [] diff --git a/Assets/Shaders/QuakeInput.hlsl b/Assets/Shaders/QuakeInput.hlsl new file mode 100644 index 0000000..f8ee631 --- /dev/null +++ b/Assets/Shaders/QuakeInput.hlsl @@ -0,0 +1,50 @@ +#ifndef UNIVERSAL_SIMPLE_LIT_INPUT_INCLUDED +#define UNIVERSAL_SIMPLE_LIT_INPUT_INCLUDED + +#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl" +#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/SurfaceInput.hlsl" + +CBUFFER_START(UnityPerMaterial) + float4 _BaseMap_ST; + float4 _LightMap_ST; + half4 _BaseColor; + half4 _EmissionColor; + half _Cutoff; + half _Surface; +CBUFFER_END + +#ifdef UNITY_DOTS_INSTANCING_ENABLED + UNITY_DOTS_INSTANCING_START(MaterialPropertyMetadata) + UNITY_DOTS_INSTANCED_PROP(float4, _BaseColor) + UNITY_DOTS_INSTANCED_PROP(float4, _EmissionColor) + UNITY_DOTS_INSTANCED_PROP(float , _Cutoff) + UNITY_DOTS_INSTANCED_PROP(float , _Surface) + UNITY_DOTS_INSTANCING_END(MaterialPropertyMetadata) + + #define _BaseColor UNITY_ACCESS_DOTS_INSTANCED_PROP_FROM_MACRO(float4 , Metadata__BaseColor) + #define _EmissionColor UNITY_ACCESS_DOTS_INSTANCED_PROP_FROM_MACRO(float4 , Metadata__EmissionColor) + #define _Cutoff UNITY_ACCESS_DOTS_INSTANCED_PROP_FROM_MACRO(float , Metadata__Cutoff) + #define _Surface UNITY_ACCESS_DOTS_INSTANCED_PROP_FROM_MACRO(float , Metadata__Surface) +#endif + +TEXTURE2D(_LightMap); SAMPLER(sampler_LightMap); + +inline void InitializeSimpleLitSurfaceData(float2 uv, out SurfaceData outSurfaceData) +{ + outSurfaceData = (SurfaceData)0; + + half4 albedoAlpha = SampleAlbedoAlpha(uv, TEXTURE2D_ARGS(_BaseMap, sampler_BaseMap)); + outSurfaceData.alpha = albedoAlpha.a * _BaseColor.a; + AlphaDiscard(outSurfaceData.alpha, _Cutoff); + + outSurfaceData.albedo = albedoAlpha.rgb * _BaseColor.rgb; + + outSurfaceData.metallic = 0.0; // unused + outSurfaceData.specular = 0.0; // unused + outSurfaceData.smoothness = 0.0; // unused + //outSurfaceData.normalTS = SampleNormal(uv, TEXTURE2D_ARGS(_BumpMap, sampler_BumpMap)); + outSurfaceData.occlusion = 1.0; // unused + outSurfaceData.emission = SampleEmission(uv, _EmissionColor.rgb, TEXTURE2D_ARGS(_EmissionMap, sampler_EmissionMap)); +} + +#endif diff --git a/Assets/Shaders/QuakeInput.hlsl.meta b/Assets/Shaders/QuakeInput.hlsl.meta new file mode 100644 index 0000000..2101089 --- /dev/null +++ b/Assets/Shaders/QuakeInput.hlsl.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: 209b8f66e5d711946ba4c4ccb9e0a9b4 +ShaderImporter: + externalObjects: {} + defaultTextures: [] + nonModifiableTextures: [] + preprocessorOverride: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Styles/GLQuake/Materials/GLQuake_Entity.mat b/Assets/Styles/GLQuake/Materials/GLQuake_Entity.mat index 35a8d74..5d29c93 100644 --- a/Assets/Styles/GLQuake/Materials/GLQuake_Entity.mat +++ b/Assets/Styles/GLQuake/Materials/GLQuake_Entity.mat @@ -8,14 +8,13 @@ Material: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_Name: GLQuake_Entity - m_Shader: {fileID: 4800000, guid: 8d2bb70cbf9db8d4da26e15b26e74248, type: 3} + m_Shader: {fileID: 4800000, guid: eb7abeca30cd4fb4cb8be05eca69f850, type: 3} m_ShaderKeywords: _EMISSION _RECEIVE_SHADOWS_OFF m_LightmapFlags: 2 m_EnableInstancingVariants: 0 m_DoubleSidedGI: 0 - m_CustomRenderQueue: 2000 - stringTagMap: - RenderType: Opaque + m_CustomRenderQueue: -1 + stringTagMap: {} disabledShaderPasses: [] m_SavedProperties: serializedVersion: 3 @@ -44,6 +43,10 @@ Material: m_Texture: {fileID: 0} m_Scale: {x: 1, y: 1} m_Offset: {x: 0, y: 0} + - _LightMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} - _MainTex: m_Texture: {fileID: 0} m_Scale: {x: 1, y: 1} @@ -83,7 +86,7 @@ Material: - _ClearCoatMask: 0 - _ClearCoatSmoothness: 0 - _Cull: 2 - - _Cutoff: 0.5 + - _Cutoff: 0.666 - _DetailAlbedoMapScale: 1 - _DetailNormalMapScale: 1 - _DstBlend: 0 diff --git a/Assets/Styles/GLQuake/Materials/GLQuake_World.mat b/Assets/Styles/GLQuake/Materials/GLQuake_World.mat index d2ba49e..e0159f7 100644 --- a/Assets/Styles/GLQuake/Materials/GLQuake_World.mat +++ b/Assets/Styles/GLQuake/Materials/GLQuake_World.mat @@ -21,7 +21,7 @@ Material: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_Name: GLQuake_World - m_Shader: {fileID: 4800000, guid: 91d80b5043d41db478595fab96d6e2c2, type: 3} + m_Shader: {fileID: 4800000, guid: eb7abeca30cd4fb4cb8be05eca69f850, type: 3} m_ShaderKeywords: _EMISSION _RECEIVE_SHADOWS_OFF m_LightmapFlags: 2 m_EnableInstancingVariants: 0 @@ -104,7 +104,7 @@ Material: - _ClearCoatMask: 0 - _ClearCoatSmoothness: 0 - _Cull: 2 - - _Cutoff: 0.5 + - _Cutoff: 0.666 - _DetailAlbedoMapScale: 1 - _DetailNormalMapScale: 1 - _DstBlend: 0 From fc051df6da76fb222aac1c5f7b2cc1fed4d33687 Mon Sep 17 00:00:00 2001 From: Nico de Poel Date: Wed, 28 Jul 2021 16:55:17 +0200 Subject: [PATCH 08/11] Added support for point sampling and affine texturing to the custom Quake shader, both of which can be enabled/disabled through the visual style. Also changes all of the keywords into multi_compile, so they aren't stripped during builds (all of these are controlled through code). --- Assets/Scripts/Game/GameAssets.cs | 2 +- Assets/Scripts/UniQuake.cs | 1 + Assets/Scripts/VisualStyle.cs | 23 +++++++++++++++++++++++ Assets/Shaders/Quake.shader | 11 ++++++----- Assets/Shaders/QuakeForwardPass.hlsl | 23 ++++++++++++++++++++--- Assets/Shaders/QuakeInput.hlsl | 2 ++ Assets/Styles/GLQuake/GLQuake.asset | 4 +++- 7 files changed, 56 insertions(+), 10 deletions(-) diff --git a/Assets/Scripts/Game/GameAssets.cs b/Assets/Scripts/Game/GameAssets.cs index 88eb99d..b0db6dc 100644 --- a/Assets/Scripts/Game/GameAssets.cs +++ b/Assets/Scripts/Game/GameAssets.cs @@ -66,7 +66,7 @@ public class GameAssets { if (!lightmaps.TryGetValue(lightmapNum, out var lightmap)) { - lightmap = new Texture2D(width, height, TextureFormat.RGBA32, false) { name = $"Lightmap_{lightmapNum}" }; + lightmap = new Texture2D(width, height, TextureFormat.RGBA32, false) { name = $"Lightmap_{lightmapNum}", wrapMode = TextureWrapMode.Clamp }; lightmaps.Add(lightmapNum, lightmap); } diff --git a/Assets/Scripts/UniQuake.cs b/Assets/Scripts/UniQuake.cs index 36f8c8b..7e34167 100644 --- a/Assets/Scripts/UniQuake.cs +++ b/Assets/Scripts/UniQuake.cs @@ -211,6 +211,7 @@ public partial class UniQuake: MonoBehaviour if (CurrentStyle == null) { CurrentStyle = style; + CurrentStyle.Activate(); return; } diff --git a/Assets/Scripts/VisualStyle.cs b/Assets/Scripts/VisualStyle.cs index 8cfc0b0..88422b5 100644 --- a/Assets/Scripts/VisualStyle.cs +++ b/Assets/Scripts/VisualStyle.cs @@ -15,6 +15,12 @@ public class VisualStyle : ScriptableObject [SerializeField] protected Material liquidMaterial; + [SerializeField] + protected bool pointSampling; + + [SerializeField] + protected bool affineTexturing; + [SerializeField] protected LiquidProperties liquidProperties = new LiquidProperties(); @@ -22,6 +28,14 @@ public class VisualStyle : ScriptableObject protected ParticleSystems particles = new ParticleSystems(); public ParticleSystems Particles => particles; + public virtual void Activate() + { + if (pointSampling) + Shader.EnableKeyword("_POINT_SAMPLING"); + else + Shader.DisableKeyword("_POINT_SAMPLING"); + } + public virtual Material CreateEntityMaterial() { return new Material(entityMaterial); @@ -86,6 +100,11 @@ public class VisualStyle : ScriptableObject material.EnableKeyword("_EMISSION"); else material.DisableKeyword("_EMISSION"); + + if (affineTexturing) + material.EnableKeyword("_AFFINE_ON"); + else + material.DisableKeyword("_AFFINE_ON"); } public virtual void SetWorldTextures(Material material, Texture2D mainTexture, Texture2D fullBright, Texture2D lightmap) @@ -105,6 +124,10 @@ public class VisualStyle : ScriptableObject material.SetTexture("_LightMap", lightmap); material.EnableKeyword("_QLIGHTMAP_ON"); } + else + { + material.DisableKeyword("_QLIGHTMAP_ON"); + } } } diff --git a/Assets/Shaders/Quake.shader b/Assets/Shaders/Quake.shader index dee7ccc..d6b420f 100644 --- a/Assets/Shaders/Quake.shader +++ b/Assets/Shaders/Quake.shader @@ -44,13 +44,14 @@ Shader "UniQuake/Quake" // ------------------------------------- // Material Keywords - #pragma shader_feature_local_fragment _ALPHATEST_ON - #pragma shader_feature_local_fragment _EMISSION - #pragma shader_feature_local _QLIGHTMAP_ON + #pragma multi_compile_local __ _ALPHATEST_ON + #pragma multi_compile_local __ _EMISSION + #pragma multi_compile_local __ _QLIGHTMAP_ON _AFFINE_ON // Lightmapping and affine texturing are mutually exclusive // ------------------------------------- // Universal Pipeline keywords #pragma multi_compile _ _ADDITIONAL_LIGHTS_VERTEX _ADDITIONAL_LIGHTS + #pragma multi_compile _ _POINT_SAMPLING // ------------------------------------- // Unity defined keywords @@ -83,7 +84,7 @@ Shader "UniQuake/Quake" // ------------------------------------- // Material Keywords - #pragma shader_feature_local_fragment _ALPHATEST_ON + #pragma multi_compile_local __ _ALPHATEST_ON #include "QuakeInput.hlsl" #include "Packages/com.unity.render-pipelines.universal/Shaders/DepthOnlyPass.hlsl" @@ -108,7 +109,7 @@ Shader "UniQuake/Quake" // ------------------------------------- // Material Keywords - #pragma shader_feature_local_fragment _ALPHATEST_ON + #pragma multi_compile_local __ _ALPHATEST_ON #include "QuakeInput.hlsl" #include "Packages/com.unity.render-pipelines.universal/Shaders/DepthNormalsPass.hlsl" diff --git a/Assets/Shaders/QuakeForwardPass.hlsl b/Assets/Shaders/QuakeForwardPass.hlsl index f92b030..76c1465 100644 --- a/Assets/Shaders/QuakeForwardPass.hlsl +++ b/Assets/Shaders/QuakeForwardPass.hlsl @@ -17,7 +17,11 @@ struct Attributes struct Varyings { +#ifdef _AFFINE_ON + noperspective +#endif float2 uv : TEXCOORD0; + #ifdef _QLIGHTMAP_ON float2 lightmapUV : TEXCOORD1; #else @@ -104,8 +108,15 @@ half4 LitPassFragmentSimple(Varyings input) : SV_Target UNITY_SETUP_INSTANCE_ID(input); UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(input); - float2 uv = input.uv; - half4 diffuseAlpha = SampleAlbedoAlpha(uv, TEXTURE2D_ARGS(_BaseMap, sampler_BaseMap)); + float2 uv = input.uv.xy; + half4 diffuseAlpha = SampleAlbedoAlpha(uv, + #if _POINT_SAMPLING + TEXTURE2D_ARGS(_BaseMap, point_repeat_sampler_BaseMap) + #else + TEXTURE2D_ARGS(_BaseMap, sampler_BaseMap) + #endif + ); + half3 diffuse = diffuseAlpha.rgb * _BaseColor.rgb; half alpha = diffuseAlpha.a * _BaseColor.a; @@ -126,7 +137,13 @@ half4 LitPassFragmentSimple(Varyings input) : SV_Target #endif #ifdef _EMISSION - finalColor += SampleEmission(uv, _EmissionColor.rgb, TEXTURE2D_ARGS(_EmissionMap, sampler_EmissionMap)); + finalColor += SampleEmission(uv, _EmissionColor.rgb, + #if _POINT_SAMPLING + TEXTURE2D_ARGS(_EmissionMap, point_repeat_sampler_EmissionMap) + #else + TEXTURE2D_ARGS(_EmissionMap, sampler_EmissionMap) + #endif + ); #endif half4 color = half4(finalColor, alpha); diff --git a/Assets/Shaders/QuakeInput.hlsl b/Assets/Shaders/QuakeInput.hlsl index f8ee631..bec3fce 100644 --- a/Assets/Shaders/QuakeInput.hlsl +++ b/Assets/Shaders/QuakeInput.hlsl @@ -27,6 +27,8 @@ CBUFFER_END #define _Surface UNITY_ACCESS_DOTS_INSTANCED_PROP_FROM_MACRO(float , Metadata__Surface) #endif + SAMPLER(point_repeat_sampler_BaseMap); + SAMPLER(point_repeat_sampler_EmissionMap); TEXTURE2D(_LightMap); SAMPLER(sampler_LightMap); inline void InitializeSimpleLitSurfaceData(float2 uv, out SurfaceData outSurfaceData) diff --git a/Assets/Styles/GLQuake/GLQuake.asset b/Assets/Styles/GLQuake/GLQuake.asset index eba8ec3..e2a156a 100644 --- a/Assets/Styles/GLQuake/GLQuake.asset +++ b/Assets/Styles/GLQuake/GLQuake.asset @@ -15,10 +15,12 @@ MonoBehaviour: entityMaterial: {fileID: 2100000, guid: 4d7703ac1adf3534f89b4041b779ff5e, type: 2} worldMaterial: {fileID: 2100000, guid: fcbaf32c00bea2344bbb1419c61364b6, type: 2} liquidMaterial: {fileID: 2100000, guid: cd59502a1689c0847a1963c60e347987, type: 2} + pointSampling: 0 + affineTexturing: 0 liquidProperties: waterAlpha: 0.8 slimeAlpha: 0.85 lavaAlpha: 0.9 - teleporterAlpha: 1 + teleAlpha: 1 particles: explosion: {fileID: 0} From f0bda1fcb1466a7c0a969ceb3020eb17ed5421d8 Mon Sep 17 00:00:00 2001 From: Nico de Poel Date: Wed, 28 Jul 2021 17:21:31 +0200 Subject: [PATCH 09/11] Added a Software mode visual style, with point sampling and affine texturing, and without any liquid transparencies. --- Assets/Styles/GLQuake/Software.asset | 26 +++++++++++++++++++++++ Assets/Styles/GLQuake/Software.asset.meta | 8 +++++++ 2 files changed, 34 insertions(+) create mode 100644 Assets/Styles/GLQuake/Software.asset create mode 100644 Assets/Styles/GLQuake/Software.asset.meta diff --git a/Assets/Styles/GLQuake/Software.asset b/Assets/Styles/GLQuake/Software.asset new file mode 100644 index 0000000..c78a3db --- /dev/null +++ b/Assets/Styles/GLQuake/Software.asset @@ -0,0 +1,26 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &11400000 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 2df0cbad06a6ed5489201e395f9c5bb3, type: 3} + m_Name: Software + m_EditorClassIdentifier: + entityMaterial: {fileID: 2100000, guid: 4d7703ac1adf3534f89b4041b779ff5e, type: 2} + worldMaterial: {fileID: 2100000, guid: fcbaf32c00bea2344bbb1419c61364b6, type: 2} + liquidMaterial: {fileID: 2100000, guid: cd59502a1689c0847a1963c60e347987, type: 2} + pointSampling: 1 + affineTexturing: 1 + liquidProperties: + waterAlpha: 1 + slimeAlpha: 1 + lavaAlpha: 1 + teleAlpha: 1 + particles: + explosion: {fileID: 0} diff --git a/Assets/Styles/GLQuake/Software.asset.meta b/Assets/Styles/GLQuake/Software.asset.meta new file mode 100644 index 0000000..d0e02ab --- /dev/null +++ b/Assets/Styles/GLQuake/Software.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 9f4246ec8cb97bd4d98b1f583ac1d2cd +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: From 475444f90495456f2914044d99b06f954f7547a4 Mon Sep 17 00:00:00 2001 From: Nico de Poel Date: Wed, 28 Jul 2021 17:36:33 +0200 Subject: [PATCH 10/11] We can share one point sampler for both diffuse and emissive textures, so reduced it down to one --- Assets/Shaders/QuakeForwardPass.hlsl | 4 ++-- Assets/Shaders/QuakeInput.hlsl | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/Assets/Shaders/QuakeForwardPass.hlsl b/Assets/Shaders/QuakeForwardPass.hlsl index 76c1465..724fdf9 100644 --- a/Assets/Shaders/QuakeForwardPass.hlsl +++ b/Assets/Shaders/QuakeForwardPass.hlsl @@ -111,7 +111,7 @@ half4 LitPassFragmentSimple(Varyings input) : SV_Target float2 uv = input.uv.xy; half4 diffuseAlpha = SampleAlbedoAlpha(uv, #if _POINT_SAMPLING - TEXTURE2D_ARGS(_BaseMap, point_repeat_sampler_BaseMap) + TEXTURE2D_ARGS(_BaseMap, point_repeat_sampler) #else TEXTURE2D_ARGS(_BaseMap, sampler_BaseMap) #endif @@ -139,7 +139,7 @@ half4 LitPassFragmentSimple(Varyings input) : SV_Target #ifdef _EMISSION finalColor += SampleEmission(uv, _EmissionColor.rgb, #if _POINT_SAMPLING - TEXTURE2D_ARGS(_EmissionMap, point_repeat_sampler_EmissionMap) + TEXTURE2D_ARGS(_EmissionMap, point_repeat_sampler) #else TEXTURE2D_ARGS(_EmissionMap, sampler_EmissionMap) #endif diff --git a/Assets/Shaders/QuakeInput.hlsl b/Assets/Shaders/QuakeInput.hlsl index bec3fce..f3658ba 100644 --- a/Assets/Shaders/QuakeInput.hlsl +++ b/Assets/Shaders/QuakeInput.hlsl @@ -27,8 +27,7 @@ CBUFFER_END #define _Surface UNITY_ACCESS_DOTS_INSTANCED_PROP_FROM_MACRO(float , Metadata__Surface) #endif - SAMPLER(point_repeat_sampler_BaseMap); - SAMPLER(point_repeat_sampler_EmissionMap); + SAMPLER(point_repeat_sampler); TEXTURE2D(_LightMap); SAMPLER(sampler_LightMap); inline void InitializeSimpleLitSurfaceData(float2 uv, out SurfaceData outSurfaceData) From 8fc85d5ecb08f886005c7e988cd1f1e3dcf8e421 Mon Sep 17 00:00:00 2001 From: Nico de Poel Date: Wed, 28 Jul 2021 19:18:48 +0200 Subject: [PATCH 11/11] Reworked brush game object creation so that entity brush models are configured differently from world brush models. Rearranged the VisualStyle class to fit this strategy. --- Assets/Scripts/Game/Entity.cs | 2 +- Assets/Scripts/Game/GameState.cs | 52 ++++++++++++++++++++++++-------- Assets/Scripts/VisualStyle.cs | 42 +++++++++++++++----------- 3 files changed, 65 insertions(+), 31 deletions(-) diff --git a/Assets/Scripts/Game/Entity.cs b/Assets/Scripts/Game/Entity.cs index 3b3d06e..f1c60dd 100644 --- a/Assets/Scripts/Game/Entity.cs +++ b/Assets/Scripts/Game/Entity.cs @@ -151,7 +151,7 @@ public class Entity private void AssignMeshRenderer() { - material = visualStyle.CreateEntityMaterial(); + material = visualStyle.CreateEntityMaterial(true); if (aliasModel.IsAnimated) { diff --git a/Assets/Scripts/Game/GameState.cs b/Assets/Scripts/Game/GameState.cs index 64225e4..7baea81 100644 --- a/Assets/Scripts/Game/GameState.cs +++ b/Assets/Scripts/Game/GameState.cs @@ -31,11 +31,11 @@ public class GameState // The first sub-model contains all of the static geometry var subModel = worldModel.GetSubModel(0); - var subModelGO = CreateBrushGameObject(subModel); + var subModelGO = CreateWorldBrushObject(subModel); subModelGO.transform.SetParent(worldGameObject.transform); } - private GameObject CreateBrushGameObject(BrushModel.SubModel subModel) + private GameObject CreateWorldBrushObject(BrushModel.SubModel subModel) { var subModelGO = new GameObject(subModel.Name) { layer = (int)uq.GameLayer }; @@ -44,13 +44,12 @@ public class GameState var meshGO = new GameObject(surfaceMesh.Mesh.name) { layer = (int)uq.GameLayer }; meshGO.transform.SetParent(subModelGO.transform); - var mf = meshGO.AddComponent(); - var mr = meshGO.AddComponent(); - mf.sharedMesh = surfaceMesh.Mesh; - - // TODO FIXME This is wrong for brush model entities - uq.CurrentStyle.SetupWorldRenderer(mr); - mr.material = uq.CurrentStyle.CreateWorldMaterial(surfaceMesh.Flags); // TODO FIXME this currently leaks Materials + var meshFilter = meshGO.AddComponent(); + meshFilter.sharedMesh = surfaceMesh.Mesh; + + var meshRenderer = meshGO.AddComponent(); + uq.CurrentStyle.SetupWorldRenderer(meshRenderer); + meshRenderer.material = uq.CurrentStyle.CreateWorldMaterial(surfaceMesh.Flags); // TODO FIXME this currently leaks Materials uint texNum = surfaceMesh.TextureNum; if (uq.GameAssets.TryGetTexture(texNum, out var texture)) @@ -59,7 +58,36 @@ public class GameState uq.GameAssets.TryGetTexture(fbNum, out var fullBright); uq.GameAssets.TryGetLightmap(surfaceMesh.Lightmap, out var lightmap); - uq.CurrentStyle.SetWorldTextures(mr.material, texture, fullBright, lightmap); + uq.CurrentStyle.SetWorldTextures(meshRenderer.material, texture, fullBright, lightmap); + } + } + + return subModelGO; + } + + private GameObject CreateEntityBrushObject(BrushModel.SubModel subModel) + { + var subModelGO = new GameObject(subModel.Name) { layer = (int)uq.GameLayer }; + + foreach (var surfaceMesh in subModel.SurfaceMeshes) + { + var meshGO = new GameObject(surfaceMesh.Mesh.name) { layer = (int)uq.GameLayer }; + meshGO.transform.SetParent(subModelGO.transform); + + var meshFilter = meshGO.AddComponent(); + meshFilter.sharedMesh = surfaceMesh.Mesh; + + var meshRenderer = meshGO.AddComponent(); + uq.CurrentStyle.SetupEntityRenderer(meshRenderer); + meshRenderer.material = uq.CurrentStyle.CreateEntityMaterial(false); // TODO FIXME this currently leaks Materials + + uint texNum = surfaceMesh.TextureNum; + if (uq.GameAssets.TryGetTexture(texNum, out var texture)) + { + uint fbNum = surfaceMesh.FullBrightNum; + uq.GameAssets.TryGetTexture(fbNum, out var fullBright); + + uq.CurrentStyle.SetEntityTextures(meshRenderer.material, texture, fullBright); } } @@ -112,7 +140,7 @@ public class GameState var brushModelGO = new GameObject(brushModel.Name) { layer = (int)Layers.Game1 }; for (int i = 0; i < brushModel.SubModelCount; ++i) { - var subModelGO = CreateBrushGameObject(brushModel.GetSubModel(i)); + var subModelGO = CreateEntityBrushObject(brushModel.GetSubModel(i)); subModelGO.transform.SetParent(brushModelGO.transform); } @@ -132,7 +160,7 @@ public class GameState // TODO: these relatively complex world game objects are going to get destroyed and re-created all the time // as the player moves through the map and moves in and out of range of these entities. This can and should // be done more efficiently by creating the game objects only once and enabling/disabling them on demand. - var worldModelGO = CreateBrushGameObject(subModel); + var worldModelGO = CreateWorldBrushObject(subModel); entity.SetWorldModel(worldModelGO); } diff --git a/Assets/Scripts/VisualStyle.cs b/Assets/Scripts/VisualStyle.cs index 88422b5..a4c7194 100644 --- a/Assets/Scripts/VisualStyle.cs +++ b/Assets/Scripts/VisualStyle.cs @@ -36,13 +36,22 @@ public class VisualStyle : ScriptableObject Shader.DisableKeyword("_POINT_SAMPLING"); } - public virtual Material CreateEntityMaterial() + public virtual Material CreateEntityMaterial(bool aliasModel) { - return new Material(entityMaterial); + var material = new Material(entityMaterial); + + if (aliasModel && affineTexturing) + material.EnableKeyword("_AFFINE_ON"); + else + material.DisableKeyword("_AFFINE_ON"); + + return material; } public virtual Material CreateWorldMaterial(QSurfaceFlags surfaceFlags) { + Material material; + if (surfaceFlags.HasFlag(QSurfaceFlags.DrawTurbulence) && liquidMaterial != null) { float alpha = 1f; @@ -55,14 +64,20 @@ public class VisualStyle : ScriptableObject else if (surfaceFlags.HasFlag(QSurfaceFlags.DrawTeleporter)) alpha = liquidProperties.teleAlpha; - var material = new Material(liquidMaterial); + material = new Material(liquidMaterial); material.SetColor("_BaseColor", new Color(1, 1, 1, alpha)); - return material; + } + else + { + material = new Material(worldMaterial); } - // TODO: enable alpha test for DrawFence flag - - return new Material(worldMaterial); + if (surfaceFlags.HasFlag(QSurfaceFlags.DrawFence)) + { + material.EnableKeyword("_ALPHATEST_ON"); + } + + return material; } public virtual void SetupEntityRenderer(MeshRenderer meshRenderer) @@ -100,11 +115,6 @@ public class VisualStyle : ScriptableObject material.EnableKeyword("_EMISSION"); else material.DisableKeyword("_EMISSION"); - - if (affineTexturing) - material.EnableKeyword("_AFFINE_ON"); - else - material.DisableKeyword("_AFFINE_ON"); } public virtual void SetWorldTextures(Material material, Texture2D mainTexture, Texture2D fullBright, Texture2D lightmap) @@ -119,19 +129,15 @@ public class VisualStyle : ScriptableObject else material.DisableKeyword("_EMISSION"); + material.SetTexture("_LightMap", lightmap); + if (lightmap != null) - { - material.SetTexture("_LightMap", lightmap); material.EnableKeyword("_QLIGHTMAP_ON"); - } else - { material.DisableKeyword("_QLIGHTMAP_ON"); - } } } -// TODO: should probably just use the cvars for this (r_wateralpha and such) [System.Serializable] public class LiquidProperties {