Second commit

This commit is contained in:
ThomasCharester committed 2026-09-20 15:26:03 +03:00
commit 36840f3ad4
999 files changed
+61364

No files matched your search

+306
View File
@@ -0,0 +1,306 @@
//
// 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.Globalization;
using System.Runtime.CompilerServices;
using UnityEngine;
namespace TinyGoose.Tremble
{
public record BspOptions(float UV2Padding = 0.02f);
public class MapBsp
{
public string MapName { get; init; }
public float ImportScale { get; init; }
// -----------------------------------------------------------------------------------------------------------------------------
// State
// -----------------------------------------------------------------------------------------------------------------------------
public TexInfo[] TexInfos { get; init; }
public Plane[] Planes { get; init; }
public BspNode[] Nodes { get; init; }
public BspLeaf[] Leaves { get; init; }
public int[] LeafFaces { get; init; }
public bool[] UsedFaces { get; init; }
public BspModel[] Models { get; init; }
public BspVertex[] Vertices { get; init; }
public BspFace[] Faces { get; init; }
public int[] MeshVertices { get; init; }
public List<BspEntity> Entities { get; init; }
public BspOptions Options { get; init; }
public MapGroupData[] Groups { get; init; }
public void GatherResourceList(List<string> outPaths)
{
TrembleSyncSettings syncSettings = TrembleSyncSettings.Get();
MaterialNameLookup materialNameLookup = new(syncSettings);
PrefabNameLookup prefabNameLookup = new(syncSettings);
GatherResourceList(materialNameLookup, prefabNameLookup, outPaths);
}
public void GatherResourceList(MaterialNameLookup materialNameLookup, PrefabNameLookup prefabNameLookup, List<string> outPaths)
{
foreach (TexInfo texInfo in TexInfos)
{
if (!materialNameLookup.TryGetMaterialPathFromMapName(texInfo.Name, out string materialPath))
continue;
outPaths.Add(materialPath);
}
foreach (BspEntity entity in Entities)
{
if (!prefabNameLookup.TryGetPrefabPathFromMapName(entity.GetClassname(), out string prefabPath))
continue;
outPaths.Add(prefabPath);
}
}
}
// -----------------------------------------------------------------------------------------------------------------------------
// Map Types
// -----------------------------------------------------------------------------------------------------------------------------
public readonly struct MapGroupData
{
public string Name { get; init; }
public bool IsNoExport { get; init; }
public bool IsLayer { get; init; }
public string LabelName => (IsLayer ? "layer: " : "group: ") + Name;
}
// -----------------------------------------------------------------------------------------------------------------------------
// BSPTypes
// -----------------------------------------------------------------------------------------------------------------------------
public readonly struct BspEntity : IEquatable<BspEntity>
{
public int SerialNumber { get; init; }
public Dictionary<string, string> Entries { get; init; }
public float ImportScale { get; init; }
public bool Equals(BspEntity other)
{
return other.SerialNumber == SerialNumber;
}
public override bool Equals(object obj)
{
return obj is BspEntity other && Equals(other);
}
public override int GetHashCode()
{
return SerialNumber;
}
public static bool operator ==(BspEntity left, BspEntity right)
{
return left.Equals(right);
}
public static bool operator !=(BspEntity left, BspEntity right)
{
return !left.Equals(right);
}
}
public readonly struct TexInfo
{
public string Name { get; init; }
public int Flags { get; init; }
public int Contents { get; init; }
}
public readonly struct BspVertex
{
public Vector3 Position { get; init; }
public Vector3 Normal { get; init; }
public Color32 Color { get; init; }
public Vector2 TexCoord { get; init; }
public Vector2 LightmapCoord { get; init; }
}
public readonly struct BspFace
{
public int TexID { get; init; }
public int Effect { get; init; }
public int Type { get; init; } // 1=polygon, 2=patch, 3=mesh, 4=billboard
public int VertexStartIdx { get; init; }
public int NumVertices { get; init; }
public int MeshVertexStartIdx { get; init; }
public int NumMeshVertices { get; init; }
public int LightmapIndex { get; init; }
public Vector2Int LightmapStart { get; init; }
public Vector2Int LightmapSize { get; init; }
public Vector3 LightmapOrigin { get; init; }
public Vector3 LightmapVector1 { get; init; }
public Vector3 LightmapVector2 { get; init; }
public Vector3 Normal { get; init; }
public Vector2Int Size { get; init; }
}
public readonly struct BspModel
{
public Vector3 Min { get; init; }
public Vector3 Max { get; init; }
public int StartFaceIdx { get; init; }
public int NumFaces { get; init; }
public int StartBrushIdx { get; init; }
public int NumBrushes { get; init; }
}
public readonly struct BspMeshVertex
{
public Vector3 Position { get; init; }
public Vector3 Normal { get; init; }
public Vector2 UV { get; init; }
public int BaseIndex { get; init; }
}
public struct BspSubMesh
{
public int TexID { get; init; }
public List<int> Triangles { get; init; }
}
public readonly struct BspNode
{
public int PlaneIndex { get; init; }
public int Child1 { get; init; } // negative numbers are leafs
public int Child2 { get; init; }
public Vector3Int Min { get; init; }
public Vector3Int Max { get; init; }
}
public readonly struct BspLeaf
{
public int VisCluster { get; init; }
public int PortalArea { get; init; }
public Vector3Int Min { get; init; }
public Vector3Int Max { get; init; }
public int LeafFaceStartIdx { get; init; }
public int NumLeafFaces { get; init; }
public int LeafBrushStartIdx { get; init; }
public int NumLeafBrushes { get; init; }
}
// -----------------------------------------------------------------------------------------------------------------------------
// MD3 Types
// -----------------------------------------------------------------------------------------------------------------------------
public readonly struct MD3Surface
{
public Vector3[] Vertices { get; init; }
public Vector2[] Uvs { get; init; }
public Vector3[] Normals { get; init; }
public string Texture { get; init; }
public string Name { get; init; }
public int DataSize => Vertices.Length * 16 + Vertices.Length * 4 + 176;
}
// -----------------------------------------------------------------------------------------------------------------------------
// Vector Conversions
// -----------------------------------------------------------------------------------------------------------------------------
public static class Q3VectorExtensions
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector3 Q3ToUnityVector(this Vector3 q3Vector, float scale = 1f)
=> new Vector3(q3Vector.x, q3Vector.z, q3Vector.y) * scale;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector3 UnityToQ3Vector(this Vector3 unityVector, float scale = 1f)
=> new Vector3(unityVector.x, unityVector.z, unityVector.y) / scale;
public static bool TryParseQ3Vector(this string inVector, out Vector3 value)
{
string[] parts = inVector.Split(" ");
// Vector, e.g. "x y z"
if (parts.Length == 3 &&
parts[0].TryParseFloat(out float x) &&
parts[1].TryParseFloat(out float y) &&
parts[2].TryParseFloat(out float z))
{
value = new(x, y, z);
return true;
}
// Float, e.g. "x"
if (parts.Length == 1 && parts[0].TryParseFloat(out float f))
{
value = new(f, f, f);
return true;
}
value = Vector3.zero;
return false;
}
}
// -----------------------------------------------------------------------------------------------------------------------------
// Colour Conversions
// -----------------------------------------------------------------------------------------------------------------------------
public static class Q3ColourExtensions
{
public static bool TryParseQ3Colour(this string inColour, out Color value)
{
if (inColour.IsNullOrEmpty())
{
value = Color.white;
return false;
}
string[] parts = inColour.Split(" ");
if (parts.Length != 3)
{
value = default;
return false;
}
if (!parts[0].TryParseFloat(out float r)
|| !parts[1].TryParseFloat(out float g)
|| !parts[2].TryParseFloat(out float b))
{
value = default;
return false;
}
if (r > 1f || g > 1f || b > 1f)
{
// Assume BYTE values, 0-255
value = new(r / 255f, g / 255f, b / 255f);
return true;
}
else
{
// Assume FLOAT values, 0-1
value = new(r, g, b);
return true;
}
}
}
}
+10
View File
@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: a502265f744340118b51b9ce63ad42bc
timeCreated: 1698058785
AssetOrigin:
serializedVersion: 1
productId: 277805
packageName: Tremble - Map Importer
packageVersion: 1.11.1
assetPath: Assets/Tremble/Types/BspTypes.cs
uploadId: 939664
+290
View File
@@ -0,0 +1,290 @@
//
// 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.Linq;
using System.Reflection;
using UnityEngine;
namespace TinyGoose.Tremble
{
public enum EntityType
{
Brush,
Point
}
public static class MapBspGetters
{
private static TrembleFieldConverterCollection s_ConverterCollection;
public static bool FindEntityById(this MapBsp mapBsp, string id, out BspEntity entity)
{
string identityProperty = TrembleSyncSettings.Get().IdentityPropertyName;
foreach (BspEntity ent in mapBsp.Entities)
{
if (!ent.TryGetString(identityProperty, out string foundId) || !foundId.EqualsInvariant(id, caseSensitive: true))
continue;
entity = ent;
return true;
}
entity = default;
return false;
}
public static List<BspEntity> FindEntitiesById(this MapBsp mapBsp, string id)
{
string identityProperty = TrembleSyncSettings.Get().IdentityPropertyName;
List<BspEntity> entities = new();
foreach (BspEntity ent in mapBsp.Entities)
{
if (!ent.TryGetString(identityProperty, out string foundId) || !foundId.EqualsInvariant(id, caseSensitive: true))
continue;
entities.Add(ent);
}
return entities;
}
public static BspEntity FindFirstEntityOfClass(this MapBsp mapBsp, string className)
{
foreach (BspEntity ent in mapBsp.Entities)
{
if (ent.GetClassname().EqualsInvariant(className, caseSensitive: true))
return ent;
}
return default;
}
public static List<BspEntity> FindEntitiesOfClass(this MapBsp mapBsp, string className)
{
List<BspEntity> entities = new();
foreach (BspEntity ent in mapBsp.Entities)
{
if (!ent.GetClassname().EqualsInvariant(className, caseSensitive: true))
continue;
entities.Add(ent);
}
return entities;
}
public static TReturnType GetBaseClassData<TReturnType>(this BspEntity entity, ITrembleBaseClass instance, string name)
{
PropertyInfo defaultProperty = instance
.GetType()
.GetInterfaces()
.SelectMany(i => i.GetProperties())
.FirstOrDefault(p => p.Name.EqualsInvariant(name, caseSensitive: true));
if (defaultProperty == null)
return default;
string fieldName = name.GetFieldNameInMap(TrembleSyncSettings.Get().FieldNamingConvention);
s_ConverterCollection ??= new();
TrembleFieldConverter converter = s_ConverterCollection.GetConverterForType(defaultProperty.PropertyType);
if (converter != null)
{
// Use a converter to convert the map value
if (converter.TryGetValueFromMap(entity, fieldName, null, defaultProperty, out object mapValue))
{
if (defaultProperty.GetFieldOrPropertyTypeOrElementType() == typeof(Transform) && mapValue is Component c)
{
mapValue = c.transform;
}
return (TReturnType)mapValue;
}
// Couldn't convert - read the default value from the interface base
return (TReturnType)defaultProperty.GetValue(instance);
}
// Nothing worked - log a warning
Debug.LogWarning($"Saw field '{defaultProperty.Name}' of type {defaultProperty.GetFieldOrPropertyType()}, which is not yet supported. Try adding a custom TrembleFieldConverter for it, if you like!");
return (TReturnType)defaultProperty.GetValue(instance);
}
public static BspEntity GetWorldspawnEntity(this MapBsp mapBsp)
{
return FindFirstEntityOfClass(mapBsp, "worldspawn");
}
public static string GetClassname(this BspEntity entity)
=> entity.GetString(FgdConsts.PROPERTY_CLASSNAME);
public static string GetID(this BspEntity entity)
=> entity.GetString(TrembleSyncSettings.Get().IdentityPropertyName);
public static bool HasID(this BspEntity entity)
=> entity.Entries.ContainsKey(TrembleSyncSettings.Get().IdentityPropertyName);
public static bool HasKey(this BspEntity entity, string key) => entity.Entries.ContainsKey(key);
public static IEnumerable<string> GetAllNumberedKeys(this BspEntity entity, string prefix)
{
return entity.Entries.Keys
.Where(k => k.StartsWithInvariant(prefix, caseSensitive: true))
.OrderBy(k => k);
}
public static bool TryGetString(this BspEntity entity, string key, out string value) => entity.Entries.TryGetValue(key, out value);
public static string GetString(this BspEntity entity, string key, string defaultValue = null)
=> entity.Entries.GetValueOrDefault(key, defaultValue);
public static string[] GetStrings(this BspEntity entity, string keyPrefix)
=> entity.GetAllNumberedKeys(keyPrefix).Select(key => entity.GetString(key)).ToArray();
public static bool TryGetInt(this BspEntity entity, string key, out int value)
{
if (!entity.Entries.TryGetValue(key, out string strValue))
{
value = default;
return false;
}
return int.TryParse(strValue, out value);
}
public static int GetInt(this BspEntity entity, string key, int defaultValue)
=> TryGetInt(entity, key, out int value) ? value : defaultValue;
public static int[] GetInts(this BspEntity entity, string keyPrefix)
=> entity.GetAllNumberedKeys(keyPrefix).Select(key => entity.GetInt(key, 0)).ToArray();
public static bool TryGetFloat(this BspEntity entity, string key, out float value)
{
if (!entity.Entries.TryGetValue(key, out string strValue))
{
value = default;
return false;
}
return strValue.TryParseFloat(out value);
}
public static float GetFloat(this BspEntity entity, string key, float defaultValue)
=> TryGetFloat(entity, key, out float value) ? value : defaultValue;
public static float[] GetFloats(this BspEntity entity, string keyPrefix)
=> entity.GetAllNumberedKeys(keyPrefix).Select(key => entity.GetFloat(key, 0f)).ToArray();
public static bool TryGetBoolean(this BspEntity entity, string key, out bool value)
{
if (!entity.Entries.TryGetValue(key, out string strValue)
|| !int.TryParse(strValue, out int intValue))
{
value = default;
return false;
}
value = (intValue > 0);
return true;
}
public static bool GetBoolean(this BspEntity entity, string key, bool defaultValue)
=> TryGetBoolean(entity, key, out bool value) ? value : defaultValue;
public static bool[] GetBooleans(this BspEntity entity, string keyPrefix)
=> entity.GetAllNumberedKeys(keyPrefix).Select(key => entity.GetBoolean(key, false)).ToArray();
public static bool TryGetSpawnFlag(this BspEntity entity, int bit, out bool value)
{
int bitValue = 1 << bit;
if (!entity.Entries.TryGetValue(FgdConsts.PROPERTY_SPAWNFLAGS, out string strValue)
|| !int.TryParse(strValue, out int intValue))
{
value = default;
return false;
}
value = (intValue & bitValue) > 0;
return true;
}
public static bool GetSpawnFlag(this BspEntity entity, int bit, bool defaultValue)
=> TryGetSpawnFlag(entity, bit, out bool value) ? value : defaultValue;
public static bool TryGetUntransformedVector(this BspEntity entity, string key, out Vector3 value)
{
if (!entity.Entries.TryGetValue(key, out string strValue))
{
value = default;
return false;
}
return strValue.TryParseQ3Vector(out value);
}
public static Vector3 GetUntransformedVector(this BspEntity entity, string key, Vector3 defaultValue)
=> TryGetUntransformedVector(entity, key, out Vector3 value) ? value : defaultValue;
public static Vector3[] GetUntransformedVectors(this BspEntity entity, string keyPrefix)
=> entity.GetAllNumberedKeys(keyPrefix).Select(key => entity.GetUntransformedVector(key, Vector3.zero)).ToArray();
public static bool TryGetVector(this BspEntity entity, string key, out Vector3 value)
{
if (!TryGetUntransformedVector(entity, key, out Vector3 rawValue))
{
value = rawValue;
return false;
}
value = rawValue.Q3ToUnityVector(scale: entity.ImportScale);
return true;
}
public static Vector3 GetVector(this BspEntity entity, string key, Vector3 defaultValue)
=> TryGetVector(entity, key, out Vector3 value) ? value : defaultValue;
public static Vector3[] GetVectors(this BspEntity entity, string keyPrefix)
=> entity.GetAllNumberedKeys(keyPrefix).Select(key => entity.GetVector(key, Vector3.zero)).ToArray();
public static bool TryGetRotation(this BspEntity entity, string key, out Quaternion value)
{
if (entity.TryGetUntransformedVector(key, out Vector3 rawAngles))
{
// From TB Docs: "angles" is interpreted as "pitch yaw roll" (if the entity model is a Quake MDL, pitch is inverted)
Quaternion quat = Quaternion.identity;
if (rawAngles.z != 0) quat = Quaternion.AngleAxis(-rawAngles.z, Vector3.right) * quat; // pitch
if (rawAngles.x != 0) quat = Quaternion.AngleAxis(-rawAngles.x, Vector3.forward) * quat; // roll
if (rawAngles.y != 0) quat = Quaternion.AngleAxis(-rawAngles.y, Vector3.up) * quat; // yaw
value = quat;
return true;
}
value = default;
return false;
}
public static Quaternion GetRotation(this BspEntity entity, string key, Quaternion defaultValue)
=> TryGetRotation(entity, key, out Quaternion value) ? value : defaultValue;
public static Quaternion[] GetRotations(this BspEntity entity, string keyPrefix)
=> entity.GetAllNumberedKeys(keyPrefix).Select(key => entity.GetRotation(key, Quaternion.identity)).ToArray();
public static Vector3 GetPosition(this BspEntity entity)
=> TryGetVector(entity, FgdConsts.PROPERTY_ORIGIN, out Vector3 value) ? value : Vector3.zero;
public static bool TryGetColour(this BspEntity entity, string key, out Color value)
{
if (!entity.Entries.TryGetValue(key, out string strValue))
{
value = default;
return false;
}
return strValue.TryParseQ3Colour(out value);
}
public static Color GetColour(this BspEntity entity, string key, Color defaultValue)
=> TryGetColour(entity, key, out Color value) ? value : defaultValue;
public static Color[] GetColours(this BspEntity entity, string keyPrefix)
=> entity.GetAllNumberedKeys(keyPrefix).Select(key => entity.GetColour(key, Color.white)).ToArray();
}
}
@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 282f35cb4fdd4cc59aeffd3c688fb299
timeCreated: 1698070299
AssetOrigin:
serializedVersion: 1
productId: 277805
packageName: Tremble - Map Importer
packageVersion: 1.11.1
assetPath: Assets/Tremble/Types/MapBspGetters.cs
uploadId: 939664
+125
View File
@@ -0,0 +1,125 @@
//
// 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 UnityEngine;
using Assembly = System.Reflection.Assembly;
#if UNITY_EDITOR
using UnityEditor;
#endif
namespace TinyGoose.Tremble
{
[Serializable]
public class MapProcessorClass
{
// -----------------------------------------------------------------------------------------------------------------------------
// Serialised
// -----------------------------------------------------------------------------------------------------------------------------
[SerializeField] private string m_MapClassName;
// -----------------------------------------------------------------------------------------------------------------------------
// Static lookup
// -----------------------------------------------------------------------------------------------------------------------------
private static readonly Dictionary<string, Type> s_MapClassLookup = new();
// -----------------------------------------------------------------------------------------------------------------------------
// State
// -----------------------------------------------------------------------------------------------------------------------------
private Type m_Class;
public MapProcessorClass(string mapClassName = null)
{
m_MapClassName = mapClassName;
}
public MapProcessorClass(Type mapClass)
{
m_Class = mapClass;
m_MapClassName = mapClass.FullName;
}
public static implicit operator MapProcessorClass(Type t) => new(t);
// -----------------------------------------------------------------------------------------------------------------------------
// Equality
// -----------------------------------------------------------------------------------------------------------------------------
protected bool Equals(MapProcessorClass other) => m_MapClassName == other.m_MapClassName;
public override bool Equals(object obj)
{
if (obj is null)
return false;
if (ReferenceEquals(this, obj))
return true;
return obj.GetType() == GetType() && Equals((MapProcessorClass) obj);
}
public override int GetHashCode() => m_MapClassName?.GetHashCode() ?? 0;
public static bool operator ==(MapProcessorClass left, MapProcessorClass right) => Equals(left, right);
public static bool operator !=(MapProcessorClass left, MapProcessorClass right) => !Equals(left, right);
public Type Class
{
get
{
// Have cached type?
if (m_Class != null)
return m_Class;
m_Class = GetClassWithName(m_MapClassName);
return m_Class;
}
}
public static Type GetClassWithName(string className)
{
#if UNITY_EDITOR
// Unsupported - but hopefully fine!
return Unsupported.GetTypeFromFullName(className);
#else
if (className == null)
return null;
if (s_MapClassLookup.Count == 0)
{
GenerateClassLookup();
}
// Try looking up - we should refresh if not
if (s_MapClassLookup.TryGetValue(className, out Type foundClass))
return foundClass;
return null;
#endif
}
public MapProcessorBase CreateInstance() => (MapProcessorBase)Activator.CreateInstance(Class);
public bool IsValid => Class != null;
public bool IsA<T>() => Class == typeof(T) || Class.IsSubclassOf(typeof(T));
private static void GenerateClassLookup()
{
List<Type> typesFound = new(1024);
foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
typesFound.AddRange(assembly.GetTypes());
s_MapClassLookup.Clear();
foreach (Type availableType in typesFound)
{
string fullName = availableType.FullName;
if (fullName == null)
continue;
s_MapClassLookup[fullName] = availableType;
}
}
}
}
@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: b1c6f641d81f4c8b81744a96934911ef
timeCreated: 1707729910
AssetOrigin:
serializedVersion: 1
productId: 277805
packageName: Tremble - Map Importer
packageVersion: 1.11.1
assetPath: Assets/Tremble/Types/MapProcessorClass.cs
uploadId: 939664
@@ -0,0 +1,41 @@
using System;
using System.Linq;
using System.Reflection;
using UnityEngine;
namespace TinyGoose.Tremble
{
public enum MaterialExportSize
{
[InspectorName("16")] Size16,
[InspectorName("32")] Size32,
[InspectorName("64")] Size64,
[InspectorName("128")] Size128,
[InspectorName("256")] Size256,
[InspectorName("512")] Size512,
[InspectorName("1024")] Size1024,
[InspectorName("2048")] Size2048,
}
public static class MaterialExportSizeUtils
{
public static MaterialExportSize GetDefault() => MaterialExportSize.Size64;
public static string[] Names =>
s_Names ??= Enum.GetValues(typeof(MaterialExportSize))
.Cast<MaterialExportSize>()
.Select(mes => typeof(MaterialExportSize)
.GetMember(mes.ToString())[0]
.GetCustomAttribute<InspectorNameAttribute>()?.displayName ?? mes.ToString())
.ToArray();
private static int[] IntSizes => s_Sizes ??= Names.Select(int.Parse).ToArray();
public static int ToIntSize(this MaterialExportSize mes) => IntSizes[(int)mes];
public static Vector2Int ToVector2IntSize(this MaterialExportSize mes) => new(mes.ToIntSize(), mes.ToIntSize());
private static string[] s_Names;
private static int[] s_Sizes;
}
}
@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: c9d6ab96c78649b2a9c2b1c4d6cda752
timeCreated: 1753866627
AssetOrigin:
serializedVersion: 1
productId: 277805
packageName: Tremble - Map Importer
packageVersion: 1.11.1
assetPath: Assets/Tremble/Types/MaterialExportSize.cs
uploadId: 939664
@@ -0,0 +1,81 @@
using System;
using UnityEngine;
namespace TinyGoose.Tremble
{
[Serializable]
public class MaterialRenderData
{
public MaterialRenderData(Material material)
{
m_Material = material;
}
[SerializeField] private Material m_Material;
[SerializeField] private MaterialImportMode m_ExportMode;
[SerializeField] private bool m_IsResolutionOverridden;
[SerializeField] private MaterialExportSize m_ResolutionX = MaterialExportSize.Size64;
[SerializeField] private MaterialExportSize m_ResolutionY = MaterialExportSize.Size64;
[SerializeField] private bool m_Dirty;
public Material Material => m_Material;
public MaterialImportMode ExportMode
{
get => m_ExportMode;
set
{
m_ExportMode = value;
m_Dirty = true;
}
}
public bool IsResolutionOverridden
{
get => m_IsResolutionOverridden;
set
{
m_IsResolutionOverridden = value;
m_Dirty = true;
}
}
public MaterialExportSize ResolutionX
{
get => m_ResolutionX;
set
{
m_ResolutionX = value;
m_Dirty = true;
}
}
public MaterialExportSize ResolutionY
{
get => m_ResolutionY;
set
{
m_ResolutionY = value;
m_Dirty = true;
}
}
public Vector2Int GetResolutionOrDefault()
{
if (!m_IsResolutionOverridden)
return TrembleSyncSettings.Get().MaterialExportSize.ToVector2IntSize();
return new(m_ResolutionX.ToIntSize(), m_ResolutionY.ToIntSize());
}
public bool IsDirty => m_Dirty;
public void ClearDirty() => m_Dirty = false;
}
public enum MaterialImportMode
{
Dynamic,
FixedSize,
}
}
@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 5008febfabec473983456dc18bc3cf5d
timeCreated: 1753866661
AssetOrigin:
serializedVersion: 1
productId: 277805
packageName: Tremble - Map Importer
packageVersion: 1.11.1
assetPath: Assets/Tremble/Types/MaterialRenderData.cs
uploadId: 939664
+136
View File
@@ -0,0 +1,136 @@
//
// 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.Threading.Tasks;
using UnityEngine;
namespace TinyGoose.Tremble
{
[Serializable]
public struct TpakTocEntry
{
public string Path;
public string SourcePath;
public int FileLength;
}
public class TpakArchive
{
private const string TPAK_MAGIC = "TPAK";
private const int TPAK_MAX_PATH = 255;
private readonly string m_ArchiveFile;
private readonly List<TpakTocEntry> m_Entries = new();
public TpakArchive(string archiveFile)
{
m_ArchiveFile = archiveFile;
}
public void AddDirectory(string directory)
{
AddDirectory_Recursive(directory, null);
}
public void AddFile(string filePath, string filenameInTpak)
{
m_Entries.Add(new()
{
Path = filenameInTpak,
SourcePath = filePath,
FileLength = (int)(new FileInfo(filePath).Length)
});
}
public async Task<bool> UnpackAsync(string folder)
{
StreamReadIO pakIn = new(m_ArchiveFile);
if (!pakIn.ReadString(4).EqualsInvariant(TPAK_MAGIC, caseSensitive: true))
{
Debug.LogError($"Invalid magic reading tpak '{m_ArchiveFile}'!");
return false;
}
int numEntries = pakIn.ReadInt32();
for (int entryIdx = 0; entryIdx < numEntries; entryIdx++)
{
int pathLength = pakIn.ReadInt32();
TpakTocEntry entry = new()
{
Path = pakIn.ReadString(pathLength),
FileLength = pakIn.ReadInt32(),
};
entry.SourcePath = Path.Combine(folder, entry.Path);
DirectoryUtil.CreateAllDirectories(entry.SourcePath);
m_Entries.Add(entry);
}
foreach (TpakTocEntry entry in m_Entries)
{
if (File.Exists(entry.SourcePath))
{
pakIn.SkipBytes(entry.FileLength);
continue;
}
byte[] file = pakIn.ReadBytes(entry.FileLength);
await File.WriteAllBytesAsync(entry.SourcePath, file);
}
return true;
}
public async Task PackAsync()
{
await using FileStream pakOutStream = new(m_ArchiveFile, FileMode.Create, FileAccess.Write);
using StreamWriteIO pakOut = new(pakOutStream);
pakOut.WriteString(TPAK_MAGIC, 4);
pakOut.WriteInt32(m_Entries.Count);
foreach (TpakTocEntry entry in m_Entries)
{
pakOut.WriteInt32(entry.Path.Length);
pakOut.WriteString(entry.Path);
pakOut.WriteInt32(entry.FileLength);
}
byte[] buffer = new byte[1024];
foreach (TpakTocEntry entry in m_Entries)
{
await using FileStream entryFile = new(entry.SourcePath, FileMode.Open, FileAccess.Read);
using BinaryReader entryIn = new(entryFile);
int readBytes;
do
{
readBytes = entryIn.Read(buffer, 0, buffer.Length);
pakOut.WriteBytes(buffer, readBytes);
} while (readBytes > 0);
}
}
private void AddDirectory_Recursive(string directory, string currentPath)
{
foreach (string file in Directory.EnumerateFiles(directory))
{
string path = currentPath == null ? Path.GetFileName(file) : Path.Combine(currentPath, Path.GetFileName(file));
AddFile(file, path);
}
foreach (string dir in Directory.EnumerateDirectories(directory))
{
string newCurrentPath = currentPath == null ? Path.GetFileName(dir) : Path.Combine(currentPath, Path.GetFileName(dir));
AddDirectory_Recursive(dir, newCurrentPath);
}
}
}
}
+10
View File
@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: fd52ece1f19743039da2022e11fbe16c
timeCreated: 1728651734
AssetOrigin:
serializedVersion: 1
productId: 277805
packageName: Tremble - Map Importer
packageVersion: 1.11.1
assetPath: Assets/Tremble/Types/TpakArchive.cs
uploadId: 939664
+149
View File
@@ -0,0 +1,149 @@
//
// 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 UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
#endif
namespace TinyGoose.Tremble
{
[Serializable]
public struct StoredTrembleTimer
{
public string Context;
public long LastMillis;
}
public struct TrembleTimerScope : IDisposable
{
private readonly TrembleTimer m_Timer;
private bool m_Disposed;
public TrembleTimerScope(Type type)
{
m_Timer = TrembleTimer.GetTimer(type);
m_Timer.BeginCapture();
m_Disposed = false;
}
public TrembleTimerScope(TrembleTimer.Context context)
{
m_Timer = TrembleTimer.GetTimer(context);
m_Timer.BeginCapture();
m_Disposed = false;
}
public void Dispose()
{
if (m_Disposed)
return;
m_Disposed = true;
m_Timer.EndCapture();
}
}
public class TrembleTimer
{
public enum Context
{
// Import
ExportMapToStaging,
RunQ3Map2,
ParseBsp,
CreateGameObjects,
SplitMesh,
SmoothMeshNormals,
SimplifyCollisionMeshes,
GenerateUV2,
RunMapProcessors,
// Sync
CreateTypeLookups,
ExportEntities,
GatherDataAssets,
ExportMaterials,
WriteEntitiesFgdAndGameConfig,
Cleanup,
ExportAddressableDataForStandalone,
}
private static readonly Dictionary<string, TrembleTimer> s_CurrentTimers = new();
private static string s_SessionContext;
private static DateTime s_SessionStart;
private DateTime m_CaptureStart;
private long m_TotalMilliseconds;
public long TotalMilliseconds => m_TotalMilliseconds;
public static void BeginSession(string sessionContext)
{
s_SessionStart = DateTime.Now;
s_SessionContext = sessionContext;
s_CurrentTimers.Clear();
}
public static void EndSession()
{
if (TrembleSyncSettings.Get().LogOnImportAndSync)
{
float seconds = (float)(DateTime.Now - s_SessionStart).TotalSeconds;
Debug.Log($"{s_SessionContext} complete in {seconds:F3}s!");
}
foreach ((string name, TrembleTimer timer) in s_CurrentTimers)
{
#if UNITY_EDITOR
EditorPrefs.SetInt($"TrembleTimer_{name}", (int)timer.TotalMilliseconds);
#endif
if (TrembleSyncSettings.Get().LogOnImportAndSync)
{
Debug.Log($" {name} took {timer.TotalMilliseconds}ms");
}
}
}
public static TrembleTimer GetTimer(Type processorType) => GetTimer(processorType.FullName);
public static TrembleTimer GetTimer(Context context) => GetTimer(context.ToString());
private static TrembleTimer GetTimer(string name)
{
if (!s_CurrentTimers.TryGetValue(name, out TrembleTimer timer))
{
timer = new();
s_CurrentTimers[name] = timer;
}
return timer;
}
public static long GetLastTime(Type processorType) => GetLastTime(processorType.FullName);
public static long GetLastTime(Context context) => GetLastTime(context.ToString());
private static long GetLastTime(string name)
#if UNITY_EDITOR
=> EditorPrefs.GetInt($"TrembleTimer_{name}", -1);
#else
=> 0L;
#endif
public void BeginCapture()
{
m_CaptureStart = DateTime.Now;
}
public void EndCapture()
{
m_TotalMilliseconds += (long)(DateTime.Now - m_CaptureStart).TotalMilliseconds;
}
}
}
+10
View File
@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: fcf63be3ebf546e598559701514185f6
timeCreated: 1728729960
AssetOrigin:
serializedVersion: 1
productId: 277805
packageName: Tremble - Map Importer
packageVersion: 1.11.1
assetPath: Assets/Tremble/Types/TrembleTimer.cs
uploadId: 939664
+77
View File
@@ -0,0 +1,77 @@
//
// This file is part of the Tremble package by Tiny Goose.
// Copyright (c) 2024-2026 TinyGoose Ltd., All Rights Reserved.
//
using System.Collections.Generic;
using System.IO;
namespace TinyGoose.Tremble
{
public class TwoWayDictionary<TKey, TValue>
{
private readonly Dictionary<TKey, TValue> m_Lookup;
private readonly Dictionary<TValue, TKey> m_ReverseLookup;
public TwoWayDictionary()
{
m_Lookup = new();
m_ReverseLookup = new();
}
public TwoWayDictionary(int capacity)
{
m_Lookup = new(capacity);
m_ReverseLookup = new(capacity);
}
public TValue GetValue(TKey key)
{
return m_Lookup[key];
}
public TKey GetKey(TValue value)
{
return m_ReverseLookup[value];
}
public bool ContainsKey(TKey key) => m_Lookup.ContainsKey(key);
public bool ContainsValue(TValue value) => m_ReverseLookup.ContainsKey(value);
public bool TryGetValue(TKey key, out TValue value) => m_Lookup.TryGetValue(key, out value);
public bool TryGetKey(TValue value, out TKey key) => m_ReverseLookup.TryGetValue(value, out key);
public IEnumerable<TKey> Keys => m_Lookup.Keys;
public IEnumerable<TValue> Values => m_Lookup.Values;
public void Clear()
{
m_Lookup.Clear();
m_ReverseLookup.Clear();
}
public TValue this[TKey key]
{
get => GetValue(key);
set
{
m_Lookup[key] = value;
m_ReverseLookup[value] = key;
}
}
public void Add(TKey key, TValue value)
{
m_Lookup.Add(key, value);
m_ReverseLookup.Add(value, key);
}
public void Remove(TKey key)
{
if (m_Lookup.TryGetValue(key, out TValue value))
{
m_ReverseLookup.Remove(value);
}
m_Lookup.Remove(key);
}
}
}
@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: ab6c6d9d07924bbebafee07655e56466
timeCreated: 1740748009
AssetOrigin:
serializedVersion: 1
productId: 277805
packageName: Tremble - Map Importer
packageVersion: 1.11.1
assetPath: Assets/Tremble/Types/TwoWayDictionary.cs
uploadId: 939664
+36
View File
@@ -0,0 +1,36 @@
//
// This file is part of the Tremble package by Tiny Goose.
// Copyright (c) 2024-2026 TinyGoose Ltd., All Rights Reserved.
//
using System;
using UnityEngine;
namespace TinyGoose.Tremble
{
[Serializable]
public class WorldspawnScript
{
private static readonly int DEFAULT_TYPE_HASHCODE = typeof(Worldspawn).FullName?.GetHashCode() ?? 0;
private static readonly int EMPTY_STRING_HASHCODE = "".GetHashCode();
public WorldspawnScript(string scriptName = null)
{
m_ScriptName = scriptName;
}
// -----------------------------------------------------------------------------------------------------------------------------
// Serialised
// -----------------------------------------------------------------------------------------------------------------------------
[SerializeField] private string m_ScriptName;
public string ScriptName => m_ScriptName;
public Type Class => TypeDatabase.GetType(m_ScriptName);
public override int GetHashCode()
{
int hash = m_ScriptName.GetHashCode();
return hash == DEFAULT_TYPE_HASHCODE ? EMPTY_STRING_HASHCODE : hash;
}
}
}
@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 4b44834781104ac6941bd9a435d8dae3
timeCreated: 1710152777
AssetOrigin:
serializedVersion: 1
productId: 277805
packageName: Tremble - Map Importer
packageVersion: 1.11.1
assetPath: Assets/Tremble/Types/WorldspawnScript.cs
uploadId: 939664