feature: Added psxsplash installer, added basic BSP implementation (non-functional)
This commit is contained in:
872
Debug.unity
Normal file
872
Debug.unity
Normal file
@@ -0,0 +1,872 @@
|
||||
%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: 10
|
||||
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: 0}
|
||||
m_UseRadianceAmbientProbe: 0
|
||||
--- !u!157 &3
|
||||
LightmapSettings:
|
||||
m_ObjectHideFlags: 0
|
||||
serializedVersion: 13
|
||||
m_BakeOnSceneLoad: 0
|
||||
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_ReflectionCompression: 2
|
||||
m_MixedBakeMode: 2
|
||||
m_BakeBackend: 1
|
||||
m_PVRSampling: 1
|
||||
m_PVRDirectSampleCount: 32
|
||||
m_PVRSampleCount: 512
|
||||
m_PVRBounces: 2
|
||||
m_PVREnvironmentSampleCount: 256
|
||||
m_PVREnvironmentReferencePointCount: 2048
|
||||
m_PVRFilteringMode: 1
|
||||
m_PVRDenoiserTypeDirect: 1
|
||||
m_PVRDenoiserTypeIndirect: 1
|
||||
m_PVRDenoiserTypeAO: 1
|
||||
m_PVRFilterTypeDirect: 0
|
||||
m_PVRFilterTypeIndirect: 0
|
||||
m_PVRFilterTypeAO: 0
|
||||
m_PVREnvironmentMIS: 1
|
||||
m_PVRCulling: 1
|
||||
m_PVRFilteringGaussRadiusDirect: 1
|
||||
m_PVRFilteringGaussRadiusIndirect: 1
|
||||
m_PVRFilteringGaussRadiusAO: 1
|
||||
m_PVRFilteringAtrousPositionSigmaDirect: 0.5
|
||||
m_PVRFilteringAtrousPositionSigmaIndirect: 2
|
||||
m_PVRFilteringAtrousPositionSigmaAO: 1
|
||||
m_ExportTrainingData: 0
|
||||
m_TrainingDataDestination: TrainingData
|
||||
m_LightProbeSampleCountMultiplier: 4
|
||||
m_LightingDataAsset: {fileID: 20201, guid: 0000000000000000f000000000000000, type: 0}
|
||||
m_LightingSettings: {fileID: 0}
|
||||
--- !u!196 &4
|
||||
NavMeshSettings:
|
||||
serializedVersion: 2
|
||||
m_ObjectHideFlags: 0
|
||||
m_BuildSettings:
|
||||
serializedVersion: 3
|
||||
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
|
||||
buildHeightMesh: 0
|
||||
maxJobWorkers: 0
|
||||
preserveTilesOutsideBounds: 0
|
||||
debug:
|
||||
m_Flags: 0
|
||||
m_NavMeshData: {fileID: 0}
|
||||
--- !u!1 &283344192
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 283344197}
|
||||
- component: {fileID: 283344196}
|
||||
- component: {fileID: 283344195}
|
||||
- component: {fileID: 283344194}
|
||||
- component: {fileID: 283344193}
|
||||
m_Layer: 0
|
||||
m_Name: f (1)
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!114 &283344193
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 283344192}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: bea0f31a495202580ac77bd9fd6e99f2, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
IsActive: 1
|
||||
bitDepth: 8
|
||||
luaFile: {fileID: 0}
|
||||
previewNormals: 0
|
||||
normalPreviewLength: 0.5
|
||||
--- !u!65 &283344194
|
||||
BoxCollider:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 283344192}
|
||||
m_Material: {fileID: 0}
|
||||
m_IncludeLayers:
|
||||
serializedVersion: 2
|
||||
m_Bits: 0
|
||||
m_ExcludeLayers:
|
||||
serializedVersion: 2
|
||||
m_Bits: 0
|
||||
m_LayerOverridePriority: 0
|
||||
m_IsTrigger: 0
|
||||
m_ProvidesContacts: 0
|
||||
m_Enabled: 1
|
||||
serializedVersion: 3
|
||||
m_Size: {x: 1, y: 1, z: 1}
|
||||
m_Center: {x: 0, y: 0, z: 0}
|
||||
--- !u!23 &283344195
|
||||
MeshRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 283344192}
|
||||
m_Enabled: 1
|
||||
m_CastShadows: 1
|
||||
m_ReceiveShadows: 1
|
||||
m_DynamicOccludee: 1
|
||||
m_StaticShadowCaster: 0
|
||||
m_MotionVectors: 1
|
||||
m_LightProbeUsage: 1
|
||||
m_ReflectionProbeUsage: 1
|
||||
m_RayTracingMode: 2
|
||||
m_RayTraceProcedural: 0
|
||||
m_RayTracingAccelStructBuildFlagsOverride: 0
|
||||
m_RayTracingAccelStructBuildFlags: 1
|
||||
m_SmallMeshCulling: 1
|
||||
m_RenderingLayerMask: 1
|
||||
m_RendererPriority: 0
|
||||
m_Materials:
|
||||
- {fileID: 2100000, guid: 31321ba15b8f8eb4c954353edc038b1d, type: 2}
|
||||
m_StaticBatchInfo:
|
||||
firstSubMesh: 0
|
||||
subMeshCount: 0
|
||||
m_StaticBatchRoot: {fileID: 0}
|
||||
m_ProbeAnchor: {fileID: 0}
|
||||
m_LightProbeVolumeOverride: {fileID: 0}
|
||||
m_ScaleInLightmap: 1
|
||||
m_ReceiveGI: 1
|
||||
m_PreserveUVs: 0
|
||||
m_IgnoreNormalsForChartDetection: 0
|
||||
m_ImportantGI: 0
|
||||
m_StitchLightmapSeams: 1
|
||||
m_SelectedEditorRenderState: 3
|
||||
m_MinimumChartSize: 4
|
||||
m_AutoUVMaxDistance: 0.5
|
||||
m_AutoUVMaxAngle: 89
|
||||
m_LightmapParameters: {fileID: 0}
|
||||
m_SortingLayerID: 0
|
||||
m_SortingLayer: 0
|
||||
m_SortingOrder: 0
|
||||
m_AdditionalVertexStreams: {fileID: 0}
|
||||
--- !u!33 &283344196
|
||||
MeshFilter:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 283344192}
|
||||
m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0}
|
||||
--- !u!4 &283344197
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 283344192}
|
||||
serializedVersion: 2
|
||||
m_LocalRotation: {x: -0.1479161, y: 0.37224695, z: -0.33835596, w: 0.85150945}
|
||||
m_LocalPosition: {x: -30.19757, y: 0, z: 8.341}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 0}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 47.226, z: -43.342}
|
||||
--- !u!1 &1293128684
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 1293128689}
|
||||
- component: {fileID: 1293128688}
|
||||
- component: {fileID: 1293128687}
|
||||
- component: {fileID: 1293128686}
|
||||
- component: {fileID: 1293128685}
|
||||
m_Layer: 0
|
||||
m_Name: f
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!114 &1293128685
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1293128684}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: bea0f31a495202580ac77bd9fd6e99f2, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
IsActive: 1
|
||||
bitDepth: 8
|
||||
luaFile: {fileID: 0}
|
||||
previewNormals: 0
|
||||
normalPreviewLength: 0.5
|
||||
--- !u!65 &1293128686
|
||||
BoxCollider:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1293128684}
|
||||
m_Material: {fileID: 0}
|
||||
m_IncludeLayers:
|
||||
serializedVersion: 2
|
||||
m_Bits: 0
|
||||
m_ExcludeLayers:
|
||||
serializedVersion: 2
|
||||
m_Bits: 0
|
||||
m_LayerOverridePriority: 0
|
||||
m_IsTrigger: 0
|
||||
m_ProvidesContacts: 0
|
||||
m_Enabled: 1
|
||||
serializedVersion: 3
|
||||
m_Size: {x: 1, y: 1, z: 1}
|
||||
m_Center: {x: 0, y: 0, z: 0}
|
||||
--- !u!23 &1293128687
|
||||
MeshRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1293128684}
|
||||
m_Enabled: 1
|
||||
m_CastShadows: 1
|
||||
m_ReceiveShadows: 1
|
||||
m_DynamicOccludee: 1
|
||||
m_StaticShadowCaster: 0
|
||||
m_MotionVectors: 1
|
||||
m_LightProbeUsage: 1
|
||||
m_ReflectionProbeUsage: 1
|
||||
m_RayTracingMode: 2
|
||||
m_RayTraceProcedural: 0
|
||||
m_RayTracingAccelStructBuildFlagsOverride: 0
|
||||
m_RayTracingAccelStructBuildFlags: 1
|
||||
m_SmallMeshCulling: 1
|
||||
m_RenderingLayerMask: 1
|
||||
m_RendererPriority: 0
|
||||
m_Materials:
|
||||
- {fileID: 2100000, guid: 31321ba15b8f8eb4c954353edc038b1d, type: 2}
|
||||
m_StaticBatchInfo:
|
||||
firstSubMesh: 0
|
||||
subMeshCount: 0
|
||||
m_StaticBatchRoot: {fileID: 0}
|
||||
m_ProbeAnchor: {fileID: 0}
|
||||
m_LightProbeVolumeOverride: {fileID: 0}
|
||||
m_ScaleInLightmap: 1
|
||||
m_ReceiveGI: 1
|
||||
m_PreserveUVs: 0
|
||||
m_IgnoreNormalsForChartDetection: 0
|
||||
m_ImportantGI: 0
|
||||
m_StitchLightmapSeams: 1
|
||||
m_SelectedEditorRenderState: 3
|
||||
m_MinimumChartSize: 4
|
||||
m_AutoUVMaxDistance: 0.5
|
||||
m_AutoUVMaxAngle: 89
|
||||
m_LightmapParameters: {fileID: 0}
|
||||
m_SortingLayerID: 0
|
||||
m_SortingLayer: 0
|
||||
m_SortingOrder: 0
|
||||
m_AdditionalVertexStreams: {fileID: 0}
|
||||
--- !u!33 &1293128688
|
||||
MeshFilter:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1293128684}
|
||||
m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0}
|
||||
--- !u!4 &1293128689
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1293128684}
|
||||
serializedVersion: 2
|
||||
m_LocalRotation: {x: -0.13355339, y: 0.35044792, z: -0.3301185, w: 0.8662399}
|
||||
m_LocalPosition: {x: -30.19757, y: 0, z: 10.84006}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 0}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 44.053, z: -41.723}
|
||||
--- !u!1 &1331845601
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 1331845603}
|
||||
- component: {fileID: 1331845602}
|
||||
m_Layer: 0
|
||||
m_Name: Exp
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!114 &1331845602
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1331845601}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: ab5195ad94fd173cfb6d48ee06eaf245, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
GTEScaling: 100
|
||||
SceneLuaFile: {fileID: 0}
|
||||
BSPPreviewDepth: 2
|
||||
--- !u!4 &1331845603
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1331845601}
|
||||
serializedVersion: 2
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: -31.5414, y: 0, z: 10.89769}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 0}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
--- !u!1 &1337453585
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 1337453587}
|
||||
- component: {fileID: 1337453586}
|
||||
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 &1337453586
|
||||
Light:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1337453585}
|
||||
m_Enabled: 1
|
||||
serializedVersion: 11
|
||||
m_Type: 1
|
||||
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: 4
|
||||
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_ForceVisible: 0
|
||||
m_ShadowRadius: 0
|
||||
m_ShadowAngle: 0
|
||||
m_LightUnit: 1
|
||||
m_LuxAtDistance: 1
|
||||
m_EnableSpotReflector: 1
|
||||
--- !u!4 &1337453587
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1337453585}
|
||||
serializedVersion: 2
|
||||
m_LocalRotation: {x: 0.40821788, y: -0.23456968, z: 0.10938163, w: 0.8754261}
|
||||
m_LocalPosition: {x: 0, y: 3, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 0}
|
||||
m_LocalEulerAnglesHint: {x: 50, y: -30, z: 0}
|
||||
--- !u!1 &1388445967
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 1388445970}
|
||||
- component: {fileID: 1388445969}
|
||||
- component: {fileID: 1388445968}
|
||||
m_Layer: 0
|
||||
m_Name: Main Camera
|
||||
m_TagString: MainCamera
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!81 &1388445968
|
||||
AudioListener:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1388445967}
|
||||
m_Enabled: 1
|
||||
--- !u!20 &1388445969
|
||||
Camera:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1388445967}
|
||||
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_Iso: 200
|
||||
m_ShutterSpeed: 0.005
|
||||
m_Aperture: 16
|
||||
m_FocusDistance: 10
|
||||
m_FocalLength: 50
|
||||
m_BladeCount: 5
|
||||
m_Curvature: {x: 2, y: 11}
|
||||
m_BarrelClipping: 0.25
|
||||
m_Anamorphism: 0
|
||||
m_SensorSize: {x: 36, y: 24}
|
||||
m_LensShift: {x: 0, y: 0}
|
||||
m_NormalizedViewPortRect:
|
||||
serializedVersion: 2
|
||||
x: 0
|
||||
y: 0
|
||||
width: 1
|
||||
height: 1
|
||||
near clip plane: 0.3
|
||||
far clip plane: 1000
|
||||
field of view: 60
|
||||
orthographic: 0
|
||||
orthographic size: 5
|
||||
m_Depth: -1
|
||||
m_CullingMask:
|
||||
serializedVersion: 2
|
||||
m_Bits: 4294967295
|
||||
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 &1388445970
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1388445967}
|
||||
serializedVersion: 2
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 1, z: -10}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 0}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
--- !u!1 &1392840323
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 1392840328}
|
||||
- component: {fileID: 1392840327}
|
||||
- component: {fileID: 1392840326}
|
||||
- component: {fileID: 1392840325}
|
||||
- component: {fileID: 1392840324}
|
||||
m_Layer: 0
|
||||
m_Name: f (2)
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!114 &1392840324
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1392840323}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: bea0f31a495202580ac77bd9fd6e99f2, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
IsActive: 1
|
||||
bitDepth: 8
|
||||
luaFile: {fileID: 0}
|
||||
previewNormals: 0
|
||||
normalPreviewLength: 0.5
|
||||
--- !u!65 &1392840325
|
||||
BoxCollider:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1392840323}
|
||||
m_Material: {fileID: 0}
|
||||
m_IncludeLayers:
|
||||
serializedVersion: 2
|
||||
m_Bits: 0
|
||||
m_ExcludeLayers:
|
||||
serializedVersion: 2
|
||||
m_Bits: 0
|
||||
m_LayerOverridePriority: 0
|
||||
m_IsTrigger: 0
|
||||
m_ProvidesContacts: 0
|
||||
m_Enabled: 1
|
||||
serializedVersion: 3
|
||||
m_Size: {x: 1, y: 1, z: 1}
|
||||
m_Center: {x: 0, y: 0, z: 0}
|
||||
--- !u!23 &1392840326
|
||||
MeshRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1392840323}
|
||||
m_Enabled: 1
|
||||
m_CastShadows: 1
|
||||
m_ReceiveShadows: 1
|
||||
m_DynamicOccludee: 1
|
||||
m_StaticShadowCaster: 0
|
||||
m_MotionVectors: 1
|
||||
m_LightProbeUsage: 1
|
||||
m_ReflectionProbeUsage: 1
|
||||
m_RayTracingMode: 2
|
||||
m_RayTraceProcedural: 0
|
||||
m_RayTracingAccelStructBuildFlagsOverride: 0
|
||||
m_RayTracingAccelStructBuildFlags: 1
|
||||
m_SmallMeshCulling: 1
|
||||
m_RenderingLayerMask: 1
|
||||
m_RendererPriority: 0
|
||||
m_Materials:
|
||||
- {fileID: 2100000, guid: 31321ba15b8f8eb4c954353edc038b1d, type: 2}
|
||||
m_StaticBatchInfo:
|
||||
firstSubMesh: 0
|
||||
subMeshCount: 0
|
||||
m_StaticBatchRoot: {fileID: 0}
|
||||
m_ProbeAnchor: {fileID: 0}
|
||||
m_LightProbeVolumeOverride: {fileID: 0}
|
||||
m_ScaleInLightmap: 1
|
||||
m_ReceiveGI: 1
|
||||
m_PreserveUVs: 0
|
||||
m_IgnoreNormalsForChartDetection: 0
|
||||
m_ImportantGI: 0
|
||||
m_StitchLightmapSeams: 1
|
||||
m_SelectedEditorRenderState: 3
|
||||
m_MinimumChartSize: 4
|
||||
m_AutoUVMaxDistance: 0.5
|
||||
m_AutoUVMaxAngle: 89
|
||||
m_LightmapParameters: {fileID: 0}
|
||||
m_SortingLayerID: 0
|
||||
m_SortingLayer: 0
|
||||
m_SortingOrder: 0
|
||||
m_AdditionalVertexStreams: {fileID: 0}
|
||||
--- !u!33 &1392840327
|
||||
MeshFilter:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1392840323}
|
||||
m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0}
|
||||
--- !u!4 &1392840328
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1392840323}
|
||||
serializedVersion: 2
|
||||
m_LocalRotation: {x: -0.409346, y: -0.7736037, z: 0.22623056, w: 0.42754278}
|
||||
m_LocalPosition: {x: -30.19757, y: 2.041, z: 10.84006}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 0}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: -122.144, z: 55.77}
|
||||
--- !u!1 &1513869424
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 1513869429}
|
||||
- component: {fileID: 1513869428}
|
||||
- component: {fileID: 1513869427}
|
||||
- component: {fileID: 1513869426}
|
||||
- component: {fileID: 1513869425}
|
||||
m_Layer: 0
|
||||
m_Name: f (3)
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!114 &1513869425
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1513869424}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: bea0f31a495202580ac77bd9fd6e99f2, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
IsActive: 1
|
||||
bitDepth: 8
|
||||
luaFile: {fileID: 0}
|
||||
previewNormals: 0
|
||||
normalPreviewLength: 0.5
|
||||
--- !u!65 &1513869426
|
||||
BoxCollider:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1513869424}
|
||||
m_Material: {fileID: 0}
|
||||
m_IncludeLayers:
|
||||
serializedVersion: 2
|
||||
m_Bits: 0
|
||||
m_ExcludeLayers:
|
||||
serializedVersion: 2
|
||||
m_Bits: 0
|
||||
m_LayerOverridePriority: 0
|
||||
m_IsTrigger: 0
|
||||
m_ProvidesContacts: 0
|
||||
m_Enabled: 1
|
||||
serializedVersion: 3
|
||||
m_Size: {x: 1, y: 1, z: 1}
|
||||
m_Center: {x: 0, y: 0, z: 0}
|
||||
--- !u!23 &1513869427
|
||||
MeshRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1513869424}
|
||||
m_Enabled: 1
|
||||
m_CastShadows: 1
|
||||
m_ReceiveShadows: 1
|
||||
m_DynamicOccludee: 1
|
||||
m_StaticShadowCaster: 0
|
||||
m_MotionVectors: 1
|
||||
m_LightProbeUsage: 1
|
||||
m_ReflectionProbeUsage: 1
|
||||
m_RayTracingMode: 2
|
||||
m_RayTraceProcedural: 0
|
||||
m_RayTracingAccelStructBuildFlagsOverride: 0
|
||||
m_RayTracingAccelStructBuildFlags: 1
|
||||
m_SmallMeshCulling: 1
|
||||
m_RenderingLayerMask: 1
|
||||
m_RendererPriority: 0
|
||||
m_Materials:
|
||||
- {fileID: 2100000, guid: 31321ba15b8f8eb4c954353edc038b1d, type: 2}
|
||||
m_StaticBatchInfo:
|
||||
firstSubMesh: 0
|
||||
subMeshCount: 0
|
||||
m_StaticBatchRoot: {fileID: 0}
|
||||
m_ProbeAnchor: {fileID: 0}
|
||||
m_LightProbeVolumeOverride: {fileID: 0}
|
||||
m_ScaleInLightmap: 1
|
||||
m_ReceiveGI: 1
|
||||
m_PreserveUVs: 0
|
||||
m_IgnoreNormalsForChartDetection: 0
|
||||
m_ImportantGI: 0
|
||||
m_StitchLightmapSeams: 1
|
||||
m_SelectedEditorRenderState: 3
|
||||
m_MinimumChartSize: 4
|
||||
m_AutoUVMaxDistance: 0.5
|
||||
m_AutoUVMaxAngle: 89
|
||||
m_LightmapParameters: {fileID: 0}
|
||||
m_SortingLayerID: 0
|
||||
m_SortingLayer: 0
|
||||
m_SortingOrder: 0
|
||||
m_AdditionalVertexStreams: {fileID: 0}
|
||||
--- !u!33 &1513869428
|
||||
MeshFilter:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1513869424}
|
||||
m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0}
|
||||
--- !u!4 &1513869429
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1513869424}
|
||||
serializedVersion: 2
|
||||
m_LocalRotation: {x: -0.09615398, y: 0.2551484, z: 0.3392829, w: 0.9003004}
|
||||
m_LocalPosition: {x: -27.156, y: 0, z: 9.065}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 0}
|
||||
m_LocalEulerAnglesHint: {x: -20.259, y: 24.845, z: 36.791}
|
||||
--- !u!1660057539 &9223372036854775807
|
||||
SceneRoots:
|
||||
m_ObjectHideFlags: 0
|
||||
m_Roots:
|
||||
- {fileID: 1388445970}
|
||||
- {fileID: 1337453587}
|
||||
- {fileID: 1293128689}
|
||||
- {fileID: 283344197}
|
||||
- {fileID: 1392840328}
|
||||
- {fileID: 1513869429}
|
||||
- {fileID: 1331845603}
|
||||
7
Debug.unity.meta
Normal file
7
Debug.unity.meta
Normal file
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 14e1ffa687155c145bfed30e28f26bc1
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -2,214 +2,521 @@ using UnityEngine;
|
||||
using UnityEditor;
|
||||
using System.Collections.Generic;
|
||||
using SplashEdit.RuntimeCode;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
public class InstallerWindow : EditorWindow
|
||||
|
||||
namespace SplashEdit.EditorCode
|
||||
{
|
||||
// Cached status for MIPS toolchain binaries.
|
||||
private Dictionary<string, bool> mipsToolStatus = new Dictionary<string, bool>();
|
||||
|
||||
// Cached status for optional tools.
|
||||
private bool makeInstalled;
|
||||
private bool gdbInstalled;
|
||||
private string pcsxReduxPath;
|
||||
|
||||
private bool isInstalling = false;
|
||||
|
||||
[MenuItem("PSX/Toolchain & Build Tools Installer")]
|
||||
public static void ShowWindow()
|
||||
public class InstallerWindow : EditorWindow
|
||||
{
|
||||
InstallerWindow window = GetWindow<InstallerWindow>("Toolchain Installer");
|
||||
window.RefreshToolStatus();
|
||||
window.pcsxReduxPath = DataStorage.LoadData().PCSXReduxPath;
|
||||
}
|
||||
// Cached status for MIPS toolchain binaries.
|
||||
private Dictionary<string, bool> mipsToolStatus = new Dictionary<string, bool>();
|
||||
|
||||
/// <summary>
|
||||
/// Refresh the cached statuses for all tools.
|
||||
/// </summary>
|
||||
private void RefreshToolStatus()
|
||||
{
|
||||
mipsToolStatus.Clear();
|
||||
foreach (var tool in ToolchainChecker.GetRequiredTools())
|
||||
// Cached status for optional tools.
|
||||
private bool makeInstalled;
|
||||
private bool gdbInstalled;
|
||||
private string pcsxReduxPath;
|
||||
|
||||
// PSXSplash related variables
|
||||
private bool psxsplashInstalled = false;
|
||||
private bool psxsplashInstalling = false;
|
||||
private bool psxsplashFetching = false;
|
||||
private string selectedVersion = "main";
|
||||
private Dictionary<string, string> availableBranches = new Dictionary<string, string>();
|
||||
private List<string> availableReleases = new List<string>();
|
||||
private bool showBranches = true;
|
||||
private bool showReleases = false;
|
||||
private Vector2 scrollPosition;
|
||||
private Vector2 versionScrollPosition;
|
||||
|
||||
private bool isInstalling = false;
|
||||
|
||||
[MenuItem("PSX/Toolchain & Build Tools Installer")]
|
||||
public static void ShowWindow()
|
||||
{
|
||||
mipsToolStatus[tool] = ToolchainChecker.IsToolAvailable(tool);
|
||||
InstallerWindow window = GetWindow<InstallerWindow>("Toolchain Installer");
|
||||
window.RefreshToolStatus();
|
||||
window.pcsxReduxPath = DataStorage.LoadData().PCSXReduxPath;
|
||||
window.CheckPSXSplashInstallation();
|
||||
}
|
||||
|
||||
makeInstalled = ToolchainChecker.IsToolAvailable("make");
|
||||
gdbInstalled = ToolchainChecker.IsToolAvailable("gdb-multiarch");
|
||||
}
|
||||
|
||||
private void OnGUI()
|
||||
{
|
||||
GUILayout.Label("Toolchain & Build Tools Installer", EditorStyles.boldLabel);
|
||||
GUILayout.Space(5);
|
||||
|
||||
if (GUILayout.Button("Refresh Status"))
|
||||
/// <summary>
|
||||
/// Refresh the cached statuses for all tools.
|
||||
/// </summary>
|
||||
private void RefreshToolStatus()
|
||||
{
|
||||
RefreshToolStatus();
|
||||
}
|
||||
GUILayout.Space(10);
|
||||
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
DrawToolchainColumn();
|
||||
DrawAdditionalToolsColumn();
|
||||
EditorGUILayout.EndHorizontal();
|
||||
}
|
||||
|
||||
private void DrawToolchainColumn()
|
||||
{
|
||||
EditorGUILayout.BeginVertical("box", GUILayout.MaxWidth(position.width / 2 - 10));
|
||||
GUILayout.Label("MIPS Toolchain", EditorStyles.boldLabel);
|
||||
GUILayout.Space(5);
|
||||
|
||||
// Display cached status for each required MIPS tool.
|
||||
foreach (var kvp in mipsToolStatus)
|
||||
{
|
||||
GUI.color = kvp.Value ? Color.green : Color.red;
|
||||
GUILayout.Label($"{kvp.Key}: {(kvp.Value ? "Found" : "Missing")}");
|
||||
}
|
||||
GUI.color = Color.white;
|
||||
GUILayout.Space(5);
|
||||
|
||||
if (GUILayout.Button("Install MIPS Toolchain"))
|
||||
{
|
||||
if (!isInstalling)
|
||||
InstallMipsToolchainAsync();
|
||||
}
|
||||
EditorGUILayout.EndVertical();
|
||||
}
|
||||
|
||||
private void DrawAdditionalToolsColumn()
|
||||
{
|
||||
EditorGUILayout.BeginVertical("box", GUILayout.MaxWidth(position.width / 2 - 10));
|
||||
GUILayout.Label("Optional Tools", EditorStyles.boldLabel);
|
||||
GUILayout.Space(5);
|
||||
|
||||
// GNU Make status (required).
|
||||
GUI.color = makeInstalled ? Color.green : Color.red;
|
||||
GUILayout.Label($"GNU Make: {(makeInstalled ? "Found" : "Missing")} (Required)");
|
||||
GUI.color = Color.white;
|
||||
GUILayout.Space(5);
|
||||
if (GUILayout.Button("Install GNU Make"))
|
||||
{
|
||||
if (!isInstalling)
|
||||
InstallMakeAsync();
|
||||
}
|
||||
|
||||
GUILayout.Space(10);
|
||||
|
||||
// GDB status (optional).
|
||||
GUI.color = gdbInstalled ? Color.green : Color.red;
|
||||
GUILayout.Label($"GDB: {(gdbInstalled ? "Found" : "Missing")} (Optional)");
|
||||
GUI.color = Color.white;
|
||||
GUILayout.Space(5);
|
||||
if (GUILayout.Button("Install GDB"))
|
||||
{
|
||||
if (!isInstalling)
|
||||
InstallGDBAsync();
|
||||
}
|
||||
|
||||
GUILayout.Space(10);
|
||||
|
||||
// PCSX-Redux (manual install)
|
||||
GUI.color = string.IsNullOrEmpty(pcsxReduxPath) ? Color.red : Color.green;
|
||||
GUILayout.Label($"PCSX-Redux: {(string.IsNullOrEmpty(pcsxReduxPath) ? "Not Configured" : "Configured")} (Optional)");
|
||||
GUI.color = Color.white;
|
||||
|
||||
GUILayout.BeginHorizontal();
|
||||
if (GUILayout.Button("Browse for PCSX-Redux"))
|
||||
{
|
||||
string selectedPath = EditorUtility.OpenFilePanel("Select PCSX-Redux Executable", "", "");
|
||||
if (!string.IsNullOrEmpty(selectedPath))
|
||||
mipsToolStatus.Clear();
|
||||
foreach (var tool in ToolchainChecker.GetRequiredTools())
|
||||
{
|
||||
pcsxReduxPath = selectedPath;
|
||||
PSXData data = DataStorage.LoadData();
|
||||
data.PCSXReduxPath = pcsxReduxPath;
|
||||
DataStorage.StoreData(data);
|
||||
mipsToolStatus[tool] = ToolchainChecker.IsToolAvailable(tool);
|
||||
}
|
||||
|
||||
makeInstalled = ToolchainChecker.IsToolAvailable("make");
|
||||
gdbInstalled = ToolchainChecker.IsToolAvailable("gdb-multiarch");
|
||||
}
|
||||
|
||||
private void CheckPSXSplashInstallation()
|
||||
{
|
||||
psxsplashInstalled = PSXSplashInstaller.IsInstalled();
|
||||
|
||||
if (psxsplashInstalled)
|
||||
{
|
||||
FetchPSXSplashVersions();
|
||||
}
|
||||
else
|
||||
{
|
||||
availableBranches = new Dictionary<string, string>();
|
||||
availableReleases = new List<string>();
|
||||
}
|
||||
}
|
||||
if (!string.IsNullOrEmpty(pcsxReduxPath))
|
||||
|
||||
private async void FetchPSXSplashVersions()
|
||||
{
|
||||
if (GUILayout.Button("Clear", GUILayout.Width(60)))
|
||||
if (psxsplashFetching) return;
|
||||
|
||||
psxsplashFetching = true;
|
||||
try
|
||||
{
|
||||
pcsxReduxPath = "";
|
||||
PSXData data = DataStorage.LoadData();
|
||||
data.PCSXReduxPath = pcsxReduxPath;
|
||||
DataStorage.StoreData(data);
|
||||
// Fetch latest from remote
|
||||
await PSXSplashInstaller.FetchLatestAsync();
|
||||
|
||||
// Get all available versions
|
||||
var branchesTask = PSXSplashInstaller.GetBranchesWithLatestCommitsAsync();
|
||||
var releasesTask = PSXSplashInstaller.GetReleasesAsync();
|
||||
|
||||
await Task.WhenAll(branchesTask, releasesTask);
|
||||
|
||||
availableBranches = branchesTask.Result;
|
||||
availableReleases = releasesTask.Result;
|
||||
|
||||
// If no branches were found, add main as default
|
||||
if (!availableBranches.Any())
|
||||
{
|
||||
availableBranches["main"] = "latest";
|
||||
}
|
||||
|
||||
// Select the first branch by default
|
||||
if (availableBranches.Any() && string.IsNullOrEmpty(selectedVersion))
|
||||
{
|
||||
selectedVersion = availableBranches.Keys.First();
|
||||
}
|
||||
|
||||
Repaint();
|
||||
}
|
||||
catch (System.Exception e)
|
||||
{
|
||||
UnityEngine.Debug.LogError($"Failed to fetch PSXSplash versions: {e.Message}");
|
||||
}
|
||||
finally
|
||||
{
|
||||
psxsplashFetching = false;
|
||||
}
|
||||
}
|
||||
GUILayout.EndHorizontal();
|
||||
EditorGUILayout.EndVertical();
|
||||
}
|
||||
|
||||
private async void InstallMipsToolchainAsync()
|
||||
{
|
||||
try
|
||||
private void OnGUI()
|
||||
{
|
||||
isInstalling = true;
|
||||
EditorUtility.DisplayProgressBar("Installing MIPS Toolchain",
|
||||
"Please wait while the MIPS toolchain is being installed...", 0f);
|
||||
bool success = await ToolchainInstaller.InstallToolchain();
|
||||
EditorUtility.ClearProgressBar();
|
||||
if (success)
|
||||
scrollPosition = EditorGUILayout.BeginScrollView(scrollPosition);
|
||||
|
||||
GUILayout.Label("Toolchain & Build Tools Installer", EditorStyles.boldLabel);
|
||||
GUILayout.Space(5);
|
||||
|
||||
if (GUILayout.Button("Refresh Status"))
|
||||
{
|
||||
EditorUtility.DisplayDialog("Installation Complete", "MIPS toolchain installed successfully.", "OK");
|
||||
RefreshToolStatus();
|
||||
CheckPSXSplashInstallation();
|
||||
}
|
||||
RefreshToolStatus(); // Update cached statuses after installation
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
EditorUtility.ClearProgressBar();
|
||||
EditorUtility.DisplayDialog("Installation Failed", $"Error: {ex.Message}", "OK");
|
||||
}
|
||||
finally
|
||||
{
|
||||
isInstalling = false;
|
||||
}
|
||||
}
|
||||
GUILayout.Space(10);
|
||||
|
||||
private async void InstallMakeAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
isInstalling = true;
|
||||
EditorUtility.DisplayProgressBar("Installing GNU Make",
|
||||
"Please wait while GNU Make is being installed...", 0f);
|
||||
await ToolchainInstaller.InstallMake();
|
||||
EditorUtility.ClearProgressBar();
|
||||
EditorUtility.DisplayDialog("Installation Complete", "GNU Make installed successfully.", "OK");
|
||||
RefreshToolStatus();
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
EditorUtility.ClearProgressBar();
|
||||
EditorUtility.DisplayDialog("Installation Failed", $"Error: {ex.Message}", "OK");
|
||||
}
|
||||
finally
|
||||
{
|
||||
isInstalling = false;
|
||||
}
|
||||
}
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
DrawToolchainColumn();
|
||||
DrawAdditionalToolsColumn();
|
||||
DrawPSXSplashColumn();
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
private async void InstallGDBAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
isInstalling = true;
|
||||
EditorUtility.DisplayProgressBar("Installing GDB",
|
||||
"Please wait while GDB is being installed...", 0f);
|
||||
await ToolchainInstaller.InstallGDB();
|
||||
EditorUtility.ClearProgressBar();
|
||||
EditorUtility.DisplayDialog("Installation Complete", "GDB installed successfully.", "OK");
|
||||
RefreshToolStatus();
|
||||
EditorGUILayout.EndScrollView();
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
|
||||
private void DrawToolchainColumn()
|
||||
{
|
||||
EditorUtility.ClearProgressBar();
|
||||
EditorUtility.DisplayDialog("Installation Failed", $"Error: {ex.Message}", "OK");
|
||||
EditorGUILayout.BeginVertical("box", GUILayout.MaxWidth(position.width / 3 - 10));
|
||||
GUILayout.Label("MIPS Toolchain", EditorStyles.boldLabel);
|
||||
GUILayout.Space(5);
|
||||
|
||||
// Display cached status for each required MIPS tool.
|
||||
foreach (var kvp in mipsToolStatus)
|
||||
{
|
||||
GUI.color = kvp.Value ? Color.green : Color.red;
|
||||
GUILayout.Label($"{kvp.Key}: {(kvp.Value ? "Found" : "Missing")}");
|
||||
}
|
||||
GUI.color = Color.white;
|
||||
GUILayout.Space(5);
|
||||
|
||||
if (GUILayout.Button("Install MIPS Toolchain"))
|
||||
{
|
||||
if (!isInstalling)
|
||||
InstallMipsToolchainAsync();
|
||||
}
|
||||
EditorGUILayout.EndVertical();
|
||||
}
|
||||
finally
|
||||
|
||||
private void DrawAdditionalToolsColumn()
|
||||
{
|
||||
isInstalling = false;
|
||||
EditorGUILayout.BeginVertical("box", GUILayout.MaxWidth(position.width / 3 - 10));
|
||||
GUILayout.Label("Optional Tools", EditorStyles.boldLabel);
|
||||
GUILayout.Space(5);
|
||||
|
||||
// GNU Make status (required).
|
||||
GUI.color = makeInstalled ? Color.green : Color.red;
|
||||
GUILayout.Label($"GNU Make: {(makeInstalled ? "Found" : "Missing")} (Required)");
|
||||
GUI.color = Color.white;
|
||||
GUILayout.Space(5);
|
||||
if (GUILayout.Button("Install GNU Make"))
|
||||
{
|
||||
if (!isInstalling)
|
||||
InstallMakeAsync();
|
||||
}
|
||||
|
||||
GUILayout.Space(10);
|
||||
|
||||
// GDB status (optional).
|
||||
GUI.color = gdbInstalled ? Color.green : Color.red;
|
||||
GUILayout.Label($"GDB: {(gdbInstalled ? "Found" : "Missing")} (Optional)");
|
||||
GUI.color = Color.white;
|
||||
GUILayout.Space(5);
|
||||
if (GUILayout.Button("Install GDB"))
|
||||
{
|
||||
if (!isInstalling)
|
||||
InstallGDBAsync();
|
||||
}
|
||||
|
||||
GUILayout.Space(10);
|
||||
|
||||
// PCSX-Redux (manual install)
|
||||
GUI.color = string.IsNullOrEmpty(pcsxReduxPath) ? Color.red : Color.green;
|
||||
GUILayout.Label($"PCSX-Redux: {(string.IsNullOrEmpty(pcsxReduxPath) ? "Not Configured" : "Configured")} (Optional)");
|
||||
GUI.color = Color.white;
|
||||
|
||||
GUILayout.BeginHorizontal();
|
||||
if (GUILayout.Button("Browse for PCSX-Redux"))
|
||||
{
|
||||
string selectedPath = EditorUtility.OpenFilePanel("Select PCSX-Redux Executable", "", "");
|
||||
if (!string.IsNullOrEmpty(selectedPath))
|
||||
{
|
||||
pcsxReduxPath = selectedPath;
|
||||
PSXData data = DataStorage.LoadData();
|
||||
data.PCSXReduxPath = pcsxReduxPath;
|
||||
DataStorage.StoreData(data);
|
||||
}
|
||||
}
|
||||
if (!string.IsNullOrEmpty(pcsxReduxPath))
|
||||
{
|
||||
if (GUILayout.Button("Clear", GUILayout.Width(60)))
|
||||
{
|
||||
pcsxReduxPath = "";
|
||||
PSXData data = DataStorage.LoadData();
|
||||
data.PCSXReduxPath = pcsxReduxPath;
|
||||
DataStorage.StoreData(data);
|
||||
}
|
||||
}
|
||||
GUILayout.EndHorizontal();
|
||||
EditorGUILayout.EndVertical();
|
||||
}
|
||||
|
||||
private void DrawPSXSplashColumn()
|
||||
{
|
||||
EditorGUILayout.BeginVertical("box", GUILayout.MaxWidth(position.width / 3 - 10));
|
||||
GUILayout.Label("PSXSplash", EditorStyles.boldLabel);
|
||||
GUILayout.Space(5);
|
||||
|
||||
// PSXSplash status
|
||||
GUI.color = psxsplashInstalled ? Color.green : Color.red;
|
||||
GUILayout.Label($"PSXSplash: {(psxsplashInstalled ? "Installed" : "Not Installed")}");
|
||||
GUI.color = Color.white;
|
||||
|
||||
if (psxsplashFetching)
|
||||
{
|
||||
GUILayout.Label("Fetching versions...");
|
||||
}
|
||||
else if (!psxsplashInstalled)
|
||||
{
|
||||
GUILayout.Space(5);
|
||||
EditorGUILayout.HelpBox("Git is required to install PSXSplash. Make sure it's installed and available in your PATH.", MessageType.Info);
|
||||
|
||||
// Show version selection even before installation
|
||||
DrawVersionSelection();
|
||||
|
||||
if (GUILayout.Button("Install PSXSplash") && !psxsplashInstalling)
|
||||
{
|
||||
InstallPSXSplashAsync();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
GUILayout.Space(10);
|
||||
|
||||
// Current version
|
||||
EditorGUILayout.LabelField($"Current Version: {selectedVersion}", EditorStyles.boldLabel);
|
||||
|
||||
// Version selection
|
||||
DrawVersionSelection();
|
||||
|
||||
GUILayout.Space(10);
|
||||
|
||||
// Refresh and update buttons
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
if (GUILayout.Button("Refresh Versions"))
|
||||
{
|
||||
FetchPSXSplashVersions();
|
||||
}
|
||||
|
||||
if (GUILayout.Button("Update PSXSplash"))
|
||||
{
|
||||
UpdatePSXSplashAsync();
|
||||
}
|
||||
EditorGUILayout.EndHorizontal();
|
||||
}
|
||||
|
||||
EditorGUILayout.EndVertical();
|
||||
}
|
||||
|
||||
private void DrawVersionSelection()
|
||||
{
|
||||
EditorGUILayout.LabelField("Available Versions:", EditorStyles.boldLabel);
|
||||
|
||||
versionScrollPosition = EditorGUILayout.BeginScrollView(versionScrollPosition, GUILayout.Height(200));
|
||||
|
||||
// Branches (with latest commits)
|
||||
showBranches = EditorGUILayout.Foldout(showBranches, $"Branches ({availableBranches.Count})");
|
||||
if (showBranches && availableBranches.Any())
|
||||
{
|
||||
foreach (var branch in availableBranches)
|
||||
{
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
bool isSelected = selectedVersion == branch.Key;
|
||||
if (GUILayout.Toggle(isSelected, "", GUILayout.Width(20)) && !isSelected)
|
||||
{
|
||||
selectedVersion = branch.Key;
|
||||
if (psxsplashInstalled)
|
||||
{
|
||||
CheckoutPSXSplashVersionAsync(branch.Key);
|
||||
}
|
||||
}
|
||||
GUILayout.Label($"{branch.Key} (Latest: {branch.Value})", EditorStyles.label);
|
||||
EditorGUILayout.EndHorizontal();
|
||||
}
|
||||
}
|
||||
else if (showBranches)
|
||||
{
|
||||
GUILayout.Label("No branches available");
|
||||
}
|
||||
|
||||
// Releases
|
||||
showReleases = EditorGUILayout.Foldout(showReleases, $"Releases ({availableReleases.Count})");
|
||||
if (showReleases && availableReleases.Any())
|
||||
{
|
||||
foreach (var release in availableReleases)
|
||||
{
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
bool isSelected = selectedVersion == release;
|
||||
if (GUILayout.Toggle(isSelected, "", GUILayout.Width(20)) && !isSelected)
|
||||
{
|
||||
selectedVersion = release;
|
||||
if (psxsplashInstalled)
|
||||
{
|
||||
CheckoutPSXSplashVersionAsync(release);
|
||||
}
|
||||
}
|
||||
GUILayout.Label(release, EditorStyles.label);
|
||||
EditorGUILayout.EndHorizontal();
|
||||
}
|
||||
}
|
||||
else if (showReleases)
|
||||
{
|
||||
GUILayout.Label("No releases available");
|
||||
}
|
||||
|
||||
EditorGUILayout.EndScrollView();
|
||||
}
|
||||
|
||||
private async void InstallPSXSplashAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
psxsplashInstalling = true;
|
||||
EditorUtility.DisplayProgressBar("Installing PSXSplash", "Cloning repository...", 0.3f);
|
||||
|
||||
bool success = await PSXSplashInstaller.Install();
|
||||
|
||||
EditorUtility.ClearProgressBar();
|
||||
|
||||
if (success)
|
||||
{
|
||||
EditorUtility.DisplayDialog("Installation Complete", "PSXSplash installed successfully.", "OK");
|
||||
CheckPSXSplashInstallation();
|
||||
|
||||
// Checkout the selected version after installation
|
||||
if (!string.IsNullOrEmpty(selectedVersion))
|
||||
{
|
||||
await CheckoutPSXSplashVersionAsync(selectedVersion);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
EditorUtility.DisplayDialog("Installation Failed",
|
||||
"Failed to install PSXSplash. Make sure Git is installed and available in your PATH.", "OK");
|
||||
}
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
EditorUtility.ClearProgressBar();
|
||||
EditorUtility.DisplayDialog("Installation Failed", $"Error: {ex.Message}", "OK");
|
||||
}
|
||||
finally
|
||||
{
|
||||
psxsplashInstalling = false;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<bool> CheckoutPSXSplashVersionAsync(string version)
|
||||
{
|
||||
try
|
||||
{
|
||||
psxsplashInstalling = true;
|
||||
EditorUtility.DisplayProgressBar("Checking Out Version", $"Switching to {version}...", 0.3f);
|
||||
|
||||
bool success = await PSXSplashInstaller.CheckoutVersionAsync(version);
|
||||
|
||||
EditorUtility.ClearProgressBar();
|
||||
|
||||
if (success)
|
||||
{
|
||||
EditorUtility.DisplayDialog("Checkout Complete", $"Switched to {version} successfully.", "OK");
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
EditorUtility.DisplayDialog("Checkout Failed",
|
||||
$"Failed to switch to {version}.", "OK");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
EditorUtility.ClearProgressBar();
|
||||
EditorUtility.DisplayDialog("Checkout Failed", $"Error: {ex.Message}", "OK");
|
||||
return false;
|
||||
}
|
||||
finally
|
||||
{
|
||||
psxsplashInstalling = false;
|
||||
}
|
||||
}
|
||||
|
||||
private async void UpdatePSXSplashAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
psxsplashInstalling = true;
|
||||
EditorUtility.DisplayProgressBar("Updating PSXSplash", "Pulling latest changes...", 0.3f);
|
||||
|
||||
// Pull the latest changes
|
||||
bool success = await PSXSplashInstaller.CheckoutVersionAsync(selectedVersion);
|
||||
|
||||
EditorUtility.ClearProgressBar();
|
||||
|
||||
if (success)
|
||||
{
|
||||
EditorUtility.DisplayDialog("Update Complete", "PSXSplash updated successfully.", "OK");
|
||||
}
|
||||
else
|
||||
{
|
||||
EditorUtility.DisplayDialog("Update Failed",
|
||||
"Failed to update PSXSplash.", "OK");
|
||||
}
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
EditorUtility.ClearProgressBar();
|
||||
EditorUtility.DisplayDialog("Update Failed", $"Error: {ex.Message}", "OK");
|
||||
}
|
||||
finally
|
||||
{
|
||||
psxsplashInstalling = false;
|
||||
}
|
||||
}
|
||||
|
||||
private async void InstallMipsToolchainAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
isInstalling = true;
|
||||
EditorUtility.DisplayProgressBar("Installing MIPS Toolchain",
|
||||
"Please wait while the MIPS toolchain is being installed...", 0f);
|
||||
bool success = await ToolchainInstaller.InstallToolchain();
|
||||
EditorUtility.ClearProgressBar();
|
||||
if (success)
|
||||
{
|
||||
EditorUtility.DisplayDialog("Installation Complete", "MIPS toolchain installed successfully.", "OK");
|
||||
}
|
||||
RefreshToolStatus(); // Update cached statuses after installation
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
EditorUtility.ClearProgressBar();
|
||||
EditorUtility.DisplayDialog("Installation Failed", $"Error: {ex.Message}", "OK");
|
||||
}
|
||||
finally
|
||||
{
|
||||
isInstalling = false;
|
||||
}
|
||||
}
|
||||
|
||||
private async void InstallMakeAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
isInstalling = true;
|
||||
EditorUtility.DisplayProgressBar("Installing GNU Make",
|
||||
"Please wait while GNU Make is being installed...", 0f);
|
||||
await ToolchainInstaller.InstallMake();
|
||||
EditorUtility.ClearProgressBar();
|
||||
EditorUtility.DisplayDialog("Installation Complete", "GNU Make installed successfully.", "OK");
|
||||
RefreshToolStatus();
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
EditorUtility.ClearProgressBar();
|
||||
EditorUtility.DisplayDialog("Installation Failed", $"Error: {ex.Message}", "OK");
|
||||
}
|
||||
finally
|
||||
{
|
||||
isInstalling = false;
|
||||
}
|
||||
}
|
||||
|
||||
private async void InstallGDBAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
isInstalling = true;
|
||||
EditorUtility.DisplayProgressBar("Installing GDB",
|
||||
"Please wait while GDB is being installed...", 0f);
|
||||
await ToolchainInstaller.InstallGDB();
|
||||
EditorUtility.ClearProgressBar();
|
||||
EditorUtility.DisplayDialog("Installation Complete", "GDB installed successfully.", "OK");
|
||||
RefreshToolStatus();
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
EditorUtility.ClearProgressBar();
|
||||
EditorUtility.DisplayDialog("Installation Failed", $"Error: {ex.Message}", "OK");
|
||||
}
|
||||
finally
|
||||
{
|
||||
isInstalling = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
203
Editor/PSXSplashInstaller.cs
Normal file
203
Editor/PSXSplashInstaller.cs
Normal file
@@ -0,0 +1,203 @@
|
||||
using UnityEngine;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using System;
|
||||
|
||||
namespace SplashEdit.EditorCode
|
||||
{
|
||||
|
||||
public static class PSXSplashInstaller
|
||||
{
|
||||
public static readonly string RepoUrl = "https://github.com/psxsplash/psxsplash.git";
|
||||
public static readonly string InstallPath = "Assets/psxsplash";
|
||||
public static readonly string FullInstallPath;
|
||||
|
||||
static PSXSplashInstaller()
|
||||
{
|
||||
FullInstallPath = Path.Combine(Application.dataPath, "psxsplash");
|
||||
}
|
||||
|
||||
public static bool IsInstalled()
|
||||
{
|
||||
return Directory.Exists(FullInstallPath) &&
|
||||
Directory.EnumerateFileSystemEntries(FullInstallPath).Any();
|
||||
}
|
||||
|
||||
public static async Task<bool> Install()
|
||||
{
|
||||
if (IsInstalled()) return true;
|
||||
|
||||
try
|
||||
{
|
||||
// Create the parent directory if it doesn't exist
|
||||
Directory.CreateDirectory(Application.dataPath);
|
||||
|
||||
// Clone the repository
|
||||
var result = await RunGitCommandAsync($"clone --recursive {RepoUrl} \"{FullInstallPath}\"", Application.dataPath);
|
||||
return !result.Contains("error");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
UnityEngine.Debug.LogError($"Failed to install PSXSplash: {e.Message}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task<Dictionary<string, string>> GetBranchesWithLatestCommitsAsync()
|
||||
{
|
||||
if (!IsInstalled()) return new Dictionary<string, string>();
|
||||
|
||||
try
|
||||
{
|
||||
// Fetch all branches and tags
|
||||
await RunGitCommandAsync("fetch --all", FullInstallPath);
|
||||
|
||||
// Get all remote branches
|
||||
var branchesOutput = await RunGitCommandAsync("branch -r", FullInstallPath);
|
||||
var branches = branchesOutput.Split('\n')
|
||||
.Where(b => !string.IsNullOrEmpty(b.Trim()))
|
||||
.Select(b => b.Trim().Replace("origin/", ""))
|
||||
.Where(b => !b.Contains("HEAD"))
|
||||
.ToList();
|
||||
|
||||
var branchesWithCommits = new Dictionary<string, string>();
|
||||
|
||||
// Get the latest commit for each branch
|
||||
foreach (var branch in branches)
|
||||
{
|
||||
var commitOutput = await RunGitCommandAsync($"log origin/{branch} -1 --pretty=format:%h", FullInstallPath);
|
||||
if (!string.IsNullOrEmpty(commitOutput))
|
||||
{
|
||||
branchesWithCommits[branch] = commitOutput.Trim();
|
||||
}
|
||||
}
|
||||
|
||||
return branchesWithCommits;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
UnityEngine.Debug.LogError($"Failed to get branches: {e.Message}");
|
||||
return new Dictionary<string, string>();
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task<List<string>> GetReleasesAsync()
|
||||
{
|
||||
if (!IsInstalled()) return new List<string>();
|
||||
|
||||
try
|
||||
{
|
||||
await RunGitCommandAsync("fetch --tags", FullInstallPath);
|
||||
var output = await RunGitCommandAsync("tag -l", FullInstallPath);
|
||||
|
||||
return output.Split('\n')
|
||||
.Where(t => !string.IsNullOrEmpty(t.Trim()))
|
||||
.Select(t => t.Trim())
|
||||
.ToList();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
UnityEngine.Debug.LogError($"Failed to get releases: {e.Message}");
|
||||
return new List<string>();
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task<bool> CheckoutVersionAsync(string version)
|
||||
{
|
||||
if (!IsInstalled()) return false;
|
||||
|
||||
try
|
||||
{
|
||||
// If it's a branch name, checkout the branch
|
||||
// If it's a commit hash, checkout the commit
|
||||
var result = await RunGitCommandAsync($"checkout {version}", FullInstallPath);
|
||||
var result2 = await RunGitCommandAsync("submodule update --init --recursive", FullInstallPath);
|
||||
|
||||
return !result.Contains("error") && !result2.Contains("error");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
UnityEngine.Debug.LogError($"Failed to checkout version: {e.Message}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task<bool> FetchLatestAsync()
|
||||
{
|
||||
if (!IsInstalled()) return false;
|
||||
|
||||
try
|
||||
{
|
||||
var result = await RunGitCommandAsync("fetch --all", FullInstallPath);
|
||||
return !result.Contains("error");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
UnityEngine.Debug.LogError($"Failed to fetch latest: {e.Message}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task<string> RunGitCommandAsync(string arguments, string workingDirectory)
|
||||
{
|
||||
var processInfo = new ProcessStartInfo
|
||||
{
|
||||
FileName = "git",
|
||||
Arguments = arguments,
|
||||
WorkingDirectory = workingDirectory,
|
||||
UseShellExecute = false,
|
||||
RedirectStandardOutput = true,
|
||||
RedirectStandardError = true,
|
||||
CreateNoWindow = true
|
||||
};
|
||||
|
||||
using (var process = new Process())
|
||||
{
|
||||
process.StartInfo = processInfo;
|
||||
var outputBuilder = new System.Text.StringBuilder();
|
||||
var errorBuilder = new System.Text.StringBuilder();
|
||||
|
||||
process.OutputDataReceived += (sender, e) =>
|
||||
{
|
||||
if (!string.IsNullOrEmpty(e.Data))
|
||||
outputBuilder.AppendLine(e.Data);
|
||||
};
|
||||
|
||||
process.ErrorDataReceived += (sender, e) =>
|
||||
{
|
||||
if (!string.IsNullOrEmpty(e.Data))
|
||||
errorBuilder.AppendLine(e.Data);
|
||||
};
|
||||
|
||||
process.Start();
|
||||
process.BeginOutputReadLine();
|
||||
process.BeginErrorReadLine();
|
||||
|
||||
// Wait for exit with timeout
|
||||
var timeout = TimeSpan.FromSeconds(30);
|
||||
if (await Task.Run(() => process.WaitForExit((int)timeout.TotalMilliseconds)))
|
||||
{
|
||||
process.WaitForExit(); // Ensure all output is processed
|
||||
|
||||
string output = outputBuilder.ToString();
|
||||
string error = errorBuilder.ToString();
|
||||
|
||||
if (!string.IsNullOrEmpty(error))
|
||||
{
|
||||
UnityEngine.Debug.LogError($"Git error: {error}");
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
else
|
||||
{
|
||||
process.Kill();
|
||||
throw new TimeoutException("Git command timed out");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
2
Editor/PSXSplashInstaller.cs.meta
Normal file
2
Editor/PSXSplashInstaller.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 72d1da27a16f0794cb1ad49c00799e74
|
||||
39
Editor/SerialConnection.cs
Normal file
39
Editor/SerialConnection.cs
Normal file
@@ -0,0 +1,39 @@
|
||||
using System.IO.Ports;
|
||||
|
||||
|
||||
namespace SplashEdit.EditorCode
|
||||
{
|
||||
public class SerialConnection
|
||||
{
|
||||
private static SerialPort serialPort;
|
||||
|
||||
public SerialConnection(string portName, int baudRate)
|
||||
{
|
||||
serialPort = new SerialPort(portName, baudRate);
|
||||
serialPort.ReadTimeout = 50;
|
||||
serialPort.WriteTimeout = 50;
|
||||
}
|
||||
|
||||
public void Open()
|
||||
{ serialPort.Open(); }
|
||||
|
||||
public void Close()
|
||||
{ serialPort.Close(); }
|
||||
|
||||
public int ReadByte()
|
||||
{ return serialPort.ReadByte(); }
|
||||
|
||||
public int ReadChar()
|
||||
{ return serialPort.ReadChar(); }
|
||||
|
||||
public void Write(string text)
|
||||
{ serialPort.Write(text); }
|
||||
|
||||
public void Write(char[] buffer, int offset, int count)
|
||||
{ serialPort.Write(buffer, offset, count); }
|
||||
|
||||
public void Write(byte[] buffer, int offset, int count)
|
||||
{ serialPort.Write(buffer, offset, count); }
|
||||
|
||||
}
|
||||
}
|
||||
2
Editor/SerialConnection.cs.meta
Normal file
2
Editor/SerialConnection.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 714bd2374b7a9a14686078e5eb431795
|
||||
22
Editor/UniromConnection.cs
Normal file
22
Editor/UniromConnection.cs
Normal file
@@ -0,0 +1,22 @@
|
||||
namespace SplashEdit.EditorCode
|
||||
{
|
||||
public class UniromConnection
|
||||
{
|
||||
|
||||
private SerialConnection serialConnection;
|
||||
|
||||
public UniromConnection(int baudRate, string portName)
|
||||
{
|
||||
serialConnection = new SerialConnection(portName, baudRate);
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
serialConnection.Open();
|
||||
serialConnection.Write("REST");
|
||||
serialConnection.Close();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
2
Editor/UniromConnection.cs.meta
Normal file
2
Editor/UniromConnection.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d8fbc734f42ab9d42a843b6718127da7
|
||||
145
Editor/UniromConnectionWindow.cs
Normal file
145
Editor/UniromConnectionWindow.cs
Normal file
@@ -0,0 +1,145 @@
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using System.IO.Ports;
|
||||
using System.Collections;
|
||||
using SplashEdit.RuntimeCode;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace SplashEdit.EditorCode
|
||||
{
|
||||
public class PSXConnectionConfigWindow : EditorWindow
|
||||
{
|
||||
|
||||
public PSXConnectionType connectionType = PSXConnectionType.REAL_HARDWARE;
|
||||
|
||||
// REAL HARDWARE (Unirom) SETTINGS
|
||||
private string[] portNames;
|
||||
private int selectedPortIndex = 1;
|
||||
private int[] baudRates = { 9600, 115200 };
|
||||
private int selectedBaudIndex = 0;
|
||||
|
||||
|
||||
|
||||
|
||||
private string statusMessage = "";
|
||||
private MessageType statusType;
|
||||
private Vector2 scrollPosition;
|
||||
|
||||
[MenuItem("PSX/Console or Emulator Connection")]
|
||||
public static void ShowWindow()
|
||||
{
|
||||
GetWindow<PSXConnectionConfigWindow>("Serial Config");
|
||||
}
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
RefreshPorts();
|
||||
LoadSettings();
|
||||
}
|
||||
|
||||
private void RefreshPorts()
|
||||
{
|
||||
portNames = SerialPort.GetPortNames();
|
||||
if (portNames.Length == 0)
|
||||
{
|
||||
portNames = new[] { "No ports available" };
|
||||
}
|
||||
}
|
||||
|
||||
private void OnGUI()
|
||||
{
|
||||
using (var scrollView = new EditorGUILayout.ScrollViewScope(scrollPosition))
|
||||
{
|
||||
scrollPosition = scrollView.scrollPosition;
|
||||
|
||||
EditorGUILayout.LabelField("Pick connection type", EditorStyles.boldLabel);
|
||||
connectionType = (PSXConnectionType)EditorGUILayout.EnumPopup("Connection Type", connectionType);
|
||||
|
||||
if (connectionType == PSXConnectionType.REAL_HARDWARE)
|
||||
{
|
||||
// Port selection
|
||||
EditorGUILayout.LabelField("Select COM Port", EditorStyles.boldLabel);
|
||||
selectedPortIndex = EditorGUILayout.Popup("Available Ports", selectedPortIndex, portNames);
|
||||
|
||||
// Baud rate selection
|
||||
EditorGUILayout.Space();
|
||||
EditorGUILayout.LabelField("Select Baud Rate", EditorStyles.boldLabel);
|
||||
selectedBaudIndex = EditorGUILayout.Popup("Baud Rate", selectedBaudIndex, new[] { "9600", "115200" });
|
||||
|
||||
// Buttons
|
||||
EditorGUILayout.Space();
|
||||
using (new EditorGUILayout.HorizontalScope())
|
||||
{
|
||||
if (GUILayout.Button("Refresh Ports"))
|
||||
{
|
||||
RefreshPorts();
|
||||
}
|
||||
|
||||
if (GUILayout.Button("Test Connection"))
|
||||
{
|
||||
TestConnection();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (GUILayout.Button("Save settings"))
|
||||
{
|
||||
SaveSettings();
|
||||
|
||||
}
|
||||
|
||||
// Status message
|
||||
EditorGUILayout.Space();
|
||||
if (!string.IsNullOrEmpty(statusMessage))
|
||||
{
|
||||
EditorGUILayout.HelpBox(statusMessage, statusType);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private void LoadSettings()
|
||||
{
|
||||
PSXData _psxData = DataStorage.LoadData();
|
||||
if (_psxData != null)
|
||||
{
|
||||
connectionType = _psxData.ConnectionType;
|
||||
selectedBaudIndex = System.Array.IndexOf(baudRates, _psxData.BaudRate);
|
||||
if (selectedBaudIndex == -1) selectedBaudIndex = 0;
|
||||
|
||||
RefreshPorts();
|
||||
selectedPortIndex = System.Array.IndexOf(portNames, _psxData.PortName);
|
||||
if (selectedPortIndex == -1) selectedPortIndex = 0;
|
||||
}
|
||||
}
|
||||
|
||||
private void TestConnection()
|
||||
{
|
||||
if (portNames.Length == 0 || portNames[0] == "No ports available")
|
||||
{
|
||||
statusMessage = "No serial ports available";
|
||||
statusType = MessageType.Error;
|
||||
return;
|
||||
}
|
||||
|
||||
UniromConnection connection = new UniromConnection(baudRates[selectedBaudIndex], portNames[selectedPortIndex]);
|
||||
connection.Reset();
|
||||
|
||||
statusMessage = "Connection tested. If your PlayStation reset, it worked!";
|
||||
statusType = MessageType.Info;
|
||||
Repaint();
|
||||
}
|
||||
|
||||
private void SaveSettings()
|
||||
{
|
||||
PSXData _psxData = DataStorage.LoadData();
|
||||
_psxData.ConnectionType = connectionType;
|
||||
_psxData.BaudRate = baudRates[selectedBaudIndex];
|
||||
_psxData.PortName = portNames[selectedPortIndex];
|
||||
DataStorage.StoreData(_psxData);
|
||||
statusMessage = "Settings saved";
|
||||
statusType = MessageType.Info;
|
||||
Repaint();
|
||||
}
|
||||
}
|
||||
}
|
||||
2
Editor/UniromConnectionWindow.cs.meta
Normal file
2
Editor/UniromConnectionWindow.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ade0bf0fd69f449458c5b43e0f48ddff
|
||||
845
Runtime/BSP.cs
Normal file
845
Runtime/BSP.cs
Normal file
@@ -0,0 +1,845 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using System.Diagnostics;
|
||||
using SplashEdit.RuntimeCode;
|
||||
|
||||
public class BSP
|
||||
{
|
||||
private List<PSXObjectExporter> _objects;
|
||||
private Node root;
|
||||
private const float EPSILON = 1e-6f;
|
||||
private const int MAX_TRIANGLES_PER_LEAF = 256;
|
||||
private const int MAX_TREE_DEPTH = 50;
|
||||
private const int CANDIDATE_PLANE_COUNT = 15;
|
||||
|
||||
// Statistics
|
||||
private int totalTrianglesProcessed;
|
||||
private int totalSplits;
|
||||
private int treeDepth;
|
||||
private Stopwatch buildTimer;
|
||||
|
||||
public bool verboseLogging = false;
|
||||
|
||||
// Store the triangle that was used for the split plane for debugging
|
||||
private Dictionary<Node, Triangle> splitPlaneTriangles = new Dictionary<Node, Triangle>();
|
||||
|
||||
private struct Triangle
|
||||
{
|
||||
public Vector3 v0;
|
||||
public Vector3 v1;
|
||||
public Vector3 v2;
|
||||
public Vector3 n0;
|
||||
public Vector3 n1;
|
||||
public Vector3 n2;
|
||||
public Vector2 uv0;
|
||||
public Vector2 uv1;
|
||||
public Vector2 uv2;
|
||||
public Plane plane;
|
||||
public Bounds bounds;
|
||||
public PSXObjectExporter sourceExporter;
|
||||
public int materialIndex; // Store material index instead of submesh index
|
||||
|
||||
public Triangle(Vector3 a, Vector3 b, Vector3 c, Vector3 na, Vector3 nb, Vector3 nc,
|
||||
Vector2 uva, Vector2 uvb, Vector2 uvc, PSXObjectExporter exporter, int matIndex)
|
||||
{
|
||||
v0 = a;
|
||||
v1 = b;
|
||||
v2 = c;
|
||||
n0 = na;
|
||||
n1 = nb;
|
||||
n2 = nc;
|
||||
uv0 = uva;
|
||||
uv1 = uvb;
|
||||
uv2 = uvc;
|
||||
sourceExporter = exporter;
|
||||
materialIndex = matIndex;
|
||||
|
||||
// Calculate plane
|
||||
Vector3 edge1 = v1 - v0;
|
||||
Vector3 edge2 = v2 - v0;
|
||||
Vector3 normal = Vector3.Cross(edge1, edge2);
|
||||
|
||||
if (normal.sqrMagnitude < 1e-4f)
|
||||
{
|
||||
plane = new Plane(Vector3.up, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
normal.Normalize();
|
||||
plane = new Plane(normal, v0);
|
||||
}
|
||||
|
||||
// Calculate bounds
|
||||
bounds = new Bounds(v0, Vector3.zero);
|
||||
bounds.Encapsulate(v1);
|
||||
bounds.Encapsulate(v2);
|
||||
}
|
||||
|
||||
public void Transform(Matrix4x4 matrix)
|
||||
{
|
||||
v0 = matrix.MultiplyPoint3x4(v0);
|
||||
v1 = matrix.MultiplyPoint3x4(v1);
|
||||
v2 = matrix.MultiplyPoint3x4(v2);
|
||||
|
||||
// Transform normals (using inverse transpose for correct scaling)
|
||||
Matrix4x4 invTranspose = matrix.inverse.transpose;
|
||||
n0 = invTranspose.MultiplyVector(n0).normalized;
|
||||
n1 = invTranspose.MultiplyVector(n1).normalized;
|
||||
n2 = invTranspose.MultiplyVector(n2).normalized;
|
||||
|
||||
// Recalculate plane and bounds after transformation
|
||||
Vector3 edge1 = v1 - v0;
|
||||
Vector3 edge2 = v2 - v0;
|
||||
Vector3 normal = Vector3.Cross(edge1, edge2);
|
||||
|
||||
if (normal.sqrMagnitude < 1e-4f)
|
||||
{
|
||||
plane = new Plane(Vector3.up, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
normal.Normalize();
|
||||
plane = new Plane(normal, v0);
|
||||
}
|
||||
|
||||
bounds = new Bounds(v0, Vector3.zero);
|
||||
bounds.Encapsulate(v1);
|
||||
bounds.Encapsulate(v2);
|
||||
}
|
||||
}
|
||||
|
||||
private class Node
|
||||
{
|
||||
public Plane plane;
|
||||
public Node front;
|
||||
public Node back;
|
||||
public List<Triangle> triangles;
|
||||
public bool isLeaf = false;
|
||||
public Bounds bounds;
|
||||
public int depth;
|
||||
public int triangleSourceIndex = -1;
|
||||
}
|
||||
|
||||
public BSP(List<PSXObjectExporter> objects)
|
||||
{
|
||||
_objects = objects;
|
||||
buildTimer = new Stopwatch();
|
||||
}
|
||||
|
||||
public void Build()
|
||||
{
|
||||
buildTimer.Start();
|
||||
|
||||
List<Triangle> triangles = ExtractTrianglesFromMeshes();
|
||||
totalTrianglesProcessed = triangles.Count;
|
||||
|
||||
if (verboseLogging)
|
||||
UnityEngine.Debug.Log($"Starting BSP build with {totalTrianglesProcessed} triangles");
|
||||
|
||||
if (triangles.Count == 0)
|
||||
{
|
||||
root = null;
|
||||
return;
|
||||
}
|
||||
|
||||
// Calculate overall bounds
|
||||
Bounds overallBounds = CalculateBounds(triangles);
|
||||
|
||||
// Build tree recursively with depth tracking
|
||||
root = BuildNode(triangles, overallBounds, 0);
|
||||
|
||||
// Create modified meshes for all exporters
|
||||
CreateModifiedMeshes();
|
||||
|
||||
buildTimer.Stop();
|
||||
|
||||
if (verboseLogging)
|
||||
{
|
||||
UnityEngine.Debug.Log($"BSP build completed in {buildTimer.Elapsed.TotalMilliseconds}ms");
|
||||
UnityEngine.Debug.Log($"Total splits: {totalSplits}, Max depth: {treeDepth}");
|
||||
}
|
||||
}
|
||||
|
||||
private List<Triangle> ExtractTrianglesFromMeshes()
|
||||
{
|
||||
List<Triangle> triangles = new List<Triangle>();
|
||||
|
||||
foreach (var meshObj in _objects)
|
||||
{
|
||||
if (!meshObj.IsActive) continue;
|
||||
|
||||
MeshFilter mf = meshObj.GetComponent<MeshFilter>();
|
||||
Renderer renderer = meshObj.GetComponent<Renderer>();
|
||||
if (mf == null || mf.sharedMesh == null || renderer == null) continue;
|
||||
|
||||
Mesh mesh = mf.sharedMesh;
|
||||
Vector3[] vertices = mesh.vertices;
|
||||
Vector3[] normals = mesh.normals.Length > 0 ? mesh.normals : new Vector3[vertices.Length];
|
||||
Vector2[] uvs = mesh.uv.Length > 0 ? mesh.uv : new Vector2[vertices.Length];
|
||||
Matrix4x4 matrix = meshObj.transform.localToWorldMatrix;
|
||||
|
||||
// Handle case where normals are missing
|
||||
if (mesh.normals.Length == 0)
|
||||
{
|
||||
for (int i = 0; i < normals.Length; i++)
|
||||
{
|
||||
normals[i] = Vector3.up;
|
||||
}
|
||||
}
|
||||
|
||||
// Handle case where UVs are missing
|
||||
if (mesh.uv.Length == 0)
|
||||
{
|
||||
for (int i = 0; i < uvs.Length; i++)
|
||||
{
|
||||
uvs[i] = Vector2.zero;
|
||||
}
|
||||
}
|
||||
|
||||
// Process each submesh and track material index
|
||||
for (int submesh = 0; submesh < mesh.subMeshCount; submesh++)
|
||||
{
|
||||
int materialIndex = Mathf.Min(submesh, renderer.sharedMaterials.Length - 1);
|
||||
int[] indices = mesh.GetTriangles(submesh);
|
||||
|
||||
for (int i = 0; i < indices.Length; i += 3)
|
||||
{
|
||||
int idx0 = indices[i];
|
||||
int idx1 = indices[i + 1];
|
||||
int idx2 = indices[i + 2];
|
||||
|
||||
Vector3 v0 = vertices[idx0];
|
||||
Vector3 v1 = vertices[idx1];
|
||||
Vector3 v2 = vertices[idx2];
|
||||
|
||||
// Skip degenerate triangles
|
||||
if (Vector3.Cross(v1 - v0, v2 - v0).sqrMagnitude < 1e-4f)
|
||||
continue;
|
||||
|
||||
Vector3 n0 = normals[idx0];
|
||||
Vector3 n1 = normals[idx1];
|
||||
Vector3 n2 = normals[idx2];
|
||||
|
||||
Vector2 uv0 = uvs[idx0];
|
||||
Vector2 uv1 = uvs[idx1];
|
||||
Vector2 uv2 = uvs[idx2];
|
||||
|
||||
Triangle tri = new Triangle(v0, v1, v2, n0, n1, n2, uv0, uv1, uv2, meshObj, materialIndex);
|
||||
tri.Transform(matrix);
|
||||
triangles.Add(tri);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return triangles;
|
||||
}
|
||||
|
||||
private Node BuildNode(List<Triangle> triangles, Bounds bounds, int depth)
|
||||
{
|
||||
if (triangles == null || triangles.Count == 0)
|
||||
return null;
|
||||
|
||||
Node node = new Node
|
||||
{
|
||||
triangles = new List<Triangle>(),
|
||||
bounds = bounds,
|
||||
depth = depth
|
||||
};
|
||||
|
||||
treeDepth = Mathf.Max(treeDepth, depth);
|
||||
|
||||
// Create leaf node if conditions are met
|
||||
if (triangles.Count <= MAX_TRIANGLES_PER_LEAF || depth >= MAX_TREE_DEPTH)
|
||||
{
|
||||
node.isLeaf = true;
|
||||
node.triangles = triangles;
|
||||
|
||||
if (verboseLogging && depth >= MAX_TREE_DEPTH)
|
||||
UnityEngine.Debug.LogWarning($"Max tree depth reached at depth {depth} with {triangles.Count} triangles");
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
// Select the best splitting plane using multiple strategies
|
||||
Triangle? splitTriangle = null;
|
||||
if (!SelectBestSplittingPlane(triangles, bounds, out node.plane, out splitTriangle))
|
||||
{
|
||||
// Fallback: create leaf if no good split found
|
||||
node.isLeaf = true;
|
||||
node.triangles = triangles;
|
||||
|
||||
if (verboseLogging)
|
||||
UnityEngine.Debug.Log($"Created leaf node with {triangles.Count} triangles (no good split found)");
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
// Store the triangle that provided the split plane for debugging
|
||||
if (splitTriangle.HasValue)
|
||||
{
|
||||
splitPlaneTriangles[node] = splitTriangle.Value;
|
||||
}
|
||||
|
||||
List<Triangle> frontList = new List<Triangle>();
|
||||
List<Triangle> backList = new List<Triangle>();
|
||||
List<Triangle> coplanarList = new List<Triangle>();
|
||||
|
||||
// Classify all triangles
|
||||
foreach (var tri in triangles)
|
||||
{
|
||||
ClassifyTriangle(tri, node.plane, coplanarList, frontList, backList);
|
||||
}
|
||||
|
||||
// Handle cases where splitting doesn't provide benefit
|
||||
if (frontList.Count == 0 || backList.Count == 0)
|
||||
{
|
||||
// If split doesn't separate geometry, create a leaf
|
||||
node.isLeaf = true;
|
||||
node.triangles = triangles;
|
||||
|
||||
if (verboseLogging)
|
||||
UnityEngine.Debug.Log($"Created leaf node with {triangles.Count} triangles (ineffective split)");
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
// Distribute coplanar triangles to the side with fewer triangles
|
||||
if (coplanarList.Count > 0)
|
||||
{
|
||||
if (frontList.Count <= backList.Count)
|
||||
{
|
||||
frontList.AddRange(coplanarList);
|
||||
}
|
||||
else
|
||||
{
|
||||
backList.AddRange(coplanarList);
|
||||
}
|
||||
}
|
||||
|
||||
if (verboseLogging)
|
||||
UnityEngine.Debug.Log($"Node at depth {depth}: {triangles.Count} triangles -> {frontList.Count} front, {backList.Count} back");
|
||||
|
||||
// Calculate bounds for children
|
||||
Bounds frontBounds = CalculateBounds(frontList);
|
||||
Bounds backBounds = CalculateBounds(backList);
|
||||
|
||||
// Recursively build child nodes
|
||||
node.front = BuildNode(frontList, frontBounds, depth + 1);
|
||||
node.back = BuildNode(backList, backBounds, depth + 1);
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
private bool SelectBestSplittingPlane(List<Triangle> triangles, Bounds bounds, out Plane bestPlane, out Triangle? splitTriangle)
|
||||
{
|
||||
bestPlane = new Plane();
|
||||
splitTriangle = null;
|
||||
int bestScore = int.MaxValue;
|
||||
bool foundValidPlane = false;
|
||||
|
||||
// Strategy 1: Try planes from triangle centroids
|
||||
int candidatesToTry = Mathf.Min(CANDIDATE_PLANE_COUNT, triangles.Count);
|
||||
for (int i = 0; i < candidatesToTry; i++)
|
||||
{
|
||||
Triangle tri = triangles[i];
|
||||
Plane candidate = tri.plane;
|
||||
|
||||
int score = EvaluateSplitPlane(triangles, candidate);
|
||||
if (score < bestScore && score >= 0)
|
||||
{
|
||||
bestScore = score;
|
||||
bestPlane = candidate;
|
||||
splitTriangle = tri;
|
||||
foundValidPlane = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Strategy 2: Try axis-aligned planes through bounds center
|
||||
if (!foundValidPlane || bestScore > triangles.Count * 3)
|
||||
{
|
||||
Vector3[] axes = { Vector3.right, Vector3.up, Vector3.forward };
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
Plane candidate = new Plane(axes[i], bounds.center);
|
||||
int score = EvaluateSplitPlane(triangles, candidate);
|
||||
if (score < bestScore && score >= 0)
|
||||
{
|
||||
bestScore = score;
|
||||
bestPlane = candidate;
|
||||
splitTriangle = null;
|
||||
foundValidPlane = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Strategy 3: Try planes based on bounds extents
|
||||
if (!foundValidPlane)
|
||||
{
|
||||
Vector3 extents = bounds.extents;
|
||||
if (extents.x >= extents.y && extents.x >= extents.z)
|
||||
bestPlane = new Plane(Vector3.right, bounds.center);
|
||||
else if (extents.y >= extents.x && extents.y >= extents.z)
|
||||
bestPlane = new Plane(Vector3.up, bounds.center);
|
||||
else
|
||||
bestPlane = new Plane(Vector3.forward, bounds.center);
|
||||
|
||||
splitTriangle = null;
|
||||
foundValidPlane = true;
|
||||
}
|
||||
|
||||
return foundValidPlane;
|
||||
}
|
||||
|
||||
private int EvaluateSplitPlane(List<Triangle> triangles, Plane plane)
|
||||
{
|
||||
int frontCount = 0;
|
||||
int backCount = 0;
|
||||
int splitCount = 0;
|
||||
int coplanarCount = 0;
|
||||
|
||||
foreach (var tri in triangles)
|
||||
{
|
||||
float d0 = plane.GetDistanceToPoint(tri.v0);
|
||||
float d1 = plane.GetDistanceToPoint(tri.v1);
|
||||
float d2 = plane.GetDistanceToPoint(tri.v2);
|
||||
|
||||
// Check for NaN/infinity
|
||||
if (float.IsNaN(d0) || float.IsNaN(d1) || float.IsNaN(d2) ||
|
||||
float.IsInfinity(d0) || float.IsInfinity(d1) || float.IsInfinity(d2))
|
||||
{
|
||||
return int.MaxValue;
|
||||
}
|
||||
|
||||
bool front = d0 > EPSILON || d1 > EPSILON || d2 > EPSILON;
|
||||
bool back = d0 < -EPSILON || d1 < -EPSILON || d2 < -EPSILON;
|
||||
|
||||
if (front && back)
|
||||
splitCount++;
|
||||
else if (front)
|
||||
frontCount++;
|
||||
else if (back)
|
||||
backCount++;
|
||||
else
|
||||
coplanarCount++;
|
||||
}
|
||||
|
||||
// Reject planes that would cause too many splits or imbalanced trees
|
||||
if (splitCount > triangles.Count / 2)
|
||||
return int.MaxValue;
|
||||
|
||||
// Score based on balance and split count
|
||||
return Mathf.Abs(frontCount - backCount) + splitCount * 2;
|
||||
}
|
||||
|
||||
private void ClassifyTriangle(Triangle tri, Plane plane, List<Triangle> coplanar, List<Triangle> front, List<Triangle> back)
|
||||
{
|
||||
float d0 = plane.GetDistanceToPoint(tri.v0);
|
||||
float d1 = plane.GetDistanceToPoint(tri.v1);
|
||||
float d2 = plane.GetDistanceToPoint(tri.v2);
|
||||
|
||||
// Check for numerical issues
|
||||
if (float.IsNaN(d0) || float.IsNaN(d1) || float.IsNaN(d2) ||
|
||||
float.IsInfinity(d0) || float.IsInfinity(d1) || float.IsInfinity(d2))
|
||||
{
|
||||
coplanar.Add(tri);
|
||||
return;
|
||||
}
|
||||
|
||||
bool front0 = d0 > EPSILON;
|
||||
bool front1 = d1 > EPSILON;
|
||||
bool front2 = d2 > EPSILON;
|
||||
|
||||
bool back0 = d0 < -EPSILON;
|
||||
bool back1 = d1 < -EPSILON;
|
||||
bool back2 = d2 < -EPSILON;
|
||||
|
||||
int fCount = (front0 ? 1 : 0) + (front1 ? 1 : 0) + (front2 ? 1 : 0);
|
||||
int bCount = (back0 ? 1 : 0) + (back1 ? 1 : 0) + (back2 ? 1 : 0);
|
||||
|
||||
if (fCount == 3)
|
||||
{
|
||||
front.Add(tri);
|
||||
}
|
||||
else if (bCount == 3)
|
||||
{
|
||||
back.Add(tri);
|
||||
}
|
||||
else if (fCount == 0 && bCount == 0)
|
||||
{
|
||||
coplanar.Add(tri);
|
||||
}
|
||||
else
|
||||
{
|
||||
totalSplits++;
|
||||
SplitTriangle(tri, plane, front, back);
|
||||
}
|
||||
}
|
||||
|
||||
private void SplitTriangle(Triangle tri, Plane plane, List<Triangle> front, List<Triangle> back)
|
||||
{
|
||||
// Get distances
|
||||
float d0 = plane.GetDistanceToPoint(tri.v0);
|
||||
float d1 = plane.GetDistanceToPoint(tri.v1);
|
||||
float d2 = plane.GetDistanceToPoint(tri.v2);
|
||||
|
||||
// Classify points
|
||||
bool[] frontSide = { d0 > EPSILON, d1 > EPSILON, d2 > EPSILON };
|
||||
bool[] backSide = { d0 < -EPSILON, d1 < -EPSILON, d2 < -EPSILON };
|
||||
|
||||
// Count how many points are on each side
|
||||
int frontCount = (frontSide[0] ? 1 : 0) + (frontSide[1] ? 1 : 0) + (frontSide[2] ? 1 : 0);
|
||||
int backCount = (backSide[0] ? 1 : 0) + (backSide[1] ? 1 : 0) + (backSide[2] ? 1 : 0);
|
||||
|
||||
// 2 points on one side, 1 on the other
|
||||
if (frontCount == 2 && backCount == 1)
|
||||
{
|
||||
int loneIndex = backSide[0] ? 0 : (backSide[1] ? 1 : 2);
|
||||
SplitTriangle2To1(tri, plane, loneIndex, true, front, back);
|
||||
}
|
||||
else if (backCount == 2 && frontCount == 1)
|
||||
{
|
||||
int loneIndex = frontSide[0] ? 0 : (frontSide[1] ? 1 : 2);
|
||||
SplitTriangle2To1(tri, plane, loneIndex, false, front, back);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Complex case - add to both sides (should be rare)
|
||||
front.Add(tri);
|
||||
back.Add(tri);
|
||||
}
|
||||
}
|
||||
|
||||
private void SplitTriangle2To1(Triangle tri, Plane plane, int loneIndex, bool loneIsBack,
|
||||
List<Triangle> front, List<Triangle> back)
|
||||
{
|
||||
Vector3[] v = { tri.v0, tri.v1, tri.v2 };
|
||||
Vector3[] n = { tri.n0, tri.n1, tri.n2 };
|
||||
Vector2[] uv = { tri.uv0, tri.uv1, tri.uv2 };
|
||||
|
||||
Vector3 loneVertex = v[loneIndex];
|
||||
Vector3 loneNormal = n[loneIndex];
|
||||
Vector2 loneUV = uv[loneIndex];
|
||||
|
||||
Vector3 v1 = v[(loneIndex + 1) % 3];
|
||||
Vector3 v2 = v[(loneIndex + 2) % 3];
|
||||
Vector3 n1 = n[(loneIndex + 1) % 3];
|
||||
Vector3 n2 = n[(loneIndex + 2) % 3];
|
||||
Vector2 uv1 = uv[(loneIndex + 1) % 3];
|
||||
Vector2 uv2 = uv[(loneIndex + 2) % 3];
|
||||
|
||||
Vector3 i1 = PlaneIntersection(plane, loneVertex, v1);
|
||||
float t1 = CalculateInterpolationFactor(plane, loneVertex, v1);
|
||||
Vector3 n_i1 = Vector3.Lerp(loneNormal, n1, t1).normalized;
|
||||
Vector2 uv_i1 = Vector2.Lerp(loneUV, uv1, t1);
|
||||
|
||||
Vector3 i2 = PlaneIntersection(plane, loneVertex, v2);
|
||||
float t2 = CalculateInterpolationFactor(plane, loneVertex, v2);
|
||||
Vector3 n_i2 = Vector3.Lerp(loneNormal, n2, t2).normalized;
|
||||
Vector2 uv_i2 = Vector2.Lerp(loneUV, uv2, t2);
|
||||
|
||||
// Desired normal: prefer triangle's plane normal, fallback to geometric normal
|
||||
Vector3 desired = tri.plane.normal;
|
||||
if (desired.sqrMagnitude < 1e-4f)
|
||||
desired = Vector3.Cross(tri.v1 - tri.v0, tri.v2 - tri.v0).normalized;
|
||||
if (desired.sqrMagnitude < 1e-4f)
|
||||
desired = Vector3.up;
|
||||
|
||||
// Helper: decide and swap b/c if necessary, then add triangle
|
||||
void AddTriClockwise(List<Triangle> list,
|
||||
Vector3 a, Vector3 b, Vector3 c,
|
||||
Vector3 na, Vector3 nb, Vector3 nc,
|
||||
Vector2 ua, Vector2 ub, Vector2 uc)
|
||||
{
|
||||
Vector3 cross = Vector3.Cross(b - a, c - a);
|
||||
if (cross.z > 0f) // <-- assumes you're working in PS1 screen space (z forward)
|
||||
{
|
||||
// swap b <-> c
|
||||
var tmpV = b; b = c; c = tmpV;
|
||||
var tmpN = nb; nb = nc; nc = tmpN;
|
||||
var tmpUv = ub; ub = uc; uc = tmpUv;
|
||||
}
|
||||
|
||||
list.Add(new Triangle(a, b, c, na, nb, nc, ua, ub, uc, tri.sourceExporter, tri.materialIndex));
|
||||
}
|
||||
|
||||
if (loneIsBack)
|
||||
{
|
||||
// back: (lone, i1, i2)
|
||||
AddTriClockwise(back, loneVertex, i1, i2, loneNormal, n_i1, n_i2, loneUV, uv_i1, uv_i2);
|
||||
|
||||
// front: (v1, i1, i2) and (v1, i2, v2)
|
||||
AddTriClockwise(front, v1, i1, i2, n1, n_i1, n_i2, uv1, uv_i1, uv_i2);
|
||||
AddTriClockwise(front, v1, i2, v2, n1, n_i2, n2, uv1, uv_i2, uv2);
|
||||
}
|
||||
else
|
||||
{
|
||||
// front: (lone, i1, i2)
|
||||
AddTriClockwise(front, loneVertex, i1, i2, loneNormal, n_i1, n_i2, loneUV, uv_i1, uv_i2);
|
||||
|
||||
// back: (v1, i1, i2) and (v1, i2, v2)
|
||||
AddTriClockwise(back, v1, i1, i2, n1, n_i1, n_i2, uv1, uv_i1, uv_i2);
|
||||
AddTriClockwise(back, v1, i2, v2, n1, n_i2, n2, uv1, uv_i2, uv2);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private Vector3 PlaneIntersection(Plane plane, Vector3 a, Vector3 b)
|
||||
{
|
||||
Vector3 ba = b - a;
|
||||
float denominator = Vector3.Dot(plane.normal, ba);
|
||||
|
||||
// Check for parallel line (shouldn't happen in our case)
|
||||
if (Mathf.Abs(denominator) < 1e-4f)
|
||||
return a;
|
||||
|
||||
float t = (-plane.distance - Vector3.Dot(plane.normal, a)) / denominator;
|
||||
return a + ba * Mathf.Clamp01(t);
|
||||
}
|
||||
|
||||
private float CalculateInterpolationFactor(Plane plane, Vector3 a, Vector3 b)
|
||||
{
|
||||
Vector3 ba = b - a;
|
||||
float denominator = Vector3.Dot(plane.normal, ba);
|
||||
|
||||
if (Mathf.Abs(denominator) < 1e-4f)
|
||||
return 0.5f;
|
||||
|
||||
float t = (-plane.distance - Vector3.Dot(plane.normal, a)) / denominator;
|
||||
return Mathf.Clamp01(t);
|
||||
}
|
||||
|
||||
private Bounds CalculateBounds(List<Triangle> triangles)
|
||||
{
|
||||
if (triangles == null || triangles.Count == 0)
|
||||
return new Bounds();
|
||||
|
||||
Bounds bounds = triangles[0].bounds;
|
||||
for (int i = 1; i < triangles.Count; i++)
|
||||
{
|
||||
bounds.Encapsulate(triangles[i].bounds);
|
||||
}
|
||||
|
||||
return bounds;
|
||||
}
|
||||
|
||||
// Add a method to create modified meshes after BSP construction
|
||||
// Add a method to create modified meshes after BSP construction
|
||||
private void CreateModifiedMeshes()
|
||||
{
|
||||
if (root == null) return;
|
||||
|
||||
// Collect all triangles from the BSP tree
|
||||
List<Triangle> allTriangles = new List<Triangle>();
|
||||
CollectTrianglesFromNode(root, allTriangles);
|
||||
|
||||
// Group triangles by their source exporter and material index
|
||||
Dictionary<PSXObjectExporter, Dictionary<int, List<Triangle>>> exporterTriangles =
|
||||
new Dictionary<PSXObjectExporter, Dictionary<int, List<Triangle>>>();
|
||||
|
||||
foreach (var tri in allTriangles)
|
||||
{
|
||||
if (!exporterTriangles.ContainsKey(tri.sourceExporter))
|
||||
{
|
||||
exporterTriangles[tri.sourceExporter] = new Dictionary<int, List<Triangle>>();
|
||||
}
|
||||
|
||||
var materialDict = exporterTriangles[tri.sourceExporter];
|
||||
if (!materialDict.ContainsKey(tri.materialIndex))
|
||||
{
|
||||
materialDict[tri.materialIndex] = new List<Triangle>();
|
||||
}
|
||||
|
||||
materialDict[tri.materialIndex].Add(tri);
|
||||
}
|
||||
|
||||
// Create modified meshes for each exporter
|
||||
foreach (var kvp in exporterTriangles)
|
||||
{
|
||||
PSXObjectExporter exporter = kvp.Key;
|
||||
Dictionary<int, List<Triangle>> materialTriangles = kvp.Value;
|
||||
|
||||
Mesh originalMesh = exporter.GetComponent<MeshFilter>().sharedMesh;
|
||||
Renderer renderer = exporter.GetComponent<Renderer>();
|
||||
|
||||
Mesh modifiedMesh = new Mesh();
|
||||
modifiedMesh.name = originalMesh.name + "_BSP";
|
||||
|
||||
List<Vector3> vertices = new List<Vector3>();
|
||||
List<Vector3> normals = new List<Vector3>();
|
||||
List<Vector2> uvs = new List<Vector2>();
|
||||
List<Vector4> tangents = new List<Vector4>();
|
||||
List<Color> colors = new List<Color>();
|
||||
|
||||
// Create a list for each material's triangles
|
||||
List<List<int>> materialIndices = new List<List<int>>();
|
||||
for (int i = 0; i < renderer.sharedMaterials.Length; i++)
|
||||
{
|
||||
materialIndices.Add(new List<int>());
|
||||
}
|
||||
|
||||
// Get the inverse transform to convert from world space back to object space
|
||||
Matrix4x4 worldToLocal = exporter.transform.worldToLocalMatrix;
|
||||
|
||||
// Process each material
|
||||
foreach (var materialKvp in materialTriangles)
|
||||
{
|
||||
int materialIndex = materialKvp.Key;
|
||||
List<Triangle> triangles = materialKvp.Value;
|
||||
|
||||
// Add vertices, normals, and uvs for this material
|
||||
for (int i = 0; i < triangles.Count; i++)
|
||||
{
|
||||
Triangle tri = triangles[i];
|
||||
|
||||
// Transform vertices from world space back to object space
|
||||
Vector3 v0 = worldToLocal.MultiplyPoint3x4(tri.v0);
|
||||
Vector3 v1 = worldToLocal.MultiplyPoint3x4(tri.v1);
|
||||
Vector3 v2 = worldToLocal.MultiplyPoint3x4(tri.v2);
|
||||
|
||||
int vertexIndex = vertices.Count;
|
||||
vertices.Add(v0);
|
||||
vertices.Add(v1);
|
||||
vertices.Add(v2);
|
||||
|
||||
// Transform normals from world space back to object space
|
||||
Vector3 n0 = worldToLocal.MultiplyVector(tri.n0).normalized;
|
||||
Vector3 n1 = worldToLocal.MultiplyVector(tri.n1).normalized;
|
||||
Vector3 n2 = worldToLocal.MultiplyVector(tri.n2).normalized;
|
||||
|
||||
normals.Add(n0);
|
||||
normals.Add(n1);
|
||||
normals.Add(n2);
|
||||
|
||||
uvs.Add(tri.uv0);
|
||||
uvs.Add(tri.uv1);
|
||||
uvs.Add(tri.uv2);
|
||||
|
||||
// Add default tangents and colors (will be recalculated later)
|
||||
tangents.Add(new Vector4(1, 0, 0, 1));
|
||||
tangents.Add(new Vector4(1, 0, 0, 1));
|
||||
tangents.Add(new Vector4(1, 0, 0, 1));
|
||||
|
||||
colors.Add(Color.white);
|
||||
colors.Add(Color.white);
|
||||
colors.Add(Color.white);
|
||||
|
||||
// Add indices for this material
|
||||
materialIndices[materialIndex].Add(vertexIndex);
|
||||
materialIndices[materialIndex].Add(vertexIndex + 1);
|
||||
materialIndices[materialIndex].Add(vertexIndex + 2);
|
||||
}
|
||||
}
|
||||
|
||||
// Assign data to the mesh
|
||||
modifiedMesh.vertices = vertices.ToArray();
|
||||
modifiedMesh.normals = normals.ToArray();
|
||||
modifiedMesh.uv = uvs.ToArray();
|
||||
modifiedMesh.tangents = tangents.ToArray();
|
||||
modifiedMesh.colors = colors.ToArray();
|
||||
|
||||
// Set up submeshes based on materials
|
||||
modifiedMesh.subMeshCount = materialIndices.Count;
|
||||
for (int i = 0; i < materialIndices.Count; i++)
|
||||
{
|
||||
modifiedMesh.SetTriangles(materialIndices[i].ToArray(), i);
|
||||
}
|
||||
|
||||
// Recalculate important mesh properties
|
||||
modifiedMesh.RecalculateBounds();
|
||||
modifiedMesh.RecalculateTangents();
|
||||
|
||||
// Assign the modified mesh to the exporter
|
||||
exporter.ModifiedMesh = modifiedMesh;
|
||||
}
|
||||
}
|
||||
// Helper method to collect all triangles from the BSP tree
|
||||
private void CollectTrianglesFromNode(Node node, List<Triangle> triangles)
|
||||
{
|
||||
if (node == null) return;
|
||||
|
||||
if (node.isLeaf)
|
||||
{
|
||||
triangles.AddRange(node.triangles);
|
||||
}
|
||||
else
|
||||
{
|
||||
CollectTrianglesFromNode(node.front, triangles);
|
||||
CollectTrianglesFromNode(node.back, triangles);
|
||||
}
|
||||
}
|
||||
|
||||
public void DrawGizmos(int maxDepth)
|
||||
{
|
||||
if (root == null) return;
|
||||
|
||||
DrawNodeGizmos(root, 0, maxDepth);
|
||||
}
|
||||
|
||||
private void DrawNodeGizmos(Node node, int depth, int maxDepth)
|
||||
{
|
||||
if (node == null) return;
|
||||
if (depth > maxDepth) return;
|
||||
|
||||
Color nodeColor = Color.HSVToRGB((depth * 0.1f) % 1f, 0.8f, 0.8f);
|
||||
Gizmos.color = nodeColor;
|
||||
|
||||
if (node.isLeaf)
|
||||
{
|
||||
foreach (var tri in node.triangles)
|
||||
{
|
||||
DrawTriangleGizmo(tri);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
DrawPlaneGizmo(node.plane, node.bounds);
|
||||
|
||||
// Draw the triangle that was used for the split plane if available
|
||||
if (splitPlaneTriangles.ContainsKey(node))
|
||||
{
|
||||
Gizmos.color = Color.magenta;
|
||||
DrawTriangleGizmo(splitPlaneTriangles[node]);
|
||||
Gizmos.color = nodeColor;
|
||||
}
|
||||
|
||||
DrawNodeGizmos(node.front, depth + 1, maxDepth);
|
||||
DrawNodeGizmos(node.back, depth + 1, maxDepth);
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawTriangleGizmo(Triangle tri)
|
||||
{
|
||||
Gizmos.DrawLine(tri.v0, tri.v1);
|
||||
Gizmos.DrawLine(tri.v1, tri.v2);
|
||||
Gizmos.DrawLine(tri.v2, tri.v0);
|
||||
}
|
||||
|
||||
private void DrawPlaneGizmo(Plane plane, Bounds bounds)
|
||||
{
|
||||
Vector3 center = bounds.center;
|
||||
Vector3 normal = plane.normal;
|
||||
|
||||
Vector3 tangent = Vector3.Cross(normal, Vector3.up);
|
||||
if (tangent.magnitude < 0.1f) tangent = Vector3.Cross(normal, Vector3.right);
|
||||
tangent = tangent.normalized;
|
||||
|
||||
Vector3 bitangent = Vector3.Cross(normal, tangent).normalized;
|
||||
|
||||
float size = Mathf.Max(bounds.size.x, bounds.size.y, bounds.size.z) * 0.5f;
|
||||
tangent *= size;
|
||||
bitangent *= size;
|
||||
|
||||
Vector3 p0 = center - tangent - bitangent;
|
||||
Vector3 p1 = center + tangent - bitangent;
|
||||
Vector3 p2 = center + tangent + bitangent;
|
||||
Vector3 p3 = center - tangent + bitangent;
|
||||
|
||||
Gizmos.DrawLine(p0, p1);
|
||||
Gizmos.DrawLine(p1, p2);
|
||||
Gizmos.DrawLine(p2, p3);
|
||||
Gizmos.DrawLine(p3, p0);
|
||||
|
||||
Gizmos.color = Color.red;
|
||||
Gizmos.DrawLine(center, center + normal * size * 0.5f);
|
||||
}
|
||||
}
|
||||
2
Runtime/BSP.cs.meta
Normal file
2
Runtime/BSP.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 15144e67b42b92447a546346e594155b
|
||||
@@ -3,14 +3,36 @@ using UnityEngine;
|
||||
|
||||
namespace SplashEdit.RuntimeCode
|
||||
{
|
||||
|
||||
public enum PSXConnectionType
|
||||
{
|
||||
REAL_HARDWARE, // Unirom
|
||||
EMULATOR // PCSX-Redux
|
||||
}
|
||||
|
||||
[CreateAssetMenu(fileName = "PSXData", menuName = "Scriptable Objects/PSXData")]
|
||||
public class PSXData : ScriptableObject
|
||||
{
|
||||
|
||||
// Texture packing settings
|
||||
public Vector2 OutputResolution = new Vector2(320, 240);
|
||||
public bool DualBuffering = true;
|
||||
public bool VerticalBuffering = true;
|
||||
public List<ProhibitedArea> ProhibitedAreas = new List<ProhibitedArea>();
|
||||
|
||||
|
||||
// Connection settings
|
||||
public PSXConnectionType ConnectionType = PSXConnectionType.REAL_HARDWARE;
|
||||
|
||||
// Real hardware settings
|
||||
public string PortName = "COM3";
|
||||
public int BaudRate = 0;
|
||||
|
||||
// Emulator settings
|
||||
public string PCSXReduxPath = "";
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -28,7 +28,7 @@ namespace SplashEdit.RuntimeCode
|
||||
public PSXVertex v1;
|
||||
public PSXVertex v2;
|
||||
|
||||
public int TextureIndex;
|
||||
public int TextureIndex;
|
||||
public readonly PSXVertex[] Vertexes => new PSXVertex[] { v0, v1, v2 };
|
||||
}
|
||||
|
||||
@@ -82,77 +82,176 @@ namespace SplashEdit.RuntimeCode
|
||||
/// <param name="textureHeight">Height of the texture (default is 256).</param>
|
||||
/// <param name="transform">Optional transform to convert vertices to world space.</param>
|
||||
/// <returns>A new PSXMesh containing the converted triangles.</returns>
|
||||
public static PSXMesh CreateFromUnityRenderer(Renderer renderer, float GTEScaling, Transform transform, List<PSXTexture2D> textures)
|
||||
{
|
||||
PSXMesh psxMesh = new PSXMesh { Triangles = new List<Tri>() };
|
||||
Material[] materials = renderer.sharedMaterials;
|
||||
Mesh mesh = renderer.GetComponent<MeshFilter>().sharedMesh;
|
||||
|
||||
for (int submeshIndex = 0; submeshIndex < materials.Length; submeshIndex++)
|
||||
{
|
||||
int[] submeshTriangles = mesh.GetTriangles(submeshIndex);
|
||||
Material material = materials[submeshIndex];
|
||||
Texture2D texture = material.mainTexture as Texture2D;
|
||||
|
||||
// Find texture index instead of the texture itself
|
||||
int textureIndex = -1;
|
||||
if (texture != null)
|
||||
public static PSXMesh CreateFromUnityRenderer(Renderer renderer, float GTEScaling, Transform transform, List<PSXTexture2D> textures)
|
||||
{
|
||||
for (int i = 0; i < textures.Count; i++)
|
||||
PSXMesh psxMesh = new PSXMesh { Triangles = new List<Tri>() };
|
||||
Material[] materials = renderer.sharedMaterials;
|
||||
Mesh mesh = renderer.GetComponent<MeshFilter>().sharedMesh;
|
||||
|
||||
for (int submeshIndex = 0; submeshIndex < materials.Length; submeshIndex++)
|
||||
{
|
||||
if (textures[i].OriginalTexture == texture)
|
||||
int[] submeshTriangles = mesh.GetTriangles(submeshIndex);
|
||||
Material material = materials[submeshIndex];
|
||||
Texture2D texture = material.mainTexture as Texture2D;
|
||||
|
||||
// Find texture index instead of the texture itself
|
||||
int textureIndex = -1;
|
||||
if (texture != null)
|
||||
{
|
||||
textureIndex = i;
|
||||
break;
|
||||
for (int i = 0; i < textures.Count; i++)
|
||||
{
|
||||
if (textures[i].OriginalTexture == texture)
|
||||
{
|
||||
textureIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (textureIndex == -1)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get mesh data arrays
|
||||
mesh.RecalculateNormals();
|
||||
Vector3[] vertices = mesh.vertices;
|
||||
Vector3[] normals = mesh.normals;
|
||||
Vector3[] smoothNormals = RecalculateSmoothNormals(mesh);
|
||||
Vector2[] uv = mesh.uv;
|
||||
|
||||
PSXVertex convertData(int index)
|
||||
{
|
||||
Vector3 v = Vector3.Scale(vertices[index], transform.lossyScale);
|
||||
Vector3 wv = transform.TransformPoint(vertices[index]);
|
||||
Vector3 wn = transform.TransformDirection(smoothNormals[index]).normalized;
|
||||
Color c = PSXLightingBaker.ComputeLighting(wv, wn);
|
||||
return ConvertToPSXVertex(v, GTEScaling, normals[index], uv[index], textures[textureIndex]?.Width, textures[textureIndex]?.Height, c);
|
||||
}
|
||||
|
||||
for (int i = 0; i < submeshTriangles.Length; i += 3)
|
||||
{
|
||||
int vid0 = submeshTriangles[i];
|
||||
int vid1 = submeshTriangles[i + 1];
|
||||
int vid2 = submeshTriangles[i + 2];
|
||||
|
||||
Vector3 faceNormal = Vector3.Cross(vertices[vid1] - vertices[vid0], vertices[vid2] - vertices[vid0]).normalized;
|
||||
|
||||
if (Vector3.Dot(faceNormal, normals[vid0]) < 0)
|
||||
{
|
||||
(vid1, vid2) = (vid2, vid1);
|
||||
}
|
||||
|
||||
psxMesh.Triangles.Add(new Tri
|
||||
{
|
||||
v0 = convertData(vid0),
|
||||
v1 = convertData(vid1),
|
||||
v2 = convertData(vid2),
|
||||
TextureIndex = textureIndex
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return psxMesh;
|
||||
}
|
||||
|
||||
if (textureIndex == -1)
|
||||
public static PSXMesh CreateFromUnityMesh(Mesh mesh, Renderer renderer, float GTEScaling, Transform transform, List<PSXTexture2D> textures)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
PSXMesh psxMesh = new PSXMesh { Triangles = new List<Tri>() };
|
||||
Material[] materials = renderer.sharedMaterials;
|
||||
|
||||
// Get mesh data arrays
|
||||
mesh.RecalculateNormals();
|
||||
Vector3[] vertices = mesh.vertices;
|
||||
Vector3[] normals = mesh.normals;
|
||||
Vector3[] smoothNormals = RecalculateSmoothNormals(mesh);
|
||||
Vector2[] uv = mesh.uv;
|
||||
|
||||
PSXVertex convertData(int index)
|
||||
{
|
||||
Vector3 v = Vector3.Scale(vertices[index], transform.lossyScale);
|
||||
Vector3 wv = transform.TransformPoint(vertices[index]);
|
||||
Vector3 wn = transform.TransformDirection(smoothNormals[index]).normalized;
|
||||
Color c = PSXLightingBaker.ComputeLighting(wv, wn);
|
||||
return ConvertToPSXVertex(v, GTEScaling, normals[index], uv[index], textures[textureIndex]?.Width, textures[textureIndex]?.Height, c);
|
||||
}
|
||||
|
||||
for (int i = 0; i < submeshTriangles.Length; i += 3)
|
||||
{
|
||||
int vid0 = submeshTriangles[i];
|
||||
int vid1 = submeshTriangles[i + 1];
|
||||
int vid2 = submeshTriangles[i + 2];
|
||||
|
||||
Vector3 faceNormal = Vector3.Cross(vertices[vid1] - vertices[vid0], vertices[vid2] - vertices[vid0]).normalized;
|
||||
|
||||
if (Vector3.Dot(faceNormal, normals[vid0]) < 0)
|
||||
// Ensure mesh has required data
|
||||
if (mesh.normals == null || mesh.normals.Length == 0)
|
||||
{
|
||||
(vid1, vid2) = (vid2, vid1);
|
||||
mesh.RecalculateNormals();
|
||||
}
|
||||
|
||||
psxMesh.Triangles.Add(new Tri {
|
||||
v0 = convertData(vid0),
|
||||
v1 = convertData(vid1),
|
||||
v2 = convertData(vid2),
|
||||
TextureIndex = textureIndex
|
||||
});
|
||||
}
|
||||
}
|
||||
if (mesh.uv == null || mesh.uv.Length == 0)
|
||||
{
|
||||
Vector2[] uvs = new Vector2[mesh.vertices.Length];
|
||||
mesh.uv = uvs;
|
||||
}
|
||||
|
||||
return psxMesh;
|
||||
}
|
||||
// Precompute smooth normals for the entire mesh
|
||||
Vector3[] smoothNormals = RecalculateSmoothNormals(mesh);
|
||||
|
||||
// Precompute world positions and normals for all vertices
|
||||
Vector3[] worldVertices = new Vector3[mesh.vertices.Length];
|
||||
Vector3[] worldNormals = new Vector3[mesh.normals.Length];
|
||||
|
||||
for (int i = 0; i < mesh.vertices.Length; i++)
|
||||
{
|
||||
worldVertices[i] = transform.TransformPoint(mesh.vertices[i]);
|
||||
worldNormals[i] = transform.TransformDirection(mesh.normals[i]).normalized;
|
||||
}
|
||||
|
||||
for (int submeshIndex = 0; submeshIndex < mesh.subMeshCount; submeshIndex++)
|
||||
{
|
||||
int materialIndex = Mathf.Min(submeshIndex, materials.Length - 1);
|
||||
Material material = materials[materialIndex];
|
||||
Texture2D texture = material.mainTexture as Texture2D;
|
||||
|
||||
// Find texture index
|
||||
int textureIndex = -1;
|
||||
if (texture != null)
|
||||
{
|
||||
for (int i = 0; i < textures.Count; i++)
|
||||
{
|
||||
if (textures[i].OriginalTexture == texture)
|
||||
{
|
||||
textureIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int[] submeshTriangles = mesh.GetTriangles(submeshIndex);
|
||||
|
||||
// Get mesh data arrays
|
||||
Vector3[] vertices = mesh.vertices;
|
||||
Vector3[] normals = mesh.normals;
|
||||
Vector2[] uv = mesh.uv;
|
||||
|
||||
PSXVertex convertData(int index)
|
||||
{
|
||||
Vector3 v = Vector3.Scale(vertices[index], transform.lossyScale);
|
||||
|
||||
// Use precomputed world position and normal for consistent lighting
|
||||
Vector3 wv = worldVertices[index];
|
||||
Vector3 wn = worldNormals[index];
|
||||
|
||||
// For split triangles, use the original vertex's lighting if possible
|
||||
Color c = PSXLightingBaker.ComputeLighting(wv, wn);
|
||||
|
||||
return ConvertToPSXVertex(v, GTEScaling, normals[index], uv[index],
|
||||
textures[textureIndex]?.Width, textures[textureIndex]?.Height, c);
|
||||
}
|
||||
|
||||
for (int i = 0; i < submeshTriangles.Length; i += 3)
|
||||
{
|
||||
int vid0 = submeshTriangles[i];
|
||||
int vid1 = submeshTriangles[i + 1];
|
||||
int vid2 = submeshTriangles[i + 2];
|
||||
|
||||
Vector3 faceNormal = Vector3.Cross(vertices[vid1] - vertices[vid0], vertices[vid2] - vertices[vid0]).normalized;
|
||||
|
||||
if (Vector3.Dot(faceNormal, normals[vid0]) < 0)
|
||||
{
|
||||
(vid1, vid2) = (vid2, vid1);
|
||||
}
|
||||
|
||||
psxMesh.Triangles.Add(new Tri
|
||||
{
|
||||
v0 = convertData(vid0),
|
||||
v1 = convertData(vid1),
|
||||
v2 = convertData(vid2),
|
||||
TextureIndex = textureIndex
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return psxMesh;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a Unity vertex into a PSXVertex by applying fixed-point conversion, shading, and UV mapping.
|
||||
|
||||
@@ -12,19 +12,30 @@ namespace SplashEdit.RuntimeCode
|
||||
|
||||
public bool IsActive = true;
|
||||
|
||||
public List<PSXTexture2D> Textures { get; set; } = new List<PSXTexture2D>(); // Stores the converted PlayStation-style texture
|
||||
public PSXMesh Mesh { get; protected set; } // Stores the converted PlayStation-style mesh
|
||||
public List<PSXTexture2D> Textures { get; set; } = new List<PSXTexture2D>();
|
||||
public PSXMesh Mesh { get; protected set; }
|
||||
|
||||
[Header("Export Settings")]
|
||||
[FormerlySerializedAs("BitDepth")]
|
||||
[SerializeField] private PSXBPP bitDepth = PSXBPP.TEX_8BIT; // Defines the bit depth of the texture (e.g., 4BPP, 8BPP)
|
||||
[SerializeField] private PSXBPP bitDepth = PSXBPP.TEX_8BIT;
|
||||
[SerializeField] private LuaFile luaFile;
|
||||
|
||||
[Header("BSP Settings")]
|
||||
[SerializeField] private Mesh _modifiedMesh; // Mesh after BSP processing
|
||||
|
||||
[Header("Gizmo Settings")]
|
||||
[FormerlySerializedAs("PreviewNormals")]
|
||||
[SerializeField] private bool previewNormals = false;
|
||||
[SerializeField] private float normalPreviewLength = 0.5f; // Length of the normal lines
|
||||
[SerializeField] private float normalPreviewLength = 0.5f;
|
||||
|
||||
private readonly Dictionary<(int, PSXBPP), PSXTexture2D> cache = new();
|
||||
|
||||
public Mesh ModifiedMesh
|
||||
{
|
||||
get => _modifiedMesh;
|
||||
set => _modifiedMesh = value;
|
||||
}
|
||||
|
||||
private void OnDrawGizmos()
|
||||
{
|
||||
if (previewNormals)
|
||||
@@ -37,25 +48,19 @@ namespace SplashEdit.RuntimeCode
|
||||
Vector3[] vertices = mesh.vertices;
|
||||
Vector3[] normals = mesh.normals;
|
||||
|
||||
Gizmos.color = Color.green; // Normal color
|
||||
Gizmos.color = Color.green;
|
||||
|
||||
for (int i = 0; i < vertices.Length; i++)
|
||||
{
|
||||
Vector3 worldVertex = transform.TransformPoint(vertices[i]); // Convert to world space
|
||||
Vector3 worldNormal = transform.TransformDirection(normals[i]); // Transform normal to world space
|
||||
Vector3 worldVertex = transform.TransformPoint(vertices[i]);
|
||||
Vector3 worldNormal = transform.TransformDirection(normals[i]);
|
||||
|
||||
Gizmos.DrawLine(worldVertex, worldVertex + worldNormal * normalPreviewLength);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Converts the object's material texture into a PlayStation-compatible texture.
|
||||
/// </summary>
|
||||
///
|
||||
public void CreatePSXTextures2D()
|
||||
{
|
||||
Renderer renderer = GetComponent<Renderer>();
|
||||
@@ -71,14 +76,12 @@ namespace SplashEdit.RuntimeCode
|
||||
Texture mainTexture = mat.mainTexture;
|
||||
Texture2D tex2D = null;
|
||||
|
||||
// Check if it's already a Texture2D
|
||||
if (mainTexture is Texture2D existingTex2D)
|
||||
{
|
||||
tex2D = existingTex2D;
|
||||
}
|
||||
else
|
||||
{
|
||||
// If not a Texture2D, try to convert
|
||||
tex2D = ConvertToTexture2D(mainTexture);
|
||||
}
|
||||
|
||||
@@ -92,7 +95,7 @@ namespace SplashEdit.RuntimeCode
|
||||
else
|
||||
{
|
||||
tex = PSXTexture2D.CreateFromTexture2D(tex2D, bitDepth);
|
||||
tex.OriginalTexture = tex2D; // Store reference to the original texture
|
||||
tex.OriginalTexture = tex2D;
|
||||
cache.Add((tex2D.GetInstanceID(), bitDepth), tex);
|
||||
}
|
||||
Textures.Add(tex);
|
||||
@@ -104,10 +107,8 @@ namespace SplashEdit.RuntimeCode
|
||||
|
||||
private Texture2D ConvertToTexture2D(Texture texture)
|
||||
{
|
||||
// Create a new Texture2D with the same dimensions and format
|
||||
Texture2D texture2D = new Texture2D(texture.width, texture.height, TextureFormat.RGBA32, false);
|
||||
|
||||
// Read the texture pixels
|
||||
RenderTexture currentActiveRT = RenderTexture.active;
|
||||
RenderTexture.active = texture as RenderTexture;
|
||||
|
||||
@@ -128,15 +129,34 @@ namespace SplashEdit.RuntimeCode
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the object's mesh into a PlayStation-compatible mesh.
|
||||
/// </summary>
|
||||
public void CreatePSXMesh(float GTEScaling)
|
||||
public void CreatePSXMesh(float GTEScaling, bool useBSP = false)
|
||||
{
|
||||
Renderer renderer = GetComponent<Renderer>();
|
||||
if (renderer != null)
|
||||
{
|
||||
Mesh = PSXMesh.CreateFromUnityRenderer(renderer, GTEScaling, transform, Textures);
|
||||
if (useBSP && _modifiedMesh != null)
|
||||
{
|
||||
// Create a temporary GameObject with the modified mesh but same materials
|
||||
GameObject tempGO = new GameObject("TempBSPMesh");
|
||||
tempGO.transform.position = transform.position;
|
||||
tempGO.transform.rotation = transform.rotation;
|
||||
tempGO.transform.localScale = transform.localScale;
|
||||
|
||||
MeshFilter tempMF = tempGO.AddComponent<MeshFilter>();
|
||||
tempMF.sharedMesh = _modifiedMesh;
|
||||
|
||||
MeshRenderer tempMR = tempGO.AddComponent<MeshRenderer>();
|
||||
tempMR.sharedMaterials = renderer.sharedMaterials;
|
||||
|
||||
Mesh = PSXMesh.CreateFromUnityRenderer(tempMR, GTEScaling, transform, Textures);
|
||||
|
||||
// Clean up
|
||||
GameObject.DestroyImmediate(tempGO);
|
||||
}
|
||||
else
|
||||
{
|
||||
Mesh = PSXMesh.CreateFromUnityRenderer(renderer, GTEScaling, transform, Textures);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,6 +32,11 @@ namespace SplashEdit.RuntimeCode
|
||||
private Quaternion _playerRot;
|
||||
private float _playerHeight;
|
||||
|
||||
private BSP _bsp;
|
||||
|
||||
public bool PreviewBSP = true;
|
||||
public int BSPPreviewDepth = 9999;
|
||||
|
||||
public void Export()
|
||||
{
|
||||
_psxData = DataStorage.LoadData(out selectedResolution, out dualBuffering, out verticalLayout, out prohibitedAreas);
|
||||
@@ -42,7 +47,7 @@ namespace SplashEdit.RuntimeCode
|
||||
PSXObjectExporter exp = _exporters[i];
|
||||
EditorUtility.DisplayProgressBar($"{nameof(PSXSceneExporter)}", $"Export {nameof(PSXObjectExporter)}", ((float)i) / _exporters.Length);
|
||||
exp.CreatePSXTextures2D();
|
||||
exp.CreatePSXMesh(GTEScaling);
|
||||
exp.CreatePSXMesh(GTEScaling, true);
|
||||
}
|
||||
|
||||
_navmeshes = FindObjectsByType<PSXNavMesh>(FindObjectsSortMode.None);
|
||||
@@ -66,6 +71,9 @@ namespace SplashEdit.RuntimeCode
|
||||
_playerRot = player.transform.rotation;
|
||||
}
|
||||
|
||||
_bsp = new BSP(_exporters.ToList());
|
||||
_bsp.Build();
|
||||
|
||||
ExportFile();
|
||||
}
|
||||
|
||||
@@ -450,6 +458,10 @@ namespace SplashEdit.RuntimeCode
|
||||
Vector3 cubeSize = new Vector3(8.0f * GTEScaling, 8.0f * GTEScaling, 8.0f * GTEScaling);
|
||||
Gizmos.color = Color.red;
|
||||
Gizmos.DrawWireCube(sceneOrigin, cubeSize);
|
||||
|
||||
if (_bsp == null || !PreviewBSP) return;
|
||||
_bsp.DrawGizmos(BSPPreviewDepth);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user