Second commit
This commit is contained in:
commit
36840f3ad4
999 files changed
+61364
No files matched your search
@@ -0,0 +1,39 @@
|
||||
//
|
||||
// This file is part of the Tremble package by Tiny Goose.
|
||||
// Copyright (c) 2024-2026 TinyGoose Ltd., All Rights Reserved.
|
||||
//
|
||||
|
||||
using System;
|
||||
using UnityEditor;
|
||||
|
||||
namespace TinyGoose.Tremble.Editor
|
||||
{
|
||||
public class FgdFileAssetPostprocessor : AssetPostprocessor
|
||||
{
|
||||
private static void OnPostprocessAllAssets(string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromAssetPaths)
|
||||
{
|
||||
foreach (string requestFileName in importedAssets)
|
||||
{
|
||||
// We imported something, but not a MapFileAsset
|
||||
if (AssetDatabase.GetMainAssetTypeAtPath(requestFileName) != typeof(FgdFileAsset))
|
||||
continue;
|
||||
|
||||
bool success = false;
|
||||
|
||||
try
|
||||
{
|
||||
FgdParser.Import(requestFileName);
|
||||
success = true;
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (!success)
|
||||
{
|
||||
EditorUtility.DisplayDialog("Import Failed!", "Failed to import FGD file :(, try again!", "Okay");
|
||||
}
|
||||
AssetDatabase.DeleteAsset(requestFileName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 04f793041d9e49d7822ba5068a727bb7
|
||||
timeCreated: 1724694689
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 277805
|
||||
packageName: Tremble - Map Importer
|
||||
packageVersion: 1.11.1
|
||||
assetPath: Assets/Tremble/Editor/AssetProcessors/FgdFileAssetPostprocessor.cs
|
||||
uploadId: 939664
|
||||
@@ -0,0 +1,146 @@
|
||||
//
|
||||
// This file is part of the Tremble package by Tiny Goose.
|
||||
// Copyright (c) 2024-2026 TinyGoose Ltd., All Rights Reserved.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace TinyGoose.Tremble.Editor
|
||||
{
|
||||
public class MapFileAssetPostprocessor : AssetPostprocessor
|
||||
{
|
||||
private static readonly Stack<string> s_MapsToConvert = new();
|
||||
private static string s_LastMapPath;
|
||||
|
||||
private static void OnPostprocessAllAssets(string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromAssetPaths)
|
||||
{
|
||||
foreach (string requestFileName in importedAssets)
|
||||
{
|
||||
// We imported something, but not a MapFileAsset
|
||||
if (AssetDatabase.GetMainAssetTypeAtPath(requestFileName) != typeof(MapFileAsset))
|
||||
continue;
|
||||
|
||||
// Check we haven't already processed this map, or already plan to
|
||||
string mapFileName = GetMapFileName(requestFileName);
|
||||
if (File.Exists(mapFileName) || s_MapsToConvert.Contains(requestFileName))
|
||||
continue;
|
||||
|
||||
// Add to the work queue
|
||||
s_MapsToConvert.Push(requestFileName);
|
||||
|
||||
// Start conversion process
|
||||
EditorApplication.update -= PerformConversionOnUpdate;
|
||||
EditorApplication.update += PerformConversionOnUpdate;
|
||||
}
|
||||
}
|
||||
|
||||
private static string GetMapFileName(string requestFileName) => requestFileName.Replace(".asset", ".map");
|
||||
|
||||
private static void PerformConversionOnUpdate()
|
||||
{
|
||||
// Nothing left to do - stop!
|
||||
if (s_MapsToConvert.Count == 0)
|
||||
{
|
||||
EditorApplication.update -= PerformConversionOnUpdate;
|
||||
return;
|
||||
}
|
||||
|
||||
string requestFileName = s_MapsToConvert.Pop();
|
||||
|
||||
// Delete the request file
|
||||
string mapFileName = GetMapFileName(requestFileName);
|
||||
AssetDatabase.DeleteAsset(requestFileName);
|
||||
|
||||
// Open new file for write
|
||||
using FileStream file = File.Create(mapFileName);
|
||||
using StreamWriter sw = new(file, Encoding.ASCII);
|
||||
|
||||
// Write a blank map, or copy template
|
||||
TrembleSyncSettings trembleSyncSettings = TrembleSyncSettings.Get();
|
||||
if (trembleSyncSettings.TemplateMap && trembleSyncSettings.TemplateMap.TryGetComponent(out MapDocument _))
|
||||
{
|
||||
string templatePath = AssetDatabase.GetAssetPath(trembleSyncSettings.TemplateMap);
|
||||
|
||||
using FileStream templateFile = File.OpenRead(templatePath);
|
||||
using StreamReader sr = new(templateFile, Encoding.ASCII);
|
||||
|
||||
while (!sr.EndOfStream)
|
||||
{
|
||||
sw.WriteLine(sr.ReadLine());
|
||||
}
|
||||
sr.Close();
|
||||
}
|
||||
else
|
||||
{
|
||||
// New file
|
||||
sw.WriteLine($"// Game: {TrembleConsts.GAME_NAME}");
|
||||
sw.WriteLine("// Format: Quake3 (Valve)");
|
||||
sw.WriteLine("// entity 0");
|
||||
sw.WriteLine("{");
|
||||
sw.WriteLine("\"mapversion\" \"220\"");
|
||||
|
||||
sw.Write("\"_tb_textures\" \"");
|
||||
List<MaterialGroup> materialGroups = trembleSyncSettings.GetMaterialGroupsOrDefault();
|
||||
sw.Write(String.Join(';', materialGroups.Select(mg => $"textures/{mg.Name}")));
|
||||
sw.WriteLine("\"");
|
||||
|
||||
sw.WriteLine("\"classname\" \"worldspawn\"");
|
||||
sw.WriteLine("// brush 0");
|
||||
sw.WriteLine("{");
|
||||
sw.WriteLine("( -128 -128 -64 ) ( -128 -127 -64 ) ( -128 -128 -63 ) __TB_empty [ 0 -1 0 0 ] [ 0 0 -1 0 ] 0 0.5 0.5");
|
||||
sw.WriteLine("( -128 -128 -64 ) ( -128 -128 -63 ) ( -127 -128 -64 ) __TB_empty [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 0.5 0.5");
|
||||
sw.WriteLine("( -128 -128 -64 ) ( -127 -128 -64 ) ( -128 -127 -64 ) __TB_empty [ -1 0 0 0 ] [ 0 -1 0 0 ] 0 0.5 0.5");
|
||||
sw.WriteLine("( 128 128 64 ) ( 128 129 64 ) ( 129 128 64 ) __TB_empty [ 1 0 0 0 ] [ 0 -1 0 0 ] 0 0.5 0.5");
|
||||
sw.WriteLine("( 128 128 64 ) ( 129 128 64 ) ( 128 128 65 ) __TB_empty [ -1 0 0 0 ] [ 0 0 -1 0 ] 0 0.5 0.5");
|
||||
sw.WriteLine("( 128 128 64 ) ( 128 128 65 ) ( 128 129 64 ) __TB_empty [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 0.5 0.5");
|
||||
sw.WriteLine("}");
|
||||
sw.WriteLine("}");
|
||||
}
|
||||
|
||||
sw.Close();
|
||||
|
||||
// Open in TB?
|
||||
if (EditorUtility.DisplayDialog("Map Created!", "Want to open it now in TrenchBroom?", "Yep!", "Not now..."))
|
||||
{
|
||||
TrembleEditorAPI.SyncToTrenchBroom();
|
||||
TrenchBroomUtil.OpenWithTrenchBroom(mapFileName);
|
||||
|
||||
s_LastMapPath = mapFileName;
|
||||
|
||||
EditorApplication.update -= PromptToOpenMapOnFocus;
|
||||
EditorApplication.update += PromptToOpenMapOnFocus;
|
||||
}
|
||||
}
|
||||
|
||||
private static void PromptToOpenMapOnFocus()
|
||||
{
|
||||
#if UNITY_2022_1_OR_NEWER
|
||||
if (!EditorApplication.isFocused)
|
||||
return;
|
||||
#endif
|
||||
|
||||
EditorApplication.update -= PromptToOpenMapOnFocus;
|
||||
|
||||
// Place in world?
|
||||
if (EditorUtility.DisplayDialog("Welcome back!", "Want to place your brand new map in this Unity scene?", "Yep!", "No thanks"))
|
||||
{
|
||||
AssetDatabase.Refresh();
|
||||
|
||||
GameObject mapPrefab = AssetDatabase.LoadAssetAtPath<GameObject>(s_LastMapPath);
|
||||
if (!mapPrefab)
|
||||
{
|
||||
EditorUtility.DisplayDialog("Oh no!", "Your new map failed to import. You may need to restart Unity and then reimport it. (this is a rare bug)", "Okay :(");
|
||||
return;
|
||||
}
|
||||
GameObject mapObject = (GameObject)PrefabUtility.InstantiatePrefab(mapPrefab);
|
||||
mapObject.transform.position = Vector3.zero;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6642958d88f3475995ac5c15d631af2d
|
||||
timeCreated: 1707842798
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 277805
|
||||
packageName: Tremble - Map Importer
|
||||
packageVersion: 1.11.1
|
||||
assetPath: Assets/Tremble/Editor/AssetProcessors/MapFileAssetPostprocessor.cs
|
||||
uploadId: 939664
|
||||
@@ -0,0 +1,202 @@
|
||||
//
|
||||
// This file is part of the Tremble package by Tiny Goose.
|
||||
// Copyright (c) 2024-2026 TinyGoose Ltd., All Rights Reserved.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using UnityEngine.SceneManagement;
|
||||
using Debug = UnityEngine.Debug;
|
||||
|
||||
namespace TinyGoose.Tremble.Editor
|
||||
{
|
||||
public class MapLiveUpdatePostprocessor : AssetPostprocessor
|
||||
{
|
||||
private static void OnPostprocessAllAssets(string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromAssetPaths)
|
||||
{
|
||||
if (!Application.isPlaying || !TrembleSyncSettings.Get().UseLiveUpdate)
|
||||
return;
|
||||
|
||||
// Get list of imported GUIDs as strings
|
||||
string[] importedGUIDs = importedAssets
|
||||
.Select(path => AssetDatabase.GUIDFromAssetPath(path).ToString())
|
||||
.ToArray();
|
||||
|
||||
// If we reimport during gameplay, disable and re-enable the map to refresh the collision
|
||||
Scene scene = SceneManager.GetActiveScene();
|
||||
MapDocument[] updatedMaps = scene.GetRootGameObjects()
|
||||
.SelectMany(go => go.GetComponentsInChildren<MapDocument>())
|
||||
.Where(doc => importedGUIDs.Contains(doc.OriginalAssetGuid))
|
||||
.ToArray();
|
||||
|
||||
foreach (MapDocument map in updatedMaps)
|
||||
{
|
||||
Stopwatch timer = new();
|
||||
timer.Start();
|
||||
|
||||
string assetPath = AssetDatabase.GUIDToAssetPath(map.OriginalAssetGuid);
|
||||
GameObject prefabMap = AssetDatabase.LoadAssetAtPath<GameObject>(assetPath);
|
||||
MapDocument prefabMapDocument = prefabMap.GetComponent<MapDocument>();
|
||||
|
||||
// Re-instance worldspawn
|
||||
GameObject newWorldspawn = InstanceNewWorldspawn(map, prefabMapDocument);
|
||||
|
||||
// Spawn any new objects, and transfer vars to existing
|
||||
int numNewlySpawned = 0;
|
||||
int numUpdated = 0;
|
||||
foreach (Transform prefabChild in prefabMap.transform)
|
||||
{
|
||||
if (prefabChild.gameObject == prefabMapDocument.WorldspawnObject)
|
||||
continue;
|
||||
|
||||
bool isNew = true;
|
||||
foreach (Transform mapChild in map.transform)
|
||||
{
|
||||
if (mapChild.name != prefabChild.name)
|
||||
continue;
|
||||
|
||||
numUpdated++;
|
||||
|
||||
// Object is static :( grab it from the map instead
|
||||
if (mapChild.gameObject.isStatic && HasEntityMoved(mapChild, prefabChild))
|
||||
{
|
||||
Debug.LogWarning($"Tremble LiveUpdate: Replacing static object '{mapChild.name}' with new copy. This could cause issues!");
|
||||
GameObject.Destroy(mapChild.gameObject);
|
||||
break;
|
||||
}
|
||||
|
||||
if (TryFindTrembleComponent(prefabChild.gameObject, out Component prefabComp) &&
|
||||
TryFindTrembleComponent(mapChild.gameObject, out Component mapComp))
|
||||
{
|
||||
// Disable, copy across data, re-enable
|
||||
mapComp.gameObject.SetActive(false);
|
||||
CopySafeSerialisedData(from: prefabComp, to: mapComp);
|
||||
mapComp.gameObject.SetActive(true);
|
||||
|
||||
// Handle live update callbacks
|
||||
switch (mapComp)
|
||||
{
|
||||
case IOnLiveUpdate_Simple:
|
||||
mapComp.SendMessage("OnDestroy", SendMessageOptions.DontRequireReceiver);
|
||||
mapComp.SendMessage("Awake", SendMessageOptions.DontRequireReceiver);
|
||||
mapComp.SendMessage("Start", SendMessageOptions.DontRequireReceiver);
|
||||
break;
|
||||
|
||||
case IOnLiveUpdate liveUpdateable:
|
||||
liveUpdateable.OnLiveUpdated();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
isNew = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (isNew)
|
||||
{
|
||||
numNewlySpawned++;
|
||||
GameObject.Instantiate(prefabChild.gameObject, newWorldspawn.transform);
|
||||
}
|
||||
}
|
||||
|
||||
timer.Stop();
|
||||
|
||||
TryShowGameViewNotification($"Tremble LiveUpdate:\n{numNewlySpawned} new ents\n{numUpdated} updates\nin {timer.ElapsedMilliseconds}ms");
|
||||
}
|
||||
}
|
||||
|
||||
private static bool HasEntityMoved(Transform mapChild, Transform prefabChild)
|
||||
{
|
||||
if (Vector3.SqrMagnitude(mapChild.position - prefabChild.position) > 0f)
|
||||
return true;
|
||||
|
||||
if (Vector3.SqrMagnitude(mapChild.rotation.eulerAngles - prefabChild.rotation.eulerAngles) > 0f)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static GameObject InstanceNewWorldspawn(MapDocument map, MapDocument prefabMapDocument)
|
||||
{
|
||||
// Remove worldspawn
|
||||
GameObject.Destroy(map.WorldspawnObject);
|
||||
|
||||
// Reinstance worldspawn
|
||||
GameObject newWorldspawn = GameObject.Instantiate(prefabMapDocument.WorldspawnObject, map.transform, true);
|
||||
newWorldspawn.transform.localPosition = Vector3.zero;
|
||||
newWorldspawn.transform.localRotation = Quaternion.identity;
|
||||
map.Modify(so => so.FindProperty("m_WorldspawnObject").objectReferenceValue = newWorldspawn);
|
||||
newWorldspawn.transform.SetAsFirstSibling();
|
||||
|
||||
// Disable and re-enable - if you don't do this, Unity does not update the colliders
|
||||
map.gameObject.SetActive(false);
|
||||
map.gameObject.SetActive(true);
|
||||
return newWorldspawn;
|
||||
}
|
||||
|
||||
private static bool TryShowGameViewNotification(string note)
|
||||
{
|
||||
Type gameViewT = typeof(EditorWindow).Assembly.GetType("UnityEditor.GameView");
|
||||
|
||||
EditorWindow[] windows = Resources.FindObjectsOfTypeAll(gameViewT).Cast<EditorWindow>().ToArray();
|
||||
foreach (EditorWindow win in windows)
|
||||
{
|
||||
win.ShowNotification(new(note), 1f);
|
||||
}
|
||||
|
||||
return windows.Length > 0;
|
||||
}
|
||||
|
||||
private static bool TryFindTrembleComponent(GameObject go, out Component component)
|
||||
{
|
||||
foreach (Component c in go.GetComponents<Component>())
|
||||
{
|
||||
if (!c.GetType().HasCustomAttribute<EntityAttributeBase>())
|
||||
continue;
|
||||
|
||||
component = c;
|
||||
return true;
|
||||
}
|
||||
|
||||
component = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
private static void CopySafeSerialisedData(Component from, Component to)
|
||||
{
|
||||
// For now, we just copy simple data - floats, colours, vectors etc.
|
||||
// References and assets are a bit spoopier and we can't do too much there sadly!
|
||||
|
||||
SerializedObject fromObject = new(from);
|
||||
SerializedObject toObject = new(to);
|
||||
|
||||
SerializedProperty iterator = fromObject.GetIterator();
|
||||
iterator.NextVisible(true);
|
||||
|
||||
do
|
||||
{
|
||||
if (iterator.propertyType
|
||||
is SerializedPropertyType.ObjectReference
|
||||
or SerializedPropertyType.ManagedReference)
|
||||
{
|
||||
if (iterator.objectReferenceValue is Component)
|
||||
continue;
|
||||
}
|
||||
|
||||
toObject.CopyFromSerializedProperty(iterator);
|
||||
} while (iterator.NextVisible(false));
|
||||
|
||||
toObject.ApplyModifiedPropertiesWithoutUndo();
|
||||
|
||||
// Also copy basic positioning information
|
||||
Transform fromTransform = from.transform;
|
||||
Transform toTransform = to.transform;
|
||||
|
||||
toTransform.localPosition = fromTransform.localPosition;
|
||||
toTransform.localRotation = fromTransform.localRotation;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: de0e937a05b84f928a4e0c9ff6d32146
|
||||
timeCreated: 1708536137
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 277805
|
||||
packageName: Tremble - Map Importer
|
||||
packageVersion: 1.11.1
|
||||
assetPath: Assets/Tremble/Editor/AssetProcessors/MapLiveUpdatePostprocessor.cs
|
||||
uploadId: 939664
|
||||
@@ -0,0 +1,22 @@
|
||||
//
|
||||
// This file is part of the Tremble package by Tiny Goose.
|
||||
// Copyright (c) 2024-2026 TinyGoose Ltd., All Rights Reserved.
|
||||
//
|
||||
|
||||
using UnityEditor;
|
||||
|
||||
namespace TinyGoose.Tremble.Editor
|
||||
{
|
||||
public class TrembleCacheDropperPostprocessor : AssetPostprocessor
|
||||
{
|
||||
private static void OnPostprocessAllAssets(string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromAssetPaths)
|
||||
{
|
||||
if (!TrembleSyncSettings.Get().AutomaticallyReimportWhenDependencyChanges)
|
||||
{
|
||||
MapHierarchyIcons.ResetModifiedTimes();
|
||||
}
|
||||
|
||||
EditorLoaderAdaptor.DropCachedAssetPaths();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5e639ddfe46c434eb843b1bd7ee8259c
|
||||
timeCreated: 1713956404
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 277805
|
||||
packageName: Tremble - Map Importer
|
||||
packageVersion: 1.11.1
|
||||
assetPath: Assets/Tremble/Editor/AssetProcessors/TrembleCacheDropperPostprocessor.cs
|
||||
uploadId: 939664
|
||||
@@ -0,0 +1,38 @@
|
||||
//
|
||||
// This file is part of the Tremble package by Tiny Goose.
|
||||
// Copyright (c) 2024-2026 TinyGoose Ltd., All Rights Reserved.
|
||||
//
|
||||
|
||||
using UnityEditor;
|
||||
|
||||
namespace TinyGoose.Tremble.Editor
|
||||
{
|
||||
public class WadFileAssetPostprocessor : AssetPostprocessor
|
||||
{
|
||||
private static void OnPostprocessAllAssets(string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromAssetPaths)
|
||||
{
|
||||
foreach (string requestFileName in importedAssets)
|
||||
{
|
||||
// We imported something, but not a WadFileAsset
|
||||
if (AssetDatabase.GetMainAssetTypeAtPath(requestFileName) != typeof(WadFileAsset))
|
||||
continue;
|
||||
|
||||
bool success = false;
|
||||
|
||||
try
|
||||
{
|
||||
WadAssetImporterEditor.Extract(requestFileName);
|
||||
success = true;
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (!success)
|
||||
{
|
||||
EditorUtility.DisplayDialog("Import Failed!", "Failed to import WAD file :(, try again!", "Okay");
|
||||
}
|
||||
AssetDatabase.DeleteAsset(requestFileName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c83945126e2c4fa9aa009f1463530bad
|
||||
timeCreated: 1722936486
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 277805
|
||||
packageName: Tremble - Map Importer
|
||||
packageVersion: 1.11.1
|
||||
assetPath: Assets/Tremble/Editor/AssetProcessors/WadFileAssetPostprocessor.cs
|
||||
uploadId: 939664
|
||||
Reference in new issue
Block a user